ทำไม SQL Server จะไม่สนใจดัชนี


16

ผมมีตารางCustPassMasterที่มี 16 คอลัมน์ในนั้นซึ่งเป็นหนึ่งและฉันสร้างดัชนีCustNum varchar(8) IX_dbo_CustPassMaster_CustNumเมื่อฉันเรียกใช้SELECTคำสั่งของฉัน:

SELECT * FROM dbo.CustPassMaster WHERE CustNum = '12345678'

จะละเว้นดัชนีอย่างสมบูรณ์ สับสนนี้ฉันเป็นฉันมีตารางอีกCustDataMasterด้วยวิธีการคอลัมน์อื่น ๆ (55) CustNum varchar(8)ซึ่งหนึ่งในนั้นคือ ฉันสร้างดัชนีในคอลัมน์นี้ ( IX_dbo_CustDataMaster_CustNum) ในตารางนี้และใช้แบบสอบถามเดียวกันจริง:

SELECT * FROM dbo.CustDataMaster WHERE CustNum = '12345678'

และใช้ดัชนีที่ฉันสร้างขึ้น

มีเหตุผลเฉพาะที่อยู่เบื้องหลังสิ่งนี้หรือไม่? ทำไมมันจะใช้ดัชนีจากCustDataMasterแต่ไม่จากCustPassMaster? มันเป็นเพราะการนับคอลัมน์ต่ำ?

แบบสอบถามแรกส่งคืน 66 แถว สำหรับแถวที่สองจะส่งคืน 1 แถว

นอกจากนี้หมายเหตุเพิ่มเติม: CustPassMasterมี 4991 บันทึกและCustDataMasterมี 5376 บันทึก นี่อาจเป็นเหตุผลที่ละเลยดัชนีหรือไม่ CustPassMasterยังมีระเบียนที่ซ้ำกันซึ่งมีCustNumค่าเหมือนกันเช่นกัน นี่เป็นปัจจัยอื่นหรือไม่

ฉันอ้างอิงข้อเรียกร้องนี้กับผลลัพธ์การดำเนินการตามจริงของแบบสอบถามทั้งสองรายการ

นี่คือ DDL สำหรับCustPassMaster(อันที่มีดัชนีที่ไม่ได้ใช้):

CREATE TABLE dbo.CustPassMaster(
    [CustNum] [varchar](8) NOT NULL,
    [Username] [char](15) NOT NULL,
    [Password] [char](15) NOT NULL,
    /* more columns here */
    [VBTerminator] [varchar](1) NOT NULL
) ON [PRIMARY]

CREATE NONCLUSTERED INDEX [IX_dbo_CustPassMaster_CustNum] ON dbo.CustPassMaster
(
    [CustNum] ASC
) WITH (PAD_INDEX = OFF
    , STATISTICS_NORECOMPUTE = OFF
    , SORT_IN_TEMPDB = OFF
    , DROP_EXISTING = OFF
    , ONLINE = OFF
    , ALLOW_ROW_LOCKS = ON
    , ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]

และ DDL สำหรับCustDataMaster(ฉันได้เว้นฟิลด์ที่ไม่เกี่ยวข้องจำนวนมาก):

CREATE TABLE dbo.CustDataMaster(
    [CustNum] [varchar](8) NOT NULL,
    /* more columns here */
    [VBTerminator] [varchar](1) NOT NULL
) ON [PRIMARY]

CREATE NONCLUSTERED INDEX [IX_dbo_CustDataMaster_CustNum] ON dbo.CustDataMaster
(
    [CustNum] ASC
)WITH (PAD_INDEX = OFF
    , STATISTICS_NORECOMPUTE = OFF
    , SORT_IN_TEMPDB = OFF
    , DROP_EXISTING = OFF
    , ONLINE = OFF
    , ALLOW_ROW_LOCKS = ON
    , ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]

ฉันไม่มีดัชนีคลัสเตอร์ในตารางใดตารางหนึ่งเหล่านั้นดัชนีเดียวแบบไม่คลัสเตอร์

ไม่สนใจข้อเท็จจริงที่ว่าประเภทข้อมูลไม่ตรงกับประเภทของข้อมูลที่จัดเก็บทั้งหมด ฟิลด์เหล่านี้เป็นข้อมูลสำรองจากฐานข้อมูล IBM AS / 400 DB2 และเป็นประเภทข้อมูลที่เข้ากันได้ (ฉันต้องสามารถสืบค้นฐานข้อมูลสำรองนี้ด้วยคำสืบค้นที่เหมือนกันทั้งหมดและได้ผลลัพธ์ที่แน่นอนเหมือนกัน )

ข้อมูลนี้จะถูกเพียงใช้สำหรับSELECTงบ ฉันไม่ได้ทำINSERT/ UPDATE/ DELETEคำสั่งใด ๆยกเว้นเมื่อแอปพลิเคชันสำรองกำลังคัดลอกข้อมูลจาก AS / 400


อาจคุ้มค่าที่จะอ่านบทความนี้เกี่ยวกับจุดเปลี่ยนจาก NonClustered ไปยัง Clustered sqlskills.com/blogs/kimberly/the-tipping-point-query-answers
Mark Sinkinson

3
นั่นคือความแตกต่าง หากแบบสอบถามแรกใช้ดัชนีของคุณจะต้องทำการค้นหา 65 ครั้ง อันนี้มีราคาแพง แบบสอบถามที่สองจะต้องดำเนินการเพียงหนึ่ง
Aaron Bertrand

คำตอบ:


18

โดยทั่วไปแล้วดัชนีจะถูกใช้โดย SQL Server หากเห็นว่าสมควรใช้ดัชนีมากกว่าจะใช้ตารางต้นแบบโดยตรง

ดูเหมือนว่าเครื่องมือเพิ่มประสิทธิภาพตามต้นทุนจะคิดว่ามันแพงกว่าที่จะใช้ดัชนีที่เป็นปัญหาจริง ๆ คุณอาจเห็นว่ามันใช้ดัชนีถ้าSELECT *คุณSELECT T1Col1ไม่ทำ

เมื่อคุณSELECT *บอกให้ SQL Server ส่งคืนคอลัมน์ทั้งหมดในตาราง ในการส่งคืนคอลัมน์เหล่านั้น SQL Server ต้องอ่านหน้าสำหรับแถวที่ตรงกับWHEREเกณฑ์คำสั่งจากตารางเอง (ดัชนีคลัสเตอร์หรือฮีป) SQL Server อาจคิดว่าจำนวนการอ่านที่ต้องการเพื่อรับส่วนที่เหลือของคอลัมน์จากตารางหมายความว่ามันอาจสแกนตารางโดยตรงเช่นกัน มันจะมีประโยชน์ในการดูแบบสอบถามจริงและแผนการดำเนินการจริงที่ใช้โดยแบบสอบถาม


3
ดังนั้นทางออกที่ชัดเจนและเหมาะสมที่สุดคือการ จำกัด คอลัมน์ที่ฉันเลือกและรวมไว้ในINCLUDEส่วนของดัชนี?
Der Kommissar

1
นั่นเป็นสิ่งที่สร้างความแตกต่างอย่างมาก การเพิ่มคอลัมน์ทั้งหมดที่ส่งคืนโดยแบบสอบถามไปยังINCLUDEประโยคจะทำให้ SQL Server ใช้ดัชนี ต้องบอกว่าคุณพยายามเพิ่มประสิทธิภาพอะไร ดูเหมือนว่าถ้าตารางของคุณมีขนาดแถวเฉลี่ย 100 ไบต์แล้ว 5,000 แถวเป็นข้อมูลเพียงประมาณ 500kb และอาจไม่คุ้มที่จะเสียเวลา
Max Vernon

1
ขนาดเฉลี่ยอยู่แถว 0.30KB สำหรับTable1และ 0.53KB Table2สำหรับ ข้อมูลทั้งหมดนี้นำเข้าจาก AS / 400 (IBM System i) และไม่มี PK ใด ๆ เลย ฉันสร้างดัชนีทั้งหมดด้วยตนเองในวันนี้หลังจากที่มีคนพูดถึงว่าแอปพลิเคชันค่อนข้างช้าในบางครั้ง
Der Kommissar

10

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

มีแนวโน้มมากที่สุดที่การประเมินค่าใช้จ่ายสำหรับการสแกนดัชนีที่ไม่ได้ทำคลัสเตอร์ + การค้นหาคีย์นั้นสูงกว่าการประเมินค่าใช้จ่ายสำหรับการสแกนดัชนีแบบคลัสเตอร์และนั่นเป็นสาเหตุที่ดัชนีนั้นถูกเพิกเฉย

คุณสามารถลองใช้set statistics io onแล้วใช้ดัชนีคำใบ้เพื่อดูว่าค่าใช้จ่าย I / O นั้นมีขนาดเล็กลงจริงเมื่อใช้ดัชนีหรือไม่ หากความแตกต่างนั้นใหญ่คุณสามารถดูสถิติได้หากสิ่งเหล่านั้นล้าสมัย

นอกจากนี้ถ้า SQL ของคุณใช้ตัวแปรจริงและไม่ใช่ค่าที่แน่นอนนี่อาจเกิดจากการดมพารามิเตอร์ (= ค่าก่อนหน้านี้ที่ใช้ในการสร้างแผนมีแถวจำนวนมากในตาราง)


1

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

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