คอลัมน์ที่คำนวณอย่างต่อเนื่องทำให้เกิดการสแกน


9

การแปลงคอลัมน์ปกติเป็นคอลัมน์ที่คำนวณแล้วยังคงเป็นสาเหตุให้แบบสอบถามนี้ไม่สามารถค้นหาดัชนีได้ ทำไม?

ทดสอบกับ SQL Server หลายเวอร์ชันรวมถึง 2016 SP1 CU1

Repros

ปัญหาคือมี,table1col7

ตารางและแบบสอบถามเป็นต้นฉบับบางส่วน (และง่าย) ของต้นฉบับ ฉันทราบว่าคำถามอาจถูกเขียนใหม่แตกต่างกันและด้วยเหตุผลบางอย่างหลีกเลี่ยงปัญหา แต่เราจำเป็นต้องหลีกเลี่ยงการสัมผัสรหัสและคำถามที่ว่าทำไมtable1ไม่สามารถค้นหายังคงยืนอยู่

ดังที่ Paul White แสดงให้เห็น (ขอบคุณ!) การแสวงหานั้นจะเกิดขึ้นหากถูกบังคับดังนั้นคำถามคือ: ทำไมเครื่องมือเพิ่มประสิทธิภาพจึงไม่ได้ถูกเลือกโดยเครื่องมือเพิ่มประสิทธิภาพและไม่ว่าเราจะสามารถทำสิ่งที่แตกต่างออกไป รหัส?

เพื่อชี้แจงส่วนที่มีปัญหานี่คือการสแกนที่เกี่ยวข้องในแผนการดำเนินการที่ไม่ดี:

วางแผน

คำตอบ:


12

ทำไมเครื่องมือเพิ่มประสิทธิภาพจึงไม่ได้ถูกเลือก


TL: DRคำจำกัดความของคอลัมน์ที่ถูกคำนวณแบบขยายนั้นรบกวนความสามารถของเครื่องมือเพิ่มประสิทธิภาพในการจัดลำดับการรวมใหม่ในตอนแรก ด้วยจุดเริ่มต้นที่แตกต่างกันการปรับให้เหมาะสมตามต้นทุนจะใช้เส้นทางที่แตกต่างกันผ่านเครื่องมือเพิ่มประสิทธิภาพและลงท้ายด้วยตัวเลือกแผนขั้นสุดท้ายที่แตกต่างกัน


รายละเอียด

สำหรับทั้งหมดยกเว้นแบบสอบถามที่ง่ายที่สุดเครื่องมือเพิ่มประสิทธิภาพจะไม่พยายามสำรวจสิ่งต่าง ๆ เช่นพื้นที่ทั้งหมดของแผนที่เป็นไปได้ แต่จะเลือกจุดเริ่มต้นที่ดูสมเหตุสมผลจากนั้นใช้ความพยายามด้านงบประมาณในการสำรวจรูปแบบทางตรรกะและทางกายภาพในขั้นตอนการค้นหาอย่างน้อยหนึ่งระยะจนกว่าจะพบแผนการที่สมเหตุสมผล

เหตุผลหลักที่คุณได้รับแผนที่แตกต่างกัน (ด้วยการประมาณการต้นทุนขั้นสุดท้ายที่แตกต่างกัน) สำหรับทั้งสองกรณีคือมีจุดเริ่มต้นที่แตกต่างกัน การเริ่มต้นจากที่อื่นจะทำให้การเพิ่มประสิทธิภาพสิ้นสุดลงในที่ที่แตกต่างกัน (หลังจากการสำรวจซ้ำและการใช้งานซ้ำจำนวน จำกัด ) ฉันหวังว่ามันใช้งานง่ายพอสมควร

จุดเริ่มต้นที่ผมกล่าวถึงค่อนข้างขึ้นอยู่กับการแสดงเกี่ยวกับใจของแบบสอบถาม แต่การเปลี่ยนแปลงที่จะทำให้การแสดงต้นไม้ภายในขณะที่มันผ่านแยกผูกพันฟื้นฟูและทำให้เข้าใจง่ายขั้นตอนของการรวบรวมแบบสอบถาม

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

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

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

ต้นไม้ที่เกี่ยวข้องมีขนาดค่อนข้างใหญ่ แต่เพื่อแสดงให้เห็นต้นไม้แบบสอบถามเริ่มต้นคอลัมน์ที่ไม่ได้คำนวณเริ่มต้นด้วย: (สังเกตการรวมภายนอกสองรายการที่ด้านบน)

LogOp_Select
    LogOp_Apply (x_jtLeftOuter) 
        LogOp_LeftOuterJoin
            LogOp_NAryJoin
                LogOp_LeftAntiSemiJoin
                    LogOp_NAryJoin
                        LogOp_Get TBL: dbo.table1 (นามแฝง TBL: a4)
                        LogOp_Select
                            LogOp_Get TBL: dbo.table6 (นามแฝง TBL: a3)
                            ScaOp_Comp x_cmpEq
                                ScaOp_Identifier QCOL: [a3] .col18
                                ScaOp_Const TI (varchar collate 53256, Var, Trim, ML = 16)
                        LogOp_Select
                            LogOp_Get TBL: dbo.table1 (นามแฝง TBL: a1)
                            ScaOp_Comp x_cmpEq
                                ScaOp_Identifier QCOL: [a1] .col2
                                ScaOp_Const TI (varchar collate 53256, Var, Trim, ML = 16)
                        LogOp_Select
                            LogOp_Get TBL: dbo.table5 (นามแฝง TBL: a2)
                            ScaOp_Comp x_cmpEq
                                ScaOp_Identifier QCOL: [a2] .col2
                                ScaOp_Const TI (varchar collate 53256, Var, Trim, ML = 16)
                        ScaOp_Comp x_cmpEq
                            ScaOp_Identifier QCOL: [a4] .col2
                            ScaOp_Identifier QCOL: [a3] .col19
                    LogOp_Select
                        LogOp_Get TBL: dbo.table7 (นามแฝง TBL: a7)
                        ScaOp_Comp x_cmpEq
                            ScaOp_Identifier QCOL: [a7] .col22
                            ScaOp_Const TI (varchar collate 53256, Var, Trim, ML = 16)
                    ScaOp_Comp x_cmpEq
                        ScaOp_Identifier QCOL: [a4] .col2
                        ScaOp_Identifier QCOL: [a7] .col23
                LogOp_Select
                    LogOp_Get TBL: table1 (นามแฝง TBL: cdc)
                    ScaOp_Comp x_cmpEq
                        ScaOp_Identifier QCOL: [cdc] .col6
                        ScaOp_Const TI (smallint, ML = 2) XVAR (smallint, ไม่ใช่เจ้าของ, ค่า = 4)
                LogOp_Get TBL: dbo.table5 (นามแฝง TBL: a5) 
                LogOp_Get TBL: table2 (นามแฝง TBL: cdt)  
                ScaOp_Logical x_lopAnd
                    ScaOp_Comp x_cmpEq
                        ScaOp_Identifier QCOL: [a5] .col2
                        ScaOp_Identifier QCOL: [cdc] .col2
                    ScaOp_Comp x_cmpEq
                        ScaOp_Identifier QCOL: [a4] .col2
                        ScaOp_Identifier QCOL: [cdc] .col2
                    ScaOp_Comp x_cmpEq
                        ScaOp_Identifier QCOL: [cdt] .col1
                        ScaOp_Identifier QCOL: [cdc] .col1
            LogOp_ รับ TBL: table3 (นามแฝง TBL: ahcr)
            ScaOp_Comp x_cmpEq
                ScaOp_Identifier QCOL: [ahcr] .col9
                ScaOp_Identifier QCOL: [cdt] .col1

แฟรกเมนต์เดียวกันของแบบสอบถามคอลัมน์ที่คำนวณคือ: (สังเกตการรวมภายนอกด้านล่างลงมากนิยามคอลัมน์ที่คำนวณแล้วขยายและความแตกต่างเล็กน้อยอื่น ๆ ในการสั่งซื้อเข้าร่วม (ภายใน)

LogOp_Select
    LogOp_Apply (x_jtLeftOuter)
        LogOp_NAryJoin
            LogOp_LeftAntiSemiJoin
                LogOp_NAryJoin
                    LogOp_Get TBL: dbo.table1 (นามแฝง TBL: a4)
                    LogOp_Select
                        LogOp_Get TBL: dbo.table6 (นามแฝง TBL: a3)
                        ScaOp_Comp x_cmpEq
                            ScaOp_Identifier QCOL: [a3] .col18
                            ScaOp_Const TI (varchar collate 53256, Var, Trim, ML = 16)
                    LogOp_Select
                        LogOp_Get TBL: dbo.table1 (นามแฝง TBL: a1
                        ScaOp_Comp x_cmpEq
                            ScaOp_Identifier QCOL: [a1] .col2
                            ScaOp_Const TI (varchar collate 53256, Var, Trim, ML = 16)
                    LogOp_Select
                        LogOp_Get TBL: dbo.table5 (นามแฝง TBL: a2)
                        ScaOp_Comp x_cmpEq
                            ScaOp_Identifier QCOL: [a2] .col2
                            ScaOp_Const TI (varchar collate 53256, Var, Trim, ML = 16)
                    ScaOp_Comp x_cmpEq
                        ScaOp_Identifier QCOL: [a4] .col2
                        ScaOp_Identifier QCOL: [a3] .col19
                LogOp_Select
                    LogOp_Get TBL: dbo.table7 (นามแฝง TBL: a7) 
                    ScaOp_Comp x_cmpEq
                        ScaOp_Identifier QCOL: [a7] .col22
                        ScaOp_Const TI (varchar collate 53256, Var, Trim, ML = 16)
                ScaOp_Comp x_cmpEq
                    ScaOp_Identifier QCOL: [a4] .col2
                    ScaOp_Identifier QCOL: [a7] .col23
            LogOp_Project
                LogOp_LeftOuterJoin
                    LogOp_Join
                        LogOp_Select
                            LogOp_Get TBL: table1 (นามแฝง TBL: cdc) 
                            ScaOp_Comp x_cmpEq
                                ScaOp_Identifier QCOL: [cdc] .col6
                                ScaOp_Const TI (smallint, ML = 2) XVAR (smallint, ไม่ใช่เจ้าของ, ค่า = 4)
                        LogOp_Get TBL: table2 (นามแฝง TBL: cdt) 
                        ScaOp_Comp x_cmpEq
                            ScaOp_Identifier QCOL: [cdc] .col1
                            ScaOp_Identifier QCOL: [cdt] .col1
                    LogOp_ รับ TBL: table3 (นามแฝง TBL: ahcr) 
                    ScaOp_Comp x_cmpEq
                        ScaOp_Identifier QCOL: [ahcr] .col9
                        ScaOp_Identifier QCOL: [cdt] .col1
                AncOp_PrjList 
                    AncOp_PrjEl QCOL: [cdc] .col7
                        ScaOp_Convert char collate 53256, Null, Trim, ML = 6
                            ScaOp_IIF varchar เรียง 53256, Null, Var, Trim, ML = 6
                                ScaOp_Comp x_cmpEq
                                    ScaOp_Intrinsic เป็นตัวเลข
                                        ScaOp_Intrinsic right
                                            ScaOp_Identifier QCOL: [cdc] .col4
                                            ScaOp_Const TI (int, ML = 4) XVAR (int, ไม่ได้เป็นเจ้าของ, ค่า = 4)
                                    ScaOp_Const TI (int, ML = 4) XVAR (int, ไม่ได้เป็นเจ้าของ, ค่า = 0)
                                ScaOp_Const TI (varchar collate 53256, Var, Trim, ML = 1) XVAR (varchar, เป็นเจ้าของ, ค่า = Len, Data = (0,)
                                ScaOp_Intrinsic ซับสตริง
                                    ScaOp_Const TI (int, ML = 4) XVAR (int, ไม่ใช่เจ้าของ, ค่า = 6)
                                    ScaOp_Const TI (int, ML = 4) XVAR (int, ไม่ได้เป็นเจ้าของ, ค่า = 1)
                                    ScaOp_Identifier QCOL: [cdc] .col4
            LogOp_Get TBL: dbo.table5 (นามแฝง TBL: a5)
            ScaOp_Logical x_lopAnd
                ScaOp_Comp x_cmpEq
                    ScaOp_Identifier QCOL: [a5] .col2
                    ScaOp_Identifier QCOL: [cdc] .col2
                ScaOp_Comp x_cmpEq
                    ScaOp_Identifier QCOL: [a4] .col2
                    ScaOp_Identifier QCOL: [cdc] .col2

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

ในที่สุดสำหรับส่วนนี้การมีการรวมภายนอกที่ติดอยู่ตรงกลางของต้นไม้สามารถป้องกันการจับคู่กฎการเรียงลำดับใหม่เพิ่มเติมระหว่างการปรับให้เหมาะสมตามต้นทุน


การใช้คำแนะนำแผน (หรือUSE PLANคำแนะนำอย่างเท่าเทียมกัน- ตัวอย่างสำหรับคำค้นหาของคุณ ) เปลี่ยนกลยุทธ์การค้นหาให้เป็นวิธีที่มุ่งเน้นเป้าหมายมากขึ้นนำโดยรูปร่างทั่วไปและคุณสมบัติของแม่แบบที่ให้มา สิ่งนี้อธิบายว่าทำไมเครื่องมือเพิ่มประสิทธิภาพสามารถค้นหาtable1แผนเดียวกันกับทั้งคอลัมน์คอลัมน์ที่คำนวณและไม่คำนวณเมื่อใช้คำแนะนำแผนหรือคำใบ้

เราสามารถทำสิ่งที่แตกต่างเพื่อให้การค้นหาเกิดขึ้นได้หรือไม่

นี่เป็นสิ่งที่คุณต้องกังวลหากเครื่องมือเพิ่มประสิทธิภาพไม่พบแผนการที่มีคุณสมบัติประสิทธิภาพที่ยอมรับได้ด้วยตัวเอง

เครื่องมือปรับแต่งตามปกติทั้งหมดอาจใช้งานได้ ตัวอย่างเช่นคุณสามารถแบ่งแบบสอบถามออกเป็นส่วนที่ง่ายกว่าตรวจสอบและปรับปรุงการจัดทำดัชนีที่มีอยู่อัปเดตหรือสร้างสถิติใหม่ ... และอื่น ๆ

ทุกสิ่งเหล่านี้สามารถส่งผลกระทบต่อการประเมินความสำคัญของหัวใจ, เส้นทางของรหัสที่ใช้ผ่านเครื่องมือเพิ่มประสิทธิภาพและมีอิทธิพลต่อการตัดสินใจตามต้นทุนในรูปแบบที่ลึกซึ้ง

ในที่สุดคุณอาจใช้คำแนะนำ (หรือคู่มือวางแผน) แต่นั่นไม่ใช่วิธีที่ดีที่สุด


คำถามเพิ่มเติมจากความคิดเห็น

ฉันยอมรับว่าวิธีที่ดีที่สุดในการทำให้แบบสอบถามเป็นเรื่องง่ายขึ้น แต่มีวิธี (การตั้งค่าสถานะการติดตาม) เพื่อให้เครื่องมือเพิ่มประสิทธิภาพดำเนินการต่อด้วยการเพิ่มประสิทธิภาพและเข้าถึงผลลัพธ์เดียวกันหรือไม่

ไม่ไม่มีการตั้งค่าสถานะการสืบค้นกลับเพื่อทำการค้นหาแบบละเอียดและคุณไม่ต้องการ พื้นที่ค้นหาที่เป็นไปได้นั้นกว้างใหญ่และเวลารวบรวมที่เกินอายุของจักรวาลจะไม่ได้รับการตอบรับที่ดี เครื่องมือเพิ่มประสิทธิภาพไม่ทราบการแปลงเชิงตรรกะที่เป็นไปได้ (ไม่มีใครทำ)

นอกจากนี้ทำไมต้องมีการขยายที่ซับซ้อนเนื่องจากคอลัมน์ยังคงอยู่? เหตุใดเครื่องมือเพิ่มประสิทธิภาพจึงไม่สามารถขยายขยายให้ถือว่าเป็นคอลัมน์ปกติและไปถึงจุดเริ่มต้นเดียวกันได้

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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.