ข้อผิดพลาดใน database_scoped_configurations


9

ฉันพยายามแทรกชุดผลลัพธ์จาก:

SELECT * FROM sys.database_scoped_configurations

ลงในตารางชั่วคราวเนื่องจากฉันต้องการตรวจสอบการตั้งค่าสำหรับฐานข้อมูลทั้งหมดบนเซิร์ฟเวอร์ของฉัน ดังนั้นฉันจึงเขียนรหัสนี้:

DROP TABLE IF EXISTS #h
CREATE TABLE #h(dbname sysname, configuration_id INT, name sysname,     value SQL_VARIANT,  value_for_secondary SQL_VARIANT)
EXEC sys.sp_MSforeachdb 'USE ?; insert into #h(dbname, configuration_id, name, value,value_for_secondary)  SELECT ''?'' as dbname, * FROM sys.database_scoped_configurations  D'
SELECT * FROM #h H

แต่จะมีเพียงหนึ่งแถวต่อฐานข้อมูลไม่ใช่สี่แถวที่ฉันคาดหวังจากการเรียกใช้การเลือกแบบธรรมดาในแต่ละฐานข้อมูล

ฉันรู้ว่ามีวิธีการเขียนโค้ดที่ดีกว่าการใช้ sp_MSForEachDB และฉันลองหลายวิธี แต่ฉันยังได้รับเพียงหนึ่งแถวต่อฐานข้อมูล ฉันได้ลองแล้วทั้ง SQL Server 2016 RTM และ SP1

นี่เป็นข้อบกพร่องของ SQL Server 2016 หรือฉันกำลังทำอะไรผิดหรือเปล่า?


ข้อผิดพลาดได้รับการแก้ไขอย่างน้อยใน Microsoft SQL Server 2017 (RTM-CU15-GDR)
Henrik Staun Poulsen

คำตอบ:


8

นี่เป็นข้อบกพร่องของ SQL Server 2016 หรือไม่

ใช่. แน่นอนว่านี่ไม่ใช่พฤติกรรมที่ถูกต้อง ผมได้มีการรายงานได้ที่นี่และมีการแก้ไขใน SQL Server 2016 SP2 CU9

ดังที่Mikael Erikssonกล่าวไว้ในความคิดเห็นsys.database_scoped_configurationsและsys.dm_exec_sessionsนำไปใช้เป็นมุมมองในรูปแบบ

SELECT ...  
FROM OpenRowset(TABLE xxxx)  

อย่างไรก็ตามการเปรียบเทียบทั้งสองแผนด้านล่างมีความแตกต่างที่ชัดเจน

DBCC TRACEON(3604);

DECLARE @database_scoped_configurations TABLE(x INT);

INSERT INTO @database_scoped_configurations
SELECT configuration_id
FROM   sys.database_scoped_configurations
OPTION (QUERYTRACEON 8608, QUERYTRACEON 8615, QUERYTRACEON 8619, QUERYTRACEON 8620 );


DECLARE @dm_exec_sessions TABLE(x INT);

INSERT INTO @dm_exec_sessions
SELECT session_id
FROM   sys.dm_exec_sessions
OPTION (QUERYTRACEON 8608, QUERYTRACEON 8615, QUERYTRACEON 8619, QUERYTRACEON 8620 );

ป้อนคำอธิบายรูปภาพที่นี่

ผลลัพธ์การติดตามสถานะ 8619 สำหรับแบบสอบถามทั้งสองนี้แสดง

ใช้กฎ: EnforceHPandAccCard - x0-> Spool หรือ Top (x0)

เห็นได้ชัดว่า SQL Server ไม่สามารถยืนยันได้ว่าแหล่งที่มาสำหรับ TVF นั้นไม่ใช่เป้าหมายการแทรกดังนั้นจึงต้องมีการป้องกันฮาโลวีน

ในกรณีเซสชันนี้ถูกนำมาใช้เป็นสปูลที่รวบรวมแถวทั้งหมดก่อน ในการdatabase_scoped_configurationsโดยการเพิ่มTOP 1แผน การใช้TOPสำหรับการป้องกันฮาโลวีนที่กำลังจะกล่าวถึงในบทความนี้ บทความนี้ยังกล่าวถึงค่าสถานะการสืบค้นกลับที่ไม่มีเอกสารเพื่อบังคับให้สปูลแทนที่จะTOPทำงานตามที่คาดไว้

DECLARE @database_scoped_configurations TABLE(x INT);

INSERT INTO @database_scoped_configurations
SELECT configuration_id
FROM   sys.database_scoped_configurations
OPTION (QUERYTRACEON 8692)

ปัญหาที่ชัดเจนเกี่ยวกับการใช้TOP 1แทนที่จะเก็บพักคือว่ามันจะ จำกัด จำนวนแถวที่ใส่เข้าไปโดยพลการ ดังนั้นนี่จะถูกต้องถ้าจำนวนแถวที่ส่งคืนโดยฟังก์ชันคือ <= 1

บันทึกเริ่มต้นมีลักษณะเช่นนี้

ป้อนคำอธิบายรูปภาพที่นี่

เปรียบเทียบสิ่งนี้กับบันทึกเริ่มต้นสำหรับแบบสอบถาม 2

ป้อนคำอธิบายรูปภาพที่นี่

หากฉันเข้าใจอย่างถูกต้องข้างต้นก็คิดว่า TVF แรกสามารถส่งคืนได้สูงสุดหนึ่งแถวและใช้การเพิ่มประสิทธิภาพที่ไม่ถูกต้อง Max สำหรับแบบสอบถามที่สองถูกตั้งค่าเป็น1.34078E+154( 2^512)

ฉันไม่ทราบว่ามาจาก rowcount สูงสุดนี้มาจากไหน บางทีข้อมูลเมตาที่จัดทำโดยผู้เขียน DMV? มันก็แปลกที่TOP(50)วิธีแก้ปัญหาไม่ได้รับการเขียนใหม่TOP(1)เพราะTOP(50)จะไม่ป้องกันปัญหาฮาโลวีนเกิดขึ้น (แม้ว่าจะหยุดมันต่อไปเรื่อย ๆ )


6

sp_MSForEachDBกรุณาหยุดใช้ มันไม่ได้รับการสนับสนุนไม่มีเอกสารและเป็นบักกี้ - ซึ่งอาจเป็นปัญหาที่นี่ การแทนที่ของฉันแสดงให้เห็นถึงปัญหาเดียวกันที่นี่ แต่โดยทั่วไปมันเป็นสิ่งที่ปลอดภัยกว่าที่จะใช้

สำหรับสิ่งนี้ฉันต้องการสร้าง SQL แบบไดนามิกมากกว่าส่งคำสั่งเดียวไปยังโพรซีเดอร์เพื่อดำเนินการหลายครั้ง (แม้แต่ขั้นตอนของฉันซึ่งฉันไว้ใจมากขึ้น) ด้วยวิธีนี้ฉันสามารถพิมพ์คำสั่งแทนที่จะเรียกใช้งานพวกเขาและ ตรวจสอบให้แน่ใจว่าพวกเขาทั้งหมดกำลังทำสิ่งที่พวกเขาพูด

การยืมจากการสังเกตว่ารหัสที่เป็นพื้นฐานของมุมมองของระบบนั้นนำไปใช้ a TOP (1)เราสามารถลองด้วยวิธีนี้:

DROP TABLE IF EXISTS #h;

CREATE TABLE #h(dbname sysname, configuration_id INT, name sysname, 
  value SQL_VARIANT,  value_for_secondary SQL_VARIANT);

DECLARE @sql nvarchar(max) = N'', @base nvarchar(max) = N'insert into #h
  (dbname, configuration_id, name, value,value_for_secondary)  SELECT TOP ($c$) 
  $db$ as dbname, * FROM $qdb$.sys.database_scoped_configurations;';

SELECT @sql += REPLACE(REPLACE(REPLACE(@base, N'$qdb$', QUOTENAME(name)), 
  N'$db$', CHAR(39) + name + CHAR(39)), N'$c$', RTRIM(COUNT(*) OVER()))
FROM sys.databases WHERE state = 0;

PRINT @sql;
EXEC sys.sp_executesql @sql;
SELECT * FROM #h;

โปรดสังเกตว่าฉันไม่ได้ใช้USEที่นี่ แต่นำหน้าsysมุมมองแคตตาล็อกด้วยชื่อฐานข้อมูล

ทำไมมุมมองจึงใช้งานในวิธีมหัศจรรย์ฉันไม่รู้ ฉันไม่ทราบว่าคุณจะได้รับคำตอบที่ดีเนื่องจากอาจต้องการความคิดเห็นจาก Microsoft (หรือใครก็ตามที่สามารถเข้าถึงซอร์สโค้ดหรือเต็มใจที่จะเปิดตัวดีบัก)


นั่นเป็นวิธีแรกในหลายวิธีที่ฉันลองใช้ แต่ฉันไม่คิดว่าฉันจะใช้ sproc ในตัวอย่างได้
Henrik Staun Poulsen

6

ขอขอบคุณที่รายงานปัญหานี้!

นี่เป็นข้อบกพร่องในวิธีที่ Query Optimizer สร้างแผนสำหรับsys.database_scoped_configurationsมุมมองแคตตาล็อก เราจะจัดการเรื่องนี้ในหนึ่งในการอัพเดทครั้งต่อไปของ SQL Server 2016 และใน Azure SQL Database

คุณสามารถเพิ่มส่วนTOPคำสั่งในSELECTส่วนแทรกของคุณเพื่อรับแผนที่ถูกต้องเช่น:

DECLARE @database_scoped_configurations TABLE(x INT); 
INSERT INTO @database_scoped_configurations 
SELECT **TOP 100** configuration_id 
FROM sys.database_scoped_configurations 

3

ฉันยอมรับว่านี่เป็นสิ่งที่แปลกมากและมีข้อผิดพลาดที่อาจเกิดขึ้น แต่การเพิ่ม TOP (50) ลงในตัวอย่างที่คุณเลือกจะส่งคืนแถวทั้งหมดดังนั้นอย่างน้อยคุณก็จะไป ดูเหมือนว่าผลลัพธ์จะมาจากฟังก์ชันค่าตารางของระบบ ([DB_SCOPED_CONFIG]) ดังนั้นฉันจึงไม่สามารถบอกได้ว่าเกิดอะไรขึ้น

ฉันจะจับตาดูหัวข้อนี้เพื่อดูว่าคน 'ฉลาด' รู้ว่าทำไมสิ่งนี้เกิดขึ้น


คุณได้รับแถว MAXDOP เท่านั้นสำหรับแต่ละฐานข้อมูลหรือไม่
Dan Guzman

@DanGuzman - ใช่การเลือกด้วยตัวเองใช้งานได้ดี (ไม่มีสิ่งที่ foreach ทั้งหมดบนฐานข้อมูลเดียว) เมื่อคุณเพิ่มส่วนแทรกลงในที่ก่อให้เกิดพฤติกรรมแปลก ๆ
Scott Hodgin
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.