ฉันมีเวลายากที่จะเข้าใจว่าทำไม SQL Server ถึงได้มีการประมาณการที่สามารถพิสูจน์ได้อย่างง่ายดายว่าไม่สอดคล้องกับสถิติ
ความมั่นคง
ไม่มีการรับประกันความสอดคล้องโดยทั่วไป การประมาณการอาจถูกคำนวณในทรีย่อย (แต่เทียบเท่าเชิงตรรกะ) ในเวลาที่ต่างกันโดยใช้วิธีการทางสถิติที่แตกต่างกัน
ไม่มีอะไรผิดปกติกับตรรกะที่บอกว่าการเข้าร่วมทรีย่อยที่เหมือนกันทั้งสองนั้นควรที่จะสร้างผลิตภัณฑ์ครอส แต่ก็ไม่มีอะไรจะพูดได้เหมือนกันว่า
การประมาณค่าเริ่มต้น
ในกรณีเฉพาะของคุณการประมาณค่าเชิงการนับเบื้องต้นสำหรับการเข้าร่วมนั้นไม่ได้ดำเนินการกับ subtrees ที่เหมือนกันสองค่า รูปร่างของต้นไม้ในเวลานั้นคือ:
LogOp_Join
LogOp_GbAgg
LogOp_LeftOuterJoin
LogOp_ รับ TBL: ar
LogOp_Select
LogOp_Get TBL: tcr
ScaOp_Comp x_cmpEq
ScaOp_Identifier [tcr] .rId
ScaOp_Const Value = 508
ScaOp_Logical x_lopAnd
ScaOp_Comp x_cmpEq
ScaOp_Identifier [ar] .fId
ScaOp_Identifier [tcr] .fId
ScaOp_Comp x_cmpEq
ScaOp_Identifier [ar] .bId
ScaOp_Identifier [tcr] .bId
AncOp_PrjList
AncOp_PrjEl Expr1003
ScaOp_AggFunc stopMax
ScaOp_Convert int
ScaOp_Identifier [tcr] .isS
LogOp_Select
LogOp_GbAgg
LogOp_LeftOuterJoin
LogOp_ รับ TBL: ar
LogOp_Select
LogOp_Get TBL: tcr
ScaOp_Comp x_cmpEq
ScaOp_Identifier [tcr] .rId
ScaOp_Const Value = 508
ScaOp_Logical x_lopAnd
ScaOp_Comp x_cmpEq
ScaOp_Identifier [ar] .fId
ScaOp_Identifier [tcr] .fId
ScaOp_Comp x_cmpEq
ScaOp_Identifier [ar] .bId
ScaOp_Identifier [tcr] .bId
AncOp_PrjList
AncOp_PrjEl Expr1006
ScaOp_AggFunc stopMin
ScaOp_Convert int
ScaOp_Identifier [ar] .isT
AncOp_PrjEl Expr1007
ScaOp_AggFunc stopMax
ScaOp_Convert int
ScaOp_Identifier [tcr] .isS
ScaOp_Comp x_cmpEq
ScaOp_Identifier Expr1006
ScaOp_Const Value = 1
ScaOp_Comp x_cmpEq
ScaOp_Identifier QCOL: [ar] .fId
ScaOp_Identifier QCOL: [ar] .fId
ครั้งแรกที่เข้าร่วมการป้อนข้อมูลที่ได้มีการรวม unprojected ง่ายไปและที่สองเข้าร่วมการป้อนข้อมูลมีกริยาt.isT = 1
ผลักดันด้านล่างที่เป็นt.isT
MIN(CONVERT(INT, ar.isT))
อย่างไรก็ตามเรื่องนี้การคำนวณการเลือกเกิดของภาคisT
แสดงสามารถใช้CSelCalcColumnInInterval
กับฮิสโตแกรมได้:
CSelCalcColumnInInterval
คอลัมน์: COL: Expr1006
ฮิสโตแกรมที่โหลดสำหรับคอลัมน์ QCOL: [ar] .isT จากสถิติที่มี id 3
หัวกะทิ: 4.85248e-005
สร้างสถิติการสะสมแล้ว:
CStCollFilter (ID = 11, CARD = 1)
CStCollGroupBy (ID = 10, CARD = 20608)
CStCollOuterJoin (ID = 9, CARD = 20608 x_jtLeftOuter)
CStCollBaseTable (ID = 3, CARD = 20608 TBL: ar)
CStCollFilter (ID = 8, CARD = 1)
CStCollBaseTable (ID = 4, CARD = 28 TBL: tcr)
ความคาดหวัง (ถูกต้อง) สำหรับ 20,608 แถวที่จะลดลงเป็น 1 แถวโดยคำกริยานี้
เข้าร่วมการประเมิน
ตอนนี้คำถามกลายเป็นวิธีที่แถว 20,608 จากอินพุตการเข้าร่วมอื่นจะจับคู่กับแถวนี้:
LogOp_Join
CStCollGroupBy (ID = 7, CARD = 20608)
CStCollOuterJoin (ID = 6, CARD = 20608 x_jtLeftOuter)
...
CStCollFilter (ID = 11, CARD = 1)
CStCollGroupBy (ID = 10, CARD = 20608)
...
ScaOp_Comp x_cmpEq
ScaOp_Identifier QCOL: [ar] .fId
ScaOp_Identifier QCOL: [ar] .fId
มีหลายวิธีในการประเมินการเข้าร่วมโดยทั่วไป ตัวอย่างเช่นเราทำได้:
- รับฮิสโตแกรมใหม่ที่ผู้ดำเนินการแผนในแต่ละแผนผังย่อยจัดเรียงไว้ที่การเข้าร่วม (การแทรกค่าขั้นตอนตามความจำเป็น) และดูวิธีการจับคู่ หรือ
- ทำการจัดตำแหน่งฮิสโทแกรมแบบ 'หยาบ' ให้ง่ายขึ้น (ใช้ค่าต่ำสุดและสูงสุดไม่ใช่ทีละขั้นตอน); หรือ
- คำนวณการเลือกแยกต่างหากสำหรับคอลัมน์เข้าร่วมเพียงอย่างเดียว (จากตารางฐานและไม่มีการกรองใด ๆ ) จากนั้นเพิ่มเอฟเฟกต์การเลือกของกริยาที่ไม่เข้าร่วม
- ...
ขึ้นอยู่กับตัวประมาณการใช้งานของ cardinality และฮิวริสติกบางอย่างสามารถใช้ (หรือรูปแบบใดก็ได้) ก็ได้ ดูเอกสารทางเทคนิคของ Microsoft การปรับแผนแบบสอบถามของคุณให้เหมาะสมด้วย SQL Server 2014 Cardinality Estimatorสำหรับข้อมูลเพิ่มเติม
ข้อผิดพลาด?
ตอนนี้ดังที่ระบุไว้ในคำถามในกรณีนี้การรวมคอลัมน์แบบง่าย 'แบบง่าย' (บนfId
) ใช้CSelCalcExpressionComparedToExpression
เครื่องคิดเลข:
วางแผนการคำนวณ:
CSelCalcExpression เทียบกับ Expression [ar] .fId x_cmpEq [ar] .fId
ฮิสโตแกรมที่โหลดสำหรับคอลัมน์ QCOL: [ar] .bId จากสถิติที่มี id 2
ฮิสโตแกรมที่โหลดสำหรับคอลัมน์ QCOL: [ar] .fId จากสถิติที่มี id 1
หัวกะทิ: 0
การคำนวณนี้ประเมินว่าการเข้าร่วม 20,608 แถวกับ 1 แถวที่กรองแล้วจะมีค่าการเลือกเป็นศูนย์: ไม่มีแถวใดที่ตรงกัน (รายงานว่าเป็นหนึ่งแถวในแผนสุดท้าย) มันผิดหรือเปล่า? ใช่อาจมีข้อผิดพลาดใน CE ใหม่ที่นี่ หนึ่งอาจโต้แย้งว่า 1 แถวจะตรงกับทุกแถวหรือไม่มีดังนั้นผลลัพธ์อาจมีเหตุผล แต่มีเหตุผลที่จะเชื่อเป็นอย่างอื่น
รายละเอียดค่อนข้างยุ่งยาก แต่ความคาดหวังของการประมาณค่าจะขึ้นอยู่กับfId
ฮิสโตแกรมที่ไม่มีการกรองแก้ไขโดยการเลือกของตัวกรองทำให้20608 * 20608 * 4.85248e-005 = 20608
แถวมีความสมเหตุสมผล
ต่อไปนี้การคำนวณนี้จะหมายถึงการใช้เครื่องคิดเลขแทนCSelCalcSimpleJoinWithDistinctCounts
CSelCalcExpressionComparedToExpression
ไม่มีวิธีที่ทำเป็นเอกสารเพื่อทำสิ่งนี้ แต่ถ้าคุณอยากรู้คุณสามารถเปิดใช้งานการตั้งค่าสถานะการสืบค้นกลับที่ไม่มีเอกสาร 9479:
หมายเหตุการเข้าร่วมครั้งสุดท้ายก่อให้เกิด 20,608 แถวจากสองอินพุตแถวเดียว แต่ก็ไม่น่าแปลกใจ มันเป็นแผนเดียวกันที่ผลิตโดย CE ดั้งเดิมภายใต้ TF 9481
ฉันพูดถึงรายละเอียดที่ยุ่งยาก (และใช้เวลานานในการตรวจสอบ) แต่เท่าที่ฉันสามารถบอกได้สาเหตุของปัญหาเกี่ยวข้องกับภาคแสดงrId = 508
โดยมีการเลือกแบบศูนย์ การประมาณค่าศูนย์นี้ถูกยกให้เป็นหนึ่งแถวในวิธีปกติซึ่งดูเหมือนจะมีส่วนร่วมในการประเมินการเลือกแบบศูนย์ที่คำถามที่เข้าร่วมเมื่อบัญชีสำหรับภาคแสดงต่ำลงในแผนภูมิอินพุต (ดังนั้นการโหลดสถิติสำหรับbId
)
การอนุญาตให้การรวมภายนอกใช้การประมาณภายในแถวศูนย์ (แทนที่จะเพิ่มเป็นหนึ่งแถว) (เพื่อให้แถวภายนอกทั้งหมดมีคุณสมบัติ) ให้การประเมินการเข้าร่วม 'ไร้ข้อบกพร่อง' ด้วยเครื่องคิดเลขอย่างใดอย่างหนึ่ง หากคุณสนใจที่จะสำรวจสิ่งนี้ธงการติดตามที่ไม่มีเอกสารคือ 9473 (คนเดียว):
พฤติกรรมของการประมาณเชิงการเข้าร่วมด้วยCSelCalcExpressionComparedToExpression
สามารถแก้ไขได้โดยไม่คำนึงถึง `` ตัวเลข 'ด้วยรูปแบบการเปลี่ยนแปลงอื่นที่ไม่มีเอกสาร (9494) ฉันพูดถึงสิ่งเหล่านี้เพราะฉันรู้ว่าคุณมีความสนใจในสิ่งต่าง ๆ ; ไม่ใช่เพราะพวกเขาเสนอทางออก จนกว่าคุณจะรายงานปัญหาไปยัง Microsoft และพวกเขาจัดการปัญหา (หรือไม่) การแสดงข้อความค้นหาต่างกันน่าจะเป็นวิธีที่ดีที่สุดในการส่งต่อ ไม่ว่าพฤติกรรมนั้นจะตั้งใจหรือไม่ก็ตามพวกเขาควรสนใจฟังเกี่ยวกับการถดถอย
สุดท้ายเพื่อจัดระเบียบสิ่งอื่น ๆ ที่กล่าวถึงในสคริปต์การทำซ้ำ: ตำแหน่งสุดท้ายของตัวกรองในแผนคำถามคือผลลัพธ์ของการสำรวจตามค่าใช้จ่ายGbAggAfterJoinSel
ย้ายการรวมและตัวกรองเหนือการรวมเนื่องจากผลลัพธ์การเข้าร่วมมีขนาดเล็ก จำนวนแถว ตัวกรองอยู่ด้านล่างการเข้าร่วมครั้งแรกตามที่คุณคาดหวัง