เรากำลังทำอะไรผิดพลาดหรือเป็นข้อผิดพลาดของ SQL Server หรือไม่
มันเป็นข้อผิดพลาดของผลที่ผิดพลาดซึ่งคุณควรรายงานผ่านช่องทางสนับสนุนปกติของคุณ หากคุณไม่มีข้อตกลงการสนับสนุนอาจช่วยให้ทราบว่าเหตุการณ์ที่ชำระเงินจะได้รับการคืนเงินตามปกติหาก Microsoft ยืนยันพฤติกรรมเป็นข้อบกพร่อง
ข้อผิดพลาดต้องมีสามส่วนผสม:
- ลูปซ้อนพร้อมการอ้างอิงภายนอก (ใช้)
- สปูลดัชนีขี้เกียจด้านในที่ค้นหาข้อมูลอ้างอิงด้านนอก
- ตัวดำเนินการต่อข้อมูลภายใน
ตัวอย่างเช่นแบบสอบถามในคำถามสร้างแผนดังต่อไปนี้:
มีหลายวิธีในการลบหนึ่งในองค์ประกอบเหล่านี้ดังนั้นข้อผิดพลาดจะไม่ทำซ้ำ
ตัวอย่างเช่นเราสามารถสร้างดัชนีหรือสถิติที่เกิดขึ้นกับเครื่องมือเพิ่มประสิทธิภาพเลือกที่จะไม่ใช้ Lazy Index Spool หรืออาจใช้คำแนะนำเพื่อบังคับให้แฮชหรือผสานสหภาพแทนที่จะใช้การต่อข้อมูล หนึ่งสามารถเขียนแบบสอบถามเพื่อแสดงความหมายที่เหมือนกัน แต่ซึ่งส่งผลในรูปแบบแผนที่แตกต่างกันหนึ่งองค์ประกอบที่จำเป็นขาดหายไป
รายละเอียดเพิ่มเติม
Spool ดัชนีขี้เกียจเก็บแถวผลด้านในอย่างเกียจคร้านในตารางงานที่ทำดัชนีโดยการอ้างอิงภายนอก (พารามิเตอร์ที่สัมพันธ์กัน) ค่า หาก Lazy Index Spool ถูกขอให้อ้างอิงภายนอกที่เคยเห็นมาก่อนจะดึงแถวผลลัพธ์ที่แคชไว้จากตารางงาน ("ย้อนกลับ") หากสปูลถูกถามถึงค่าการอ้างอิงภายนอกที่ไม่เคยเห็นมาก่อนมันจะรันทรีย่อยด้วยค่าการอ้างอิงภายนอกปัจจุบันและแคชผลลัพธ์ ("rebind") เพรดิเคตค้นหาบน Lazy Index Spool ระบุคีย์สำหรับตารางงาน
ปัญหาเกิดขึ้นในรูปร่างแผนเฉพาะนี้เมื่อสปูลตรวจสอบเพื่อดูว่าการอ้างอิงภายนอกใหม่เป็นแบบเดียวกับที่เคยเห็นมาก่อนหรือไม่ Nested Loops Join อัพเดตการอ้างอิงภายนอกอย่างถูกต้องและแจ้งผู้ประกอบการเกี่ยวกับอินพุตด้านในผ่านPrepRecompute
วิธีการอินเทอร์เฟซ ในช่วงเริ่มต้นของการตรวจสอบนี้ผู้ดำเนินการด้านในจะอ่านCParamBounds:FNeedToReload
คุณสมบัติเพื่อดูว่าการอ้างอิงภายนอกเปลี่ยนแปลงไปจากครั้งที่แล้วหรือไม่ ตัวอย่างการติดตามสแต็กแสดงไว้ด้านล่าง:
เมื่อทรีย่อยที่แสดงด้านบนมีอยู่โดยเฉพาะเมื่อมีการใช้การเรียงต่อกันเกิดข้อผิดพลาด (อาจเป็นปัญหา ByVal / ByRef / Copy) ที่มีการเชื่อมโยงซึ่งCParamBounds:FNeedToReload
จะส่งกลับค่าเท็จเสมอโดยไม่คำนึงว่าการอ้างอิงภายนอกเปลี่ยนแปลงจริงหรือไม่
เมื่อมีทรีย่อยเดียวกัน แต่ใช้ Merge Union หรือ Hash Union คุณสมบัติที่สำคัญนี้จะถูกตั้งค่าอย่างถูกต้องในการวนซ้ำแต่ละครั้งและ Lazy Index Spool ย้อนกลับหรือ rebinds แต่ละครั้งตามความเหมาะสม Distinct Sort and Stream Aggregate นั้นไร้ที่ติเลยทีเดียว ความสงสัยของฉันคือ Merge and Hash Union ทำสำเนาของค่าก่อนหน้าในขณะที่การต่อข้อมูลใช้การอ้างอิง มันเป็นไปไม่ได้เลยที่จะตรวจสอบเรื่องนี้โดยที่ไม่สามารถเข้าถึงซอร์สโค้ดเซิร์ฟเวอร์ SQL ได้
ผลลัพธ์สุทธิคือ Lazy Index Spool ในรูปแบบแผนปัญหามักคิดว่าได้เห็นการอ้างอิงภายนอกปัจจุบันแล้วย้อนกลับโดยค้นหาในตารางการทำงานโดยทั่วไปไม่พบสิ่งใดดังนั้นจึงไม่มีการส่งคืนแถวสำหรับการอ้างอิงภายนอกนั้น ผู้ใช้สปูลจะดำเนินการตามRewindHelper
วิธีการของมันในการดีบักผ่านการดำเนินการและไม่เคยReloadHelper
ใช้วิธีการ (reload = rebind ในบริบทนี้) สิ่งนี้ชัดเจนในแผนการดำเนินการเนื่องจากตัวดำเนินการภายใต้สปูลทั้งหมดมี 'Number of Executions = 1'
แน่นอนว่าเป็นข้อยกเว้นสำหรับการอ้างอิงภายนอกครั้งแรกที่ได้รับ Spool ดัชนี Spool สิ่งนี้จะเรียกใช้แผนผังย่อยและแคชแถวผลลัพธ์ในตารางงานเสมอ การวนซ้ำตามมาทั้งหมดส่งผลให้กรอกลับซึ่งจะสร้างแถว (แถวแคชเดียว) เมื่อการวนซ้ำปัจจุบันมีค่าเดียวกันสำหรับการอ้างอิงภายนอกเป็นครั้งแรก
ดังนั้นสำหรับอินพุตที่กำหนดใด ๆ ที่อยู่ด้านนอกของการเข้าร่วมลูปซ้อนการค้นหาจะส่งคืนแถวจำนวนมากเนื่องจากมีการซ้ำซ้อนของแถวแรกที่ประมวลผล (บวกอีกหนึ่งแถวสำหรับแถวแรกของหลักสูตร)
การสาธิต
ตารางและข้อมูลตัวอย่าง:
CREATE TABLE #T1
(
pk integer IDENTITY NOT NULL,
c1 integer NOT NULL,
CONSTRAINT PK_T1
PRIMARY KEY CLUSTERED (pk)
);
GO
INSERT #T1 (c1)
VALUES
(1), (2), (3), (4), (5), (6),
(1), (2), (3), (4), (5), (6),
(1), (2), (3), (4), (5), (6);
แบบสอบถาม (เล็กน้อย) ต่อไปนี้สร้างการนับที่ถูกต้องสองสำหรับแต่ละแถว (รวม 18 รายการ) โดยใช้ Merge Union
SELECT T1.c1, C.c1
FROM #T1 AS T1
CROSS APPLY
(
SELECT COUNT_BIG(*) AS c1
FROM
(
SELECT T1.c1
UNION
SELECT NULL
) AS U
) AS C;
หากเราเพิ่มคำใบ้เพื่อบังคับให้มีการต่อข้อมูล:
SELECT T1.c1, C.c1
FROM #T1 AS T1
CROSS APPLY
(
SELECT COUNT_BIG(*) AS c1
FROM
(
SELECT T1.c1
UNION
SELECT NULL
) AS U
) AS C
OPTION (CONCAT UNION);
แผนการดำเนินการมีรูปร่างที่เป็นปัญหา:
และผลลัพธ์ไม่ถูกต้องเพียงสามแถว:
แม้ว่าจะไม่รับประกันพฤติกรรมนี้ แต่แถวแรกจากการสแกนดัชนีแบบคลัสเตอร์มีc1
ค่าเท่ากับ 1 มีอีกสองแถวที่มีค่านี้ดังนั้นจึงมีการสร้างแถวทั้งหมดสามแถว
ตอนนี้ตัดทอนตารางข้อมูลและโหลดด้วยแถวที่ซ้ำซ้อนของแถว 'แรก':
TRUNCATE TABLE #T1;
INSERT #T1 (c1)
VALUES
(1), (2), (3), (4), (5), (6),
(1), (2), (3), (4), (5), (6),
(1), (1), (1), (1), (1), (1);
ตอนนี้แผนการเชื่อมต่อคือ:
และตามที่ระบุไว้มีการสร้างแถว 8 แถวซึ่งทั้งหมดนี้มีc1 = 1
แน่นอน:
ฉันสังเกตเห็นว่าคุณได้เปิดรายการเชื่อมต่อสำหรับข้อบกพร่องนี้แต่จริงๆแล้วไม่ใช่ที่สำหรับรายงานปัญหาที่ส่งผลกระทบต่อการผลิต หากเป็นกรณีนี้คุณควรติดต่อฝ่ายสนับสนุนของ Microsoft
ข้อผิดพลาดของผลลัพธ์ที่ผิดพลาดนี้ได้รับการแก้ไขในบางช่วง มันไม่ทำซ้ำสำหรับฉันใน SQL Server รุ่นใด ๆ ตั้งแต่ปี 2555 เป็นต้นไป มันทำซ้ำในการสร้าง SQL Server 2008 R2 SP3-GDR 10.50.6560.0 (X64)