पूर्णता के लिए, इस समस्या से निपटने का एक और तरीका है OUTER APPLY का उपयोग करना । हम 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 एमएस में 625 एमएसयू सीपीयू समय और 12605 तार्किक रीड के साथ पूरा होता है। हमें प्रत्येक 100k पंक्तियों में एक नया विशिष्ट मान मिलता है, इसलिए मैं इस क्वेरी को लगभग 900000 * 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 ║
╚════════════╩══════════╩════════════════════╩═══════════════╝
APPLY
क्वेरी के लिए cpu_time और elapsed_time में स्पष्ट लाभ प्रतीत होता है ।
का आउटपुट 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 एमएस के अंतर के बारे में चिंता करने योग्य नहीं है।