เพื่อความสมบูรณ์อีกวิธีหนึ่งในการแก้ไขปัญหานี้คือการใช้งานภายนอก เราสามารถเพิ่มOUTER APPLY
โอเปอเรเตอร์สำหรับแต่ละค่าที่แตกต่างที่เราต้องการค้นหา นี่เป็นแนวคิดที่คล้ายกับวิธีการเรียกซ้ำของ ypercube แต่มีการเรียกซ้ำด้วยมืออย่างมีประสิทธิภาพ ข้อดีอย่างหนึ่งคือเราสามารถใช้TOP
ในตารางที่ได้รับแทนROW_NUMBER()
วิธีแก้ปัญหา ข้อเสียอย่างหนึ่งที่สำคัญคือข้อความค้นหาจะได้รับนานN
ขึ้นเมื่อเพิ่มขึ้น
นี่คือการใช้งานหนึ่งสำหรับแบบสอบถามกับฮีป:
SELECT VAL
FROM (
SELECT t1.VAL VAL1, t2.VAL VAL2, t3.VAL VAL3, t4.VAL VAL4, t5.VAL VAL5, t6.VAL VAL6, t7.VAL VAL7, t8.VAL VAL8, t9.VAL VAL9, t10.VAL VAL10
FROM
(
SELECT TOP 1 VAL FROM X_10_DISTINCT_HEAP
) t1
OUTER APPLY
(
SELECT TOP 1 VAL FROM X_10_DISTINCT_HEAP t2 WHERE t2.VAL NOT IN (t1.VAL)
) t2
OUTER APPLY
(
SELECT TOP 1 VAL FROM X_10_DISTINCT_HEAP t3 WHERE t3.VAL NOT IN (t1.VAL, t2.VAL)
) t3
OUTER APPLY
(
SELECT TOP 1 VAL FROM X_10_DISTINCT_HEAP t4 WHERE t4.VAL NOT IN (t1.VAL, t2.VAL, t3.VAL)
) t4
OUTER APPLY
(
SELECT TOP 1 VAL FROM X_10_DISTINCT_HEAP t5 WHERE t5.VAL NOT IN (t1.VAL, t2.VAL, t3.VAL, t4.VAL)
) t5
OUTER APPLY
(
SELECT TOP 1 VAL FROM X_10_DISTINCT_HEAP t6 WHERE t6.VAL NOT IN (t1.VAL, t2.VAL, t3.VAL, t4.VAL, t5.VAL)
) t6
OUTER APPLY
(
SELECT TOP 1 VAL FROM X_10_DISTINCT_HEAP t7 WHERE t7.VAL NOT IN (t1.VAL, t2.VAL, t3.VAL, t4.VAL, t5.VAL, t6.VAL)
) t7
OUTER APPLY
(
SELECT TOP 1 VAL FROM X_10_DISTINCT_HEAP t8 WHERE t8.VAL NOT IN (t1.VAL, t2.VAL, t3.VAL, t4.VAL, t5.VAL, t6.VAL, t7.VAL)
) t8
OUTER APPLY
(
SELECT TOP 1 VAL FROM X_10_DISTINCT_HEAP t9 WHERE t9.VAL NOT IN (t1.VAL, t2.VAL, t3.VAL, t4.VAL, t5.VAL, t6.VAL, t7.VAL, t8.VAL)
) t9
OUTER APPLY
(
SELECT TOP 1 VAL FROM X_10_DISTINCT_HEAP t10 WHERE t10.VAL NOT IN (t1.VAL, t2.VAL, t3.VAL, t4.VAL, t5.VAL, t6.VAL, t7.VAL, t8.VAL, t9.VAL)
) t10
) t
UNPIVOT
(
VAL FOR VALS IN (VAL1, VAL2, VAL3, VAL4, VAL5, VAL6, VAL7, VAL8, VAL9, VAL10)
) AS upvt;
นี่คือแผนแบบสอบถามจริงสำหรับแบบสอบถามด้านบน ในเครื่องของฉันการค้นหานี้เสร็จสมบูรณ์ใน 713 ms ด้วยเวลา CPU 625 ms และการอ่านแบบลอจิคัล 12605 เราได้รับค่าที่แตกต่างใหม่ทุก ๆ 100k แถวดังนั้นฉันคาดหวังว่าแบบสอบถามนี้จะสแกนประมาณ 900,000 * 10 * 0.5 = 4500000 แถว ในทางทฤษฎีแล้วแบบสอบถามนี้ควรทำห้าครั้งที่ตรรกะอ่านของแบบสอบถามนี้จากคำตอบอื่น:
DECLARE @j INT = 10;
SELECT DISTINCT TOP (@j) VAL
FROM X_10_DISTINCT_HEAP
OPTION (MAXDOP 1, OPTIMIZE FOR (@j = 1));
แบบสอบถามนั้นทำการอ่านเชิงตรรกะ 2537 ครั้ง 2537 * 5 = 12685 ซึ่งค่อนข้างใกล้เคียงกับ 12605
สำหรับตารางที่มีดัชนีคลัสเตอร์เราสามารถทำได้ดีกว่า นี่เป็นเพราะเราสามารถส่งผ่านค่าคีย์คลัสเตอร์ล่าสุดลงในตารางที่ได้รับเพื่อหลีกเลี่ยงการสแกนแถวเดียวกันสองครั้ง การใช้งานหนึ่ง:
SELECT VAL
FROM (
SELECT t1.VAL VAL1, t2.VAL VAL2, t3.VAL VAL3, t4.VAL VAL4, t5.VAL VAL5, t6.VAL VAL6, t7.VAL VAL7, t8.VAL VAL8, t9.VAL VAL9, t10.VAL VAL10
FROM
(
SELECT TOP 1 PK, VAL FROM X_10_DISTINCT_CI
) t1
OUTER APPLY
(
SELECT TOP 1 PK, VAL FROM X_10_DISTINCT_CI t2 WHERE PK > t1.PK AND t2.VAL NOT IN (t1.VAL)
) t2
OUTER APPLY
(
SELECT TOP 1 PK, VAL FROM X_10_DISTINCT_CI t3 WHERE PK > t2.PK AND t3.VAL NOT IN (t1.VAL, t2.VAL)
) t3
OUTER APPLY
(
SELECT TOP 1 PK, VAL FROM X_10_DISTINCT_CI t4 WHERE PK > t3.PK AND t4.VAL NOT IN (t1.VAL, t2.VAL, t3.VAL)
) t4
OUTER APPLY
(
SELECT TOP 1 PK, VAL FROM X_10_DISTINCT_CI t5 WHERE PK > t4.PK AND t5.VAL NOT IN (t1.VAL, t2.VAL, t3.VAL, t4.VAL)
) t5
OUTER APPLY
(
SELECT TOP 1 PK, VAL FROM X_10_DISTINCT_CI t6 WHERE PK > t5.PK AND t6.VAL NOT IN (t1.VAL, t2.VAL, t3.VAL, t4.VAL, t5.VAL)
) t6
OUTER APPLY
(
SELECT TOP 1 PK, VAL FROM X_10_DISTINCT_CI t7 WHERE PK > t6.PK AND t7.VAL NOT IN (t1.VAL, t2.VAL, t3.VAL, t4.VAL, t5.VAL, t6.VAL)
) t7
OUTER APPLY
(
SELECT TOP 1 PK, VAL FROM X_10_DISTINCT_CI t8 WHERE PK > t7.PK AND t8.VAL NOT IN (t1.VAL, t2.VAL, t3.VAL, t4.VAL, t5.VAL, t6.VAL, t7.VAL)
) t8
OUTER APPLY
(
SELECT TOP 1 PK, VAL FROM X_10_DISTINCT_CI t9 WHERE PK > t8.PK AND t9.VAL NOT IN (t1.VAL, t2.VAL, t3.VAL, t4.VAL, t5.VAL, t6.VAL, t7.VAL, t8.VAL)
) t9
OUTER APPLY
(
SELECT TOP 1 PK, VAL FROM X_10_DISTINCT_CI t10 WHERE PK > t9.PK AND t10.VAL NOT IN (t1.VAL, t2.VAL, t3.VAL, t4.VAL, t5.VAL, t6.VAL, t7.VAL, t8.VAL, t9.VAL)
) t10
) t
UNPIVOT
(
VAL FOR VALS IN (VAL1, VAL2, VAL3, VAL4, VAL5, VAL6, VAL7, VAL8, VAL9, VAL10)
) AS upvt;
นี่คือแผนแบบสอบถามจริงสำหรับแบบสอบถามด้านบน ในเครื่องของฉันการค้นหานี้เสร็จสมบูรณ์ใน 154 ms ด้วยเวลา 140 ms ของ CPU และการอ่านแบบลอจิคัล 3203 ดูเหมือนว่าจะทำงานได้เร็วกว่าสักเล็กน้อยOPTIMIZE FOR
แบบสอบถามสำหรับตารางดัชนีคลัสเตอร์ ฉันไม่ได้คาดหวังดังนั้นฉันจึงพยายามวัดประสิทธิภาพอย่างระมัดระวังมากขึ้น วิธีการของผมคือการทำงานในแต่ละแบบสอบถามสิบครั้งโดยไม่ต้องชุดผลลัพธ์และดูที่ตัวเลขรวมจากและsys.dm_exec_sessions
sys.dm_exec_session_wait_stats
เซสชันที่ 56 เป็นAPPLY
คิวรีและเซสชัน 63 เป็นOPTIMIZE FOR
คิวรี
ผลผลิตของ sys.dm_exec_sessions
:
╔════════════╦══════════╦════════════════════╦═══════════════╗
║ session_id ║ cpu_time ║ total_elapsed_time ║ logical_reads ║
╠════════════╬══════════╬════════════════════╬═══════════════╣
║ 56 ║ 1360 ║ 1373 ║ 32030 ║
║ 63 ║ 2094 ║ 2091 ║ 30400 ║
╚════════════╩══════════╩════════════════════╩═══════════════╝
มีข้อได้เปรียบที่ชัดเจนใน cpu_time และ elapsed_time สำหรับการAPPLY
สืบค้น
ผลผลิตของ sys.dm_exec_session_wait_stats
:
╔════════════╦════════════════════════════════╦═════════════════════╦══════════════╦══════════════════╦═════════════════════╗
║ session_id ║ wait_type ║ waiting_tasks_count ║ wait_time_ms ║ max_wait_time_ms ║ signal_wait_time_ms ║
╠════════════╬════════════════════════════════╬═════════════════════╬══════════════╬══════════════════╬═════════════════════╣
║ 56 ║ SOS_SCHEDULER_YIELD ║ 340 ║ 0 ║ 0 ║ 0 ║
║ 56 ║ MEMORY_ALLOCATION_EXT ║ 38 ║ 0 ║ 0 ║ 0 ║
║ 63 ║ SOS_SCHEDULER_YIELD ║ 518 ║ 0 ║ 0 ║ 0 ║
║ 63 ║ MEMORY_ALLOCATION_EXT ║ 98 ║ 0 ║ 0 ║ 0 ║
║ 63 ║ RESERVED_MEMORY_ALLOCATION_EXT ║ 400 ║ 0 ║ 0 ║ 0 ║
╚════════════╩════════════════════════════════╩═════════════════════╩══════════════╩══════════════════╩═════════════════════╝
OPTIMIZE FOR
แบบสอบถามมีชนิดการรอเพิ่มเติมRESERVED_MEMORY_ALLOCATION_EXT ฉันไม่รู้ว่ามันหมายถึงอะไรกันแน่ อาจเป็นการวัดค่าใช้จ่ายในตัวดำเนินการจับคู่แฮช (การไหลที่ต่างกัน) ไม่ว่าในกรณีใด ๆ อาจไม่คุ้มค่าที่จะกังวลเกี่ยวกับความแตกต่างของ 70 มิลลิวินาทีในเวลา CPU