क्लोजेस्ट मैच, भाग 1 में, मैंने एक टी-एसक्यूएल चुनौती को कवर किया जो मुझे आरबीसी में एक जूनियर फिक्स्ड इनकम एनालिस्ट करेन ली द्वारा प्रदान की गई थी। चुनौती में दो टेबल शामिल थे - टी 1 और टी 2, प्रत्येक में एक कुंजी कॉलम (कीकॉल), एक मान (वैल), और कुछ अन्य कॉलम (अन्य कॉलम नामक कॉलम के साथ प्रतिनिधित्व) शामिल थे। कार्य T1 से प्रत्येक पंक्ति से मिलान करना था T2 से पंक्ति जहां T2.val T1.val के सबसे निकट है (पूर्ण अंतर सबसे कम है), एक टाईब्रेकर के रूप में सबसे कम T2.keycol मान का उपयोग करना। मैंने कुछ संबंधपरक समाधान प्रदान किए, और उनकी प्रदर्शन विशेषताओं पर चर्चा की। मैंने आपको उन मामलों में यथोचित निष्पादन समाधान खोजने का प्रयास करने और खोजने के लिए भी चुनौती दी है जहां आपको सहायक अनुक्रमणिका बनाने की अनुमति/सक्षम नहीं है। भाग 2 में, मैंने प्रदर्शित किया कि इसे पुनरावृत्त समाधानों का उपयोग करके कैसे प्राप्त किया जा सकता है। मुझे कामिल कोन्सनो, एलेजांद्रो मेसा और राडेक सेलुच से कई बहुत ही रोचक पाठक समाधान मिले, और वे समाधान इस महीने के लेख का फोकस हैं।
नमूना डेटा
एक अनुस्मारक के रूप में, मैंने आपको काम करने के लिए नमूना डेटा के छोटे और बड़े दोनों सेट प्रदान किए हैं। आपके समाधान की वैधता की जांच करने के लिए छोटे सेट और इसके प्रदर्शन की जांच के लिए बड़े सेट। नमूना डेटाबेस testdb बनाने के लिए निम्न कोड का उपयोग करें और इसके भीतर नमूना तालिका T1 और T2:
-- नमूना dbSET NOCOUNT ON; IF DB_ID('testdb') IS NULL CREATE DATABASE testdb; GO USE testdb;GO - सैंपल टेबल T1 और T2DROP टेबल यदि मौजूद है dbo.T1, dbo.T2; तालिका dbo.T1 बनाएं (कुंजीपटल नहीं पूर्ण पहचान बाधा PK_T1 प्राथमिक कुंजी, वैल INT शून्य नहीं, अन्य कॉल्स बाइनरी (100) पूर्ण बाधा नहीं DFT_T1_col1 डिफ़ॉल्ट (0xAA)); तालिका dbo.T2 बनाएं (कुंजीपटल नहीं पूर्ण पहचान बाधा PK_T2 प्राथमिक कुंजी, वैल INT शून्य नहीं, अन्य कॉल्स बाइनरी (100) पूर्ण बाधा नहीं DFT_T2_col1 डिफ़ॉल्ट (0xBB));
नमूना डेटा के छोटे सेट वाली तालिकाओं को भरने के लिए निम्न कोड का उपयोग करें:
-- T1 और T2 को नमूना डेटा के छोटे सेट के साथ पॉप्युलेट करें TRUNCATE TABLE dbo.T1;TRUNCATE TABLE dbo.T2; dbo.T1 (वैल) मान(1),(1),(3),(3),(5),(8),(13),(16),(18),(20),( 21); dbo.T2 (वैल) मान(2),(2),(7),(3),(3),(11),(11),(13),(17),(19);
विभिन्न समाधानों के तर्क का वर्णन करते समय मैं नमूना डेटा के छोटे सेट का उपयोग करूंगा और समाधानों के चरणों के लिए मध्यवर्ती परिणाम सेट प्रदान करूंगा।
हेल्पर फंक्शन बनाने के लिए निम्नलिखित कोड का उपयोग करें GetNums
और नमूना डेटा के बड़े सेट के साथ तालिकाओं को भरने के लिए:
-- हेल्पर फ़ंक्शन पूर्णांकों का एक क्रम उत्पन्न करने के लिए ड्रॉप फ़ंक्शन यदि मौजूद है dbo.GetNums; GO बनाएँ या फ़ंक्शन बदलें dbo.GetNums(@low as BIGINT, @high as BIGINT) L0 AS के साथ तालिकाएँ लौटाता है (चुनें c से चुनें) 1 यूनियन सभी चयन 1) एएस डी (सी)), एल 1 एएस (एल0 से एक क्रॉस जॉइन एल0 एएस बी के रूप में 1 एएस सी चुनें), एल 2 एएस (एल 1 के रूप में सी 1 को क्रॉस जॉइन एल 1 एएस बी के रूप में चुनें), एल 3 एएस (एक क्रॉस जॉइन एल 2 एएस बी के रूप में एल 2 से सी के रूप में चयन करें), एल 4 एएस (एल 3 से सी के रूप में 1 चुनें, एल 3 एएस बी के रूप में शामिल हों), एल 5 एएस (एल 4 से 1 एएस सी चुनें, क्रॉस जॉइन एल 4 एएस के रूप में चुनें) बी), नंबर्स एएस (सेलेक्ट ROW_NUMBER() ओवर (ऑर्डर बाय (सिलेक्ट न्यूल)) एएस राउनम फ्रॉम एल5) सिलेक्ट टॉप (@ हाई - @ लो + 1) @ लो + राउनम - 1 एएस एन फ्रॉम नंबर्स ऑर्डर बाय राउनम; GO - नमूना डेटा के बड़े सेट के साथ T1 और T2 को पॉप्युलेट करेंDECLARE @numrowsT1 AS INT =1000000, @maxvalT1 AS INT =10000000, @numrowsT2 AS INT =1000000, @maxvalT2 AS INT =10000000; TRUNCATE TABLE dbo.T1; TRUNCATE TABLE dbo.T2; (टैबलॉक) (वैल) के साथ dbo.T1 में डालें (वैल) एबीएस चुनें (चेकसम (नया ()))% @ maxvalT1 + 1 एएस वैल डीबीओ से। गेटनम्स (1, @ numrowsT1) अंकों के रूप में; (टैबलॉक) के साथ dbo.T2 में डालें (वैल) एबीएस चुनें (चेकसम (नया ()))% @ maxvalT2 + 1 एएस वैल डीबीओ से। गेटनम्स (1, @ numrowsT2) संख्याओं के रूप में;
जब आप सहायक इंडेक्स के साथ अपने समाधान का परीक्षण करना चाहते हैं, तो उन इंडेक्स को बनाने के लिए निम्न कोड का उपयोग करें:
डीबीओ पर इंडेक्स आईडीएक्स_वैल_की बनाएं। टी 1 (वैल, कीकॉल) शामिल करें (अन्य कॉल्स); डीबीओ पर इंडेक्स आईडीएक्स_वैल_की बनाएं। INCLUDE(othercols);
जब आप अनुक्रमणिका का समर्थन किए बिना अपने समाधान का परीक्षण करना चाहते हैं, तो उन अनुक्रमणिकाओं को निकालने के लिए निम्न कोड का उपयोग करें:
DROP INDEX IF EXISTS idx_val_key ON dbo.T1;DROP INDEX IF EXISTS idx_val_key ON dbo.T2;DROP INDEX IF EXISTS idx_valD_key ON dbo.T2;
कामिल कोस्नो द्वारा समाधान 1 - एक अस्थायी तालिका का उपयोग करना
कामिल कोस्नो ने अपने मूल विचारों के साथ कुछ - दो प्रस्तुत किए, और एलेजांद्रो और राडेक के समाधानों पर दो भिन्नताएं प्रस्तुत कीं। कामिल का पहला समाधान अनुक्रमित अस्थायी तालिका का उपयोग करता है। अक्सर ऐसा होता है कि भले ही आपको मूल तालिकाओं पर सहायक अनुक्रमणिका बनाने की अनुमति नहीं है, आपको अस्थायी तालिकाओं के साथ काम करने की अनुमति है, और अस्थायी तालिकाओं के साथ आप हमेशा अपनी अनुक्रमणिका बना सकते हैं।
यहाँ संपूर्ण समाधान का कोड है:
ड्रॉप टेबल अगर मौजूद है #T2; कीकोल के रूप में मिन (कीकोल) का चयन करें, वैलिंटो #T2FROM dbo.T2GROUP BY वैल; #T2(val, keycol) पर UNIQUE INDEX idx_nc_val_key बनाएं; बेस्टवल्स AS के साथ (सेलेक्ट कीकॉल AS keycol1, वैल AS वैल1, अन्यकॉल्स AS Othercols1, केस जब (prev + nxt IS NULL) तब COALESCE(prev, nxt) जब ABS(val - prev)समाधान के चरण 1 में आप प्रत्येक अद्वितीय वैल के लिए न्यूनतम कीकोल को #T2 नामक अस्थायी तालिका में संग्रहीत करते हैं और (वैल, कीकोल) पर एक सहायक अनुक्रमणिका बनाते हैं। इस चरण को लागू करने वाला कोड यहां दिया गया है:
ड्रॉप टेबल अगर मौजूद है #T2; कीकोल के रूप में मिन (कीकोल) का चयन करें, वैलिंटो #T2FROM dbo.T2GROUP BY वैल; #T2(val, keycol) पर UNIQUE INDEX idx_nc_val_key बनाएं; #T2 से * चुनें;अस्थायी तालिका निम्न डेटा से भरी हुई है:
कीकॉल वैल ----------- ----------1 24 33 76 118 139 1710 19समाधान के चरण 2 में आप निम्नलिखित को लागू करते हैं:
T1 में प्रत्येक पंक्ति के लिए #T2 से पिछले और nxt मानों की गणना करें। #T2 में अधिकतम वैल के रूप में प्रचलित गणना करें जो T1 में वैल से कम या उसके बराबर है। #T2 में न्यूनतम वैल के रूप में nxt की गणना करें जो T1 में वैल से अधिक या उसके बराबर है।
निम्न तर्क के आधार पर val2 की गणना करने के लिए CASE व्यंजक का उपयोग करें:
- जब पिछला या nxt शून्य हो, तो दोनों में से पहला गैर-शून्य लौटाएं, या यदि दोनों NULL हैं तो NULL लौटाएं।
- जब वैल और प्रीव के बीच का निरपेक्ष अंतर वैल और एनएक्सटी के बीच के निरपेक्ष अंतर से कम हो, तो पिछला रिटर्न दें।
- जब वैल और nxt के बीच का निरपेक्ष अंतर वैल और prv के बीच के निरपेक्ष अंतर से कम हो, तो nxt लौटाएं।
- अन्यथा, यदि nxt पिछले से कम है, तो nxt लौटाएं, अन्यथा पिछला लौटाएं।
इस चरण को लागू करने वाला कोड यहां दिया गया है:
कीकोल के रूप में कीकोल1, वैल एएस वैल1, अन्य कोल्स अन्य कॉल्स1 के रूप में चुनें, मामला जब (पिछला + अगला शून्य है) तब समाविष्ट (पिछला, अगला) जब एबीएस (वैल - पिछला) <एबीएस (वैल - एनएक्सटी) तब प्रचलित जब एबीएस (वैल - एनएक्सटी) <एबीएस (वैल - पिछला) फिर अगला ईएलएसई (मामला जब अगला <पिछला तब अगला ईएलएसई पिछला अंत) डीबीओ से वैल 2 के रूप में समाप्त होता है। टी 1 क्रॉस लागू करें ( # टी 2 से पहले के रूप में MAX (वैल) चुनें जहां वैल <=T1.val ) जैसा पिछला क्रॉस लागू होता है (#T2 से अगले के रूप में न्यूनतम (वैल) का चयन करें जहां T1.val <=वैल) अगले के रूप में;यह कोड निम्न आउटपुट उत्पन्न करता है:
keycol1 val1 अन्यcols1 val2 -------- -------------------------1 1 0xAA 22 1 0xAA 23 3 0xAA 34 3 0xAA 35 5 0xAA 36 8 0xAA 77 13 0xAA 138 16 0xAA 179 18 0xAA 1710 20 0xAA 1911 21 0xAA 19समाधान के चरण 3 में, आप चरण 2 की क्वेरी के आधार पर बेस्टवल नामक सीटीई परिभाषित करते हैं। फिर आप कुंजी प्राप्त करने के लिए #T2 के साथ bestvals में शामिल होते हैं, और शेष डेटा (अन्य कॉलम) प्राप्त करने के लिए परिणाम को T2 के साथ जोड़ते हैं।
इस चरण को लागू करने वाला कोड यहां दिया गया है:
चयन keycol1, val1, सबस्ट्रिंग(othercols1, 1, 1) as othercols1, tempT2.keycol AS keycol2, val2, SUBSTRING(T2.othercols, 1, 1) as othercols2FROM bestvals AS B INNER JOIN #T2 AS tempT2 on tempT2 .val =B.val2 इनर जॉइन dbo.T2 ON T2.keycol =tempT2.keycol;कामिल द्वारा समाधान 1 की योजना साथ सहायक सूचकांक चित्र 1 में दिखाए गए हैं।
चित्र 1:कामिल द्वारा अनुक्रमणिका के साथ समाधान 1 की योजना बनाएं
योजना समूहों की पहली शाखा T2 से डेटा एकत्र करती है और परिणाम को अस्थायी तालिका # T2 में लिखती है। ध्यान दें कि यह शाखा पहले से ऑर्डर किए गए स्ट्रीम एग्रीगेट एल्गोरिथम का उपयोग करती है जो T2 पर इंडेक्स idx_valD_key के ऑर्डर किए गए स्कैन पर निर्भर करता है।
यहां इस योजना के प्रदर्शन आंकड़े दिए गए हैं:
रन टाइम (सेकंड):5.55, CPU (सेकंड):16.6, लॉजिकल रीड्स:6,687,172कामिल द्वारा समर्थित अनुक्रमणिका के बिना समाधान 1 की योजना चित्र 2 में दिखाई गई है।
चित्र 2:कामिल द्वारा बिना अनुक्रमणिका के समाधान 1 की योजना बनाएं
इस योजना और पिछली योजना के बीच मुख्य अंतर यह है कि योजना की पहली शाखा, जो चरण 1 में समूहीकरण और एकत्रीकरण भाग को संभालती है, इस बार एक सहायक सूचकांक से पहले से ऑर्डर किए गए डेटा को नहीं खींच सकती है, इसलिए यह इसे क्लस्टर से अनियंत्रित खींचती है। T2 पर इंडेक्स करता है और फिर हैश एग्रीगेट एल्गोरिथम का उपयोग करता है।
यहां इस योजना के प्रदर्शन आंकड़े दिए गए हैं:
रन टाइम:6.44, CPU:19.3, लॉजिकल रीड्स:6,688,277अलेजांद्रो मेसा द्वारा समाधान - अंतिम गैर-शून्य तकनीक का उपयोग करना
एलेजांद्रो का समाधान यहां वर्णित अंतिम गैर-शून्य तकनीक का उपयोग करता है।
यहां संपूर्ण समाधान का कोड दिया गया है (अन्य कोल्स लौटाए बिना यहां दिया गया है):
R1 AS के साथ (कीकोल, वैल, tid, MAX चुनें (केस जब tid =0 तब CAST (वैल एएस बाइनरी (4)) + CAST (कीकोल एएस बाइनरी (4)) END) ओवर (वैल द्वारा ऑर्डर करें, tid , असीमित पूर्ववर्ती और 1 पूर्ववर्ती के बीच कीकॉल पंक्तियाँ नीचे_बिनस्ट्र के रूप में, मिन (मामला जब tid =0 तब कास्ट (वैल एएस बाइनरी (4)) + कास्ट (कीकोल एएस बाइनरी (4)) अंत) ओवर (वैल, टीआईडी द्वारा ऑर्डर करें, 1 निम्नलिखित के बीच कीकॉल पंक्तियाँ और अनबाउंडेड फॉलोइंग) जैसा कि ऊपर_बिनस्ट्र से (सेलेक्ट कीकॉल, वैल, डीबीओ से टीआईडी के रूप में 1 चुनें। ),R2 AS (चुनें keycol, val, CAST(SUBSTRING(low_binstr, 1, 4) AS int) AS down_T2_val, CAST(SUBSTRING(low_binstr, 5, 4) AS int) AS down_T2_keycol, CAST(SUBSTRING(above_binstr, 1, 4) एएस इंट) एएस ऊपर_टी2_वैल, कास्ट (सबस्ट्रिंग (उपरोक्त_बिनस्ट्र, 5, 4) एएस इंट) एएस ऊपर_टी2_कीकॉल आर1 से जहां टीआईडी =1), आर3 एएस (सेलेक्ट कीकोल, वैल, कोलेस (नीचे_टी2_वैल, ऊपर_टी2) वैल) नीचे_T2_val के रूप में, COALESCE(नीचे_T2_keycol, ऊपर_T2_keycol) AS नीचे_T2_keycol, COALESCE (ऊपर_T2_val, नीचे_T2_val) AS ऊपर_T2_val, COALESCE (WH से ऊपर_T2_keycol के ऊपर, नीचे _T_T2_keycol1 के नीचे सीए - वैल AST2_keycol के नीचे सीए - वैल एएसटी 2_कीकॉल, नीचे वैल एएसटी 2_कीकॉल), ) <=ABS (ऊपर_T2_val - वैल) फिर नीचे_T2_keycol ELSE ऊपर_T2_keycol END AS keycol2, CASE जब ABS (वैल - नीचे_T2_val) <=ABS (ऊपर_T2_val - वैल) फिर नीचे_T2_val ELSE ऊपर_T2_val R3 END AS;>समाधान के चरण 1 में, आप T1 से पंक्तियों को एकीकृत करते हैं (एक परिणाम स्तंभ जिसे tid से 1 कहा जाता है) को T2 की पंक्तियों के साथ (tid =0 निर्दिष्ट करना), और परिणाम के आधार पर T नामक एक व्युत्पन्न तालिका को परिभाषित करते हैं। प्रत्येक वर्तमान पंक्ति के लिए val, tid, keycol के क्रम के आधार पर अंतिम गैर-शून्य तकनीक का उपयोग करके, आप अंतिम tid =0 पंक्ति के मान को एक बाइनरी कॉलम में समेटे हुए प्राप्त करते हैं जिसे नीचे_बिनस्ट्र कहा जाता है। इसी तरह, आप अगली tid =0 पंक्ति के मानों को एक बाइनरी कॉलम में समेटे हुए प्राप्त करते हैं जिसे ऊपर_बिंस्ट्र कहा जाता है।
इस चरण को लागू करने वाला कोड यहां दिया गया है:
चुनें keycol, val, tid, MAX (केस जब tid =0 तब CAST(val AS बाइनरी (4)) + CAST(keycol AS बाइनरी (4)) END) ओवर (वैल, tid, keycol ROWS के बीच ऑर्डर करें) असीमित पूर्ववर्ती और 1 पूर्ववर्ती) नीचे_बिनस्ट्र के रूप में, न्यूनतम (मामला जब tid =0 तब कास्ट (वैल एएस बाइनरी (4)) + कास्ट (बाइनरी के रूप में कीकोल (4)) अंत) ओवर (वैल, टीआईडी, कीकोल पंक्तियों द्वारा ऑर्डर 1 के बीच निम्नलिखित और असीमित निम्नलिखित) ऊपर के रूप में (कीकोल, वैल, 1 डीबीओ से टीआईडी के रूप में चुनें। टी 1 यूनियन सभी चयन मिन (कीकोल) एएस कीकोल, वैल, 0 डीबीओ से टी 2 के रूप में। टी 2 ग्रुप बाय वैल) टी के रूप में;यह कोड निम्न आउटपुट उत्पन्न करता है:
कीकॉल वैल टिड नीचे_बिनस्ट्र ऊपर_बिनस्ट्र----------------------------------------------- ---------------------------------- 1 1 पूर्ण 0x00000002000000012 1 1 शून्य 0x00000002000000011 2 0 शून्य 0x00000003000000044 3 0 0x0000000200000001 0x00000007000000033 3 1 0x0000000300000004 0x00000007000000034 3 1 0x0000000300000004 0x000000070000000035 5 1 0x0000000300000004 0x00000007000000033 7 0 0x0000000300000004 0x0000000B000000066 8 1 0x0000000700000003 0x00000000700000003 0x000000000D000000088 13 0 0x00000B00000006 0x000000110000000000000 13 1 0x000000000B00000006 08 0x00000011000000098 16 1 0x0000000D00000008 0x00000011000000099 17 0 0x0000000D00000008 0x000000130000000A9 18 1 0x0000001100000009 0x000000130000000A10 19 0 0x0000001100000009 NULL10 20 1 0x000000130000000A NULL11 21 1 0x000000130000000A NULLसमाधान के चरण 2 में आप चरण 1 की क्वेरी के आधार पर R1 नामक एक CTE परिभाषित करते हैं। आप R1 को क्वेरी करते हैं, केवल उन पंक्तियों को फ़िल्टर करते हैं जहाँ tid =1 है, और अलग-अलग मानों को सम्मिलित बाइनरी स्ट्रिंग्स से निकालें।
इस चरण को लागू करने वाला कोड यहां दिया गया है:
सेलेक्ट कीकॉल, वैल, कास्ट (सबस्ट्रिंग (नीचे_बिनस्ट्र, 1, 4) एएस इंट) एएस बॉटम_टी 2_वैल, कास्ट (सबस्ट्रिंग (नीचे_बिनस्ट्र, 5, 4) एएस इंट) एएस बॉटम_टी 2_कीकोल, कास्ट (सबस्ट्रिंग (उपरोक्त_बिनस्ट्र, 1, 4) AS int) AS ऊपर_T2_val, CAST(SUBSTRING(above_binstr, 5, 4) AS int) AS ऊपर_T2_keycolFROM R1WHERE tid =1;यह कोड निम्न आउटपुट उत्पन्न करता है:
कीकॉल वैल नीचे_T2_val नीचे_T2_keycol ऊपर_T2_val ऊपर_T2_keycol-------------------------------------------------- -------- --------------------------------- 1 नल न्यूल 2 12 1 न्यूल न्यूल 2 13 3 3 4 7 34 3 3 4 7 35 5 3 4 7 36 8 7 3 11 67 13 13 8 17 98 16 13 8 17 99 18 17 9 19 1010 20 19 10 न्यूल न्यूल11 21 19 10 न्यूल न्यूलसमाधान के चरण 3 में आप चरण 2 की क्वेरी के आधार पर R2 नामक CTE को परिभाषित करते हैं। फिर आप निम्न परिणाम कॉलम की गणना करते हैं:
- नीचे_T2_val:नीचे_T2_val और ऊपर_T2_val के बीच पहला गैर-शून्य
- नीचे_T2_keycol:नीचे_T2_keycol और ऊपर_T2_keycol के बीच पहला गैर-शून्य
- ऊपर_T2_val:ऊपर_T2_val और नीचे_T2_val के बीच पहला गैर-शून्य
- ऊपर_T2_keycol:ऊपर_T2_keycol और नीचे_T2_keycol के बीच पहला नॉन-नल
इस चरण को लागू करने वाला कोड यहां दिया गया है:
चयन कुंजीकॉल, वैल, COALESCE(नीचे_T2_val, ऊपर_T2_val) नीचे के रूप में_T2_val, COALESCE(नीचे_T2_keycol, ऊपर_T2_keycol) AS नीचे_T2_keycol, COALESCE(above_T2_val, नीचे_T2_val, COALESCE (ऊपर_T2_val, नीचे_T2_val) के रूप में पूर्व_T2_col (नीचे_T2_val) के रूप में पूर्व_T_colयह कोड निम्न आउटपुट उत्पन्न करता है:
कीकॉल वैल नीचे_T2_val नीचे_T2_keycol ऊपर_T2_val ऊपर_T2_keycol-------------------------------------------------- -------- --------------------------------- 1 1 2 1 2 12 1 2 1 2 13 3 3 4 7 34 3 3 4 7 35 5 3 4 7 36 8 7 3 11 67 13 13 8 17 98 16 13 8 17 99 18 17 9 19 1010 20 19 10 19 1011 21 19 10 19 10समाधान के चरण 4 में आप चरण 3 की क्वेरी के आधार पर R3 नामक CTE को परिभाषित करते हैं। आप कीकोल को T1_keycol के रूप में उपनामित करते हैं और वैल को T1_val के रूप में उपनामित करते हैं। परिणाम कॉलम T2_keycol की गणना इस प्रकार करें:यदि वैल और नीचे_T2_val के बीच पूर्ण अंतर ऊपर_T2_val और वैल के बीच पूर्ण अंतर से कम या बराबर है, तो नीचे_T2_keycol वापस करें, अन्यथा ऊपर_T2_keycol। परिणाम कॉलम T2_val की गणना इस प्रकार करें:यदि वैल और नीचे_T2_val के बीच पूर्ण अंतर ऊपर_T2_val और वैल के बीच पूर्ण अंतर से कम या बराबर है, तो नीचे_T2_val, अन्यथा ऊपर_T2_val पर लौटें।
इस चरण को लागू करने वाला कोड यहां दिया गया है:
कीकॉल के रूप में कीकोल1, वैल एएस वैल1, केस जब एबीएस (वैल - नीचे_T2_val) <=एबीएस (ऊपर_T2_val - वैल) तब नीचे_T2_keycol ELSE ऊपर_T2_keycol END AS keycol2, केस जब ABS (वैल - नीचे_T2_val) <=ABS (ऊपर -) वैल) फिर नीचे_T2_val ELSE ऊपर_T2_val END AS val2FROM R3;सहायक इंडेक्स के साथ एलेजांद्रो के समाधान की योजना चित्र 3 में दिखाई गई है।
चित्र 3:अनुक्रमणिका के साथ एलेजांद्रो द्वारा समाधान की योजना
यहां इस योजना के प्रदर्शन आंकड़े दिए गए हैं:
रन टाइम:7.8, CPU:12.6, लॉजिकल रीड्स:35,886अनुक्रमणिका का समर्थन किए बिना एलेजांद्रो के समाधान की योजना चित्र 4 में दिखाई गई है।
चित्र 4:अनुक्रमणिका के बिना एलेजांद्रो द्वारा समाधान की योजना
यहां इस योजना के प्रदर्शन आंकड़े दिए गए हैं:
रन टाइम:8.19, CPU:14.8, लॉजिकल रीड्स:37,091ध्यान दें कि चित्र 4 में योजना की पहली दो शाखाओं में सहायक इंडेक्स की कमी के कारण मर्ज जॉइन (Concatenation) ऑपरेटर से पहले दो प्रकार हैं। चित्र 3 में योजना में उन प्रकारों की आवश्यकता नहीं है क्योंकि डेटा को सहायक अनुक्रमणिका से पूर्व-आदेशित किया गया है।
अलेजांद्रो के समाधान पर कामिल की भिन्नता
इस समाधान में, कामिल ने अंतिम गैर-शून्य तकनीक पर भी भरोसा किया। यहाँ संपूर्ण समाधान का कोड है:
ऑल_वल्स एएस के साथ (सेलेक्ट कीकोल, वैल फ्रॉम डीबीओ.टी1 यूनियन ऑल सेलेक्ट डिस्टिंट न्यूल, वैल फ्रॉम डीबीओ.टी2), रेंज एएस (सेलेक्ट कीकोल, वैल, मैक्स (केस जब कीकोल इज न्यूल थन वैल एंड) ओवर (ऑर्डर वैल, कीकॉल पंक्तियों के बीच असीमित पूर्ववर्ती और 1 पूर्ववर्ती) के रूप में पिछला, मिन (मामला जब कीकोल शून्य है तो वैल END) ओवर (वैल द्वारा ऑर्डर, कीकॉल पंक्तियों के बीच 1_vallowing और unbounded FOLLOWING के रूप में) keycol1, val AS val1, CASE जब ABS (वैल - पिछला) <=ISNULL (ABS (वैल - nxt), पिछला) के रूप में चयन करें, फिर पिछला ELSE nxt END AS val2 उन श्रेणियों से जहां keycol NULL नहीं है) keycol1, val1, चुनें सबस्ट्रिंग(T1.othercols, 1, 1) as othercols1, val2, T2.keycol AS keycol2, SUBSTRING(T2.othercols, 1, 1) as othercols2 from matched_vals as M INNER JOIN ( SELECT *, ROW_NUMBER() OVER (पार्टिशन बाय) वैल ऑर्डर द्वारा कीकोल) डीबीओ.टी2 से आरएन के रूप में) टी2 पर टी2.वैल =एम.वैल2 और आरएन =1 इनर जॉइन डीबीओ.टी1 ऑन टी1.कीकोल =कीकोल1;समाधान के चरण 1 में आप सीटीई को परिभाषित करते हैं जिसे ऑल_वल्स और रेंज कहा जाता है, जहां आप टी 1 (जहां कीकोल न्यूल नहीं है) में प्रत्येक मान की पहचान करने के लिए अंतिम गैर-तकनीक का उपयोग करते हैं, नीचे (पिछला) और ऊपर (एनएक्सटी) मानों से टी 2 ( जहां keycol रिक्त है)।
इस चरण को लागू करने वाला कोड यहां दिया गया है:
ऑल_वल्स एएस के साथ (सेलेक्ट कीकोल, वैल फ्रॉम डीबीओ.टी1 यूनियन ऑल सेलेक्ट डिस्टिंट न्यूल, वैल फ्रॉम डीबीओ.टी2), रेंज एएस (सेलेक्ट कीकोल, वैल, मैक्स (केस जब कीकोल इज न्यूल थन वैल एंड) ओवर (ऑर्डर वैल द्वारा, कीकॉल पंक्तियों के बीच असीमित पूर्ववर्ती और 1 पूर्ववर्ती) के रूप में पिछला, मिन (मामला जब कीकोल शून्य है तो वैल एंड) ओवर (वैल द्वारा आदेश, कीकॉल पंक्तियों के बीच 1_vallowing और unbounded FOLLOWING) के रूप में चुनें;यह कोड निम्न आउटपुट उत्पन्न करता है:
कीकॉल वैल पिछला अगला------------------------------------------------------- -1 1 NULL 22 1 NULL 2NULL 2 NULL 3NULL 3 2 73 3 3 74 3 3 75 5 3 7NULL 7 3 116 8 7 11NULL 11 7 13NULL 13 11 177 13 13 178 16 13 17NULL 17 13 199 18 17 19NULL 19 17 NULL10 20 19 NULL11 21 19 NULLसमाधान के चरण 2 में आप CTE श्रेणियों को क्वेरी करते हैं और केवल उन पंक्तियों को फ़िल्टर करते हैं जहाँ keycol रिक्त नहीं है। आप T1 से कीकोल और वैल कॉलम को क्रमशः keycol1 और val1 के रूप में लौटाते हैं। फिर, पिछले और nxt के बीच, आप val1 के निकटतम को val2 के रूप में लौटाते हैं।
इस चरण को लागू करने वाला कोड यहां दिया गया है:
कीकोल के रूप में कीकोल1 का चयन करें, वैल एएस वैल1 के रूप में, मामला जब एबीएस (वैल - पिछला) <=ISNULL(ABS(val - nxt), पिछला) फिर पिछला ELSE END AS val2FROM रेंजजहाँ keycol NULL नहीं है;यह कोड निम्न आउटपुट उत्पन्न करता है:
keycol1 val1 val2-----------------------------------------1 1 22 1 23 3 34 3 35 5 36 8 77 13 138 16 179 18 1710 20 1911 21 19समाधान के चरण 3 में आप चरण 2 से क्वेरी के आधार पर एक CTE को matched_vals नाम से परिभाषित करते हैं। आप T2 नामक एक व्युत्पन्न तालिका भी परिभाषित करते हैं, जहां विभाजित पंक्ति संख्याओं का उपयोग करके आप dbo.T2 से पंक्तियों के लिए टाईब्रेकिंग तर्क को संभालते हैं। फिर आप अंतिम मिलान तर्क को संभालने के लिए matched_vals, CTE T2 और तालिका T1 से जुड़ते हैं।
इस चरण को लागू करने वाला कोड यहां दिया गया है:
सेलेक्ट keycol1, val1, SUBSTRING(T1.othercols, 1, 1) AS othercols1, val2, T2.keycol AS keycol2, SUBSTRING(T2.othercols, 1, 1) AS othercols2 from matched_vals AS M INNER JOIN (चुनें * , ROW_NUMBER() ओवर (पार्टिशन बाय वैल ऑर्डर बाय कीकोल) AS RN FROM dbo.T2 ) AS T2 ON T2.val =M.val2 और rn =1 इनर जॉइन dbo.T1 ON T1.keycol =keycol1;सहायक सूचकांकों के साथ एलेजांद्रो के समाधान पर कामिल की भिन्नता की योजना चित्र 5 में दिखाई गई है।
चित्र 5:अनुक्रमित के साथ एलेजांद्रो के समाधान पर कामिल की विविधता के लिए योजना
यहां इस योजना के प्रदर्शन आंकड़े दिए गए हैं:
रन टाइम:11.5, CPU:18.3, लॉजिकल रीड्स:59,218अलेजांद्रो के समाधान पर कामिल के बदलाव की योजना को बिना समर्थन अनुक्रमित के चित्र 6 में दिखाया गया है।
चित्र 6:बिना अनुक्रमणिका के एलेजांद्रो के समाधान पर कामिल के बदलाव की योजना
यहां इस योजना के प्रदर्शन आंकड़े दिए गए हैं:
रन टाइम:12.6, CPU:23.5, लॉजिकल रीड्स:61,432एलेजांद्रो के समाधान के लिए योजनाओं के समान, इस मामले में भी चित्रा 5 में योजना को मर्ज जॉइन (संयोजन) ऑपरेटर को लागू करने से पहले स्पष्ट प्रकार की आवश्यकता नहीं होती है क्योंकि डेटा को सहायक इंडेक्स से पहले से प्राप्त किया जाता है, और चित्र 6 में योजना करता है स्पष्ट प्रकारों की आवश्यकता है क्योंकि कोई सहायक अनुक्रमणिका नहीं हैं।
राडेक सेलुच द्वारा समाधान - अंतराल का उपयोग करना
राडेक एक सरल और सुंदर विचार लेकर आए। T2.val में प्रत्येक विशिष्ट मान के लिए आप T1.val से मानों की श्रेणी का प्रतिनिधित्व करने वाले अंतराल की गणना करते हैं जो वर्तमान T2.val से मेल खाएगा।
यहाँ संपूर्ण समाधान का कोड है:
प्री1 एएस के साथ (कीकोल, वैल, अन्य कोल्स, ROW_NUMBER () ओवर (कीकोल द्वारा वैल ऑर्डर द्वारा विभाजन) डीबीओ से आरएन के रूप में। टी 2), प्री 2 एएस (सेलेक्ट कीकोल, वैल, अन्य कोल्स, लैग (वैल) ओवर ( वैल द्वारा ऑर्डर करें) पिछले के रूप में, लीड (वैल) ओवर (वैल द्वारा ऑर्डर) प्री 1 से अगले के रूप में जहां आरएन =1), टी 2 एएस (चुनें कीकोल, वैल, अन्यकॉल, ISNULL (वैल - (वैल - पिछला) / 2 + ( 1 - (वैल - पिछला)% 2), 0) कम के रूप में, ISNULL (वैल + (अगला - वैल) / 2, 2147483647) प्री 2 से उच्च के रूप में) चयन करें T1.keycol AS keycol1, T1.val AS val1, SUBSTRING( T1.othercols, 1, 1) AS othercols1, T2.keycol AS keycol2, T2.val AS val2, SUBSTRING(T2.othercols, 1, 1) AS othercols2FROM dbo.T1 इनर जॉइन T1 पर T1.val के बीच T2.low और T2.high;समाधान के चरण 1 में आप T2 को क्वेरी करते हैं और पंक्ति संख्याओं की गणना करते हैं (कॉलम rn पर कॉल करें), वैल द्वारा विभाजित और कीकोल द्वारा आदेशित। आप इस क्वेरी के आधार पर प्री1 नामक सीटीई परिभाषित करते हैं। फिर आप Pre1 को क्वेरी करते हैं और केवल उन पंक्तियों को फ़िल्टर करते हैं जहां rn =1। उसी क्वेरी में, LAG और LEAD का उपयोग करके, आप प्रत्येक पंक्ति के लिए पिछली पंक्ति से वैल कॉलम का मान लौटाते हैं (इसे पिछला कहते हैं) और अगली पंक्ति से ( इसे अगला कॉल करें)।
इस चरण को लागू करने वाला कोड यहां दिया गया है:
प्री1 एएस के साथ (कीकोल, वैल, अन्य कोल्स, ROW_NUMBER () ओवर (वैल ऑर्डर द्वारा कीकोल द्वारा पार्टिशन) डीबीओ से आरएन के रूप में चुनें। पिछले के रूप में, LEAD(val) OVER(वैल द्वारा आदेश) अगले के रूप में Pre1WHERE rn =1;यह कोड निम्न आउटपुट उत्पन्न करता है:
कीकॉल वैल अन्यकॉल्स पिछला अगला-------------------------------------------------- -1 2 0xBB NULL 34 3 0xBB 2 73 7 0xBB 3 116 11 0xBB 7 138 13 0xBB 11 179 17 0xBB 13 1910 19 0xBB 17 NULLसमाधान के चरण 2 में आप चरण 1 की क्वेरी के आधार पर प्री 2 नामक सीटीई को परिभाषित करते हैं। आप प्री 2 को क्वेरी करते हैं और प्रत्येक पंक्ति के लिए एक अंतराल [निम्न, उच्च] की गणना करते हैं जहां टी 1 से वैल गिर जाएगा। यहां कम और के लिए गणनाएं दी गई हैं। उच्च:
- कम =ISNULL (वैल - (वैल - पिछला) / 2 + (1 - (वैल - पिछला)% 2), 0)
- उच्च =ISNULL (वैल + (अगला - वैल) / 2, 2147483647)
निम्न सीमा की गणना पर मॉड 2 चेक का उपयोग निम्न T2.val आवश्यकता को पूरा करने के लिए किया जाता है, और पंक्ति संख्या फ़िल्टर का उपयोग निम्न T2.keycol आवश्यकता को पूरा करने के लिए किया जाता है। मान 0 और 2147483647 का उपयोग अत्यधिक निचली और ऊपरी सीमा के रूप में किया जाता है।
इस चरण को लागू करने वाला कोड यहां दिया गया है:
चुनें keycol, val, othercols, ISNULL(val - (val - prev) / 2 + (1 - (val - prev)% 2), 0) AS Low, ISNULL(val + (next - val) / 2 , 2147483647) प्री2 से उच्च के रूप में;यह कोड निम्न आउटपुट उत्पन्न करता है:
कीकोल वैल अदरकॉल्स लो हाई----------------------------------------------1 2 0xBB 0 24 3 0xBB 3 53 7 0xBB 6 96 11 0xBB 10 128 13 0xBB 13 159 17 0xBB 16 1810 19 0xBB 19 2147483647समाधान के चरण 3 में आप चरण 2 की क्वेरी के आधार पर T2 नामक एक CTE को परिभाषित करते हैं। फिर आप T2 में अंतराल [निम्न, उच्च] के भीतर T1 से वैल के आधार पर CTE T2 मिलान पंक्तियों के साथ तालिका T1 में शामिल हों।पी>
इस चरण को लागू करने वाला कोड यहां दिया गया है:
चयन करें T1.keycol AS keycol1, T1.val AS val1, SUBSTRING(T1.othercols, 1, 1) AS othercols1, T2.keycol AS keycol2, T2.val AS val2, SUBSTRING(T2.othercols, 1, 1 ) as othercols2FROM dbo.T1 इनर जॉइन T2 ऑन T1.val BETWEEN T2.low और T2.high;सपोर्टिंग इंडेक्स के साथ राडेक के समाधान की योजना चित्र 7 में दिखाई गई है।
चित्र 7:अनुक्रमणिका के साथ Radek द्वारा समाधान की योजना
यहां इस योजना के प्रदर्शन आंकड़े दिए गए हैं:
रन टाइम:10.6, CPU:7.63, लॉजिकल रीड्स:3,193,125समर्थन सूचकांक के बिना राडेक के समाधान की योजना चित्र 8 में दिखाई गई है।
चित्र 8:बिना अनुक्रमणिका के राडेक द्वारा समाधान की योजना
यहां इस योजना के प्रदर्शन के आंकड़े दिए गए हैं:
रन टाइम:18.1, सीपीयू:27.4, लॉजिकल रीड्स:5,827,360यहां दोनों योजनाओं के प्रदर्शन में काफी अंतर है। चित्र 8 में योजना को पंक्ति संख्याओं की गणना से पहले एक प्रारंभिक प्रकार की आवश्यकता होती है, जो कि चित्र 7 की योजना में नहीं है। इससे भी महत्वपूर्ण बात यह है कि प्रत्येक अंतराल को T1 से संबंधित पंक्तियों के साथ मिलान करने के लिए, चित्र 8 में योजना अनिवार्य रूप से लापता इंडेक्स के विकल्प के रूप में एक इंडेक्स स्पूल बनाती है, जबकि चित्र 7 में योजना केवल मौजूदा इंडेक्स idx_val_key का उपयोग T1 पर करती है। सभी प्रदर्शन उपायों में बड़े अंतर का यही मुख्य कारण है। फिर भी, अनुक्रमणिका का समर्थन किए बिना प्रदर्शन उचित है।
राडेक के समाधान पर कामिल की भिन्नता
कामिल राडेक के समाधान पर भिन्नता लेकर आए। यहाँ संपूर्ण समाधान का कोड है:
T2_collapsed AS के साथ (कीकोल के रूप में चयन करें, वैल एएस वैल 2, ROW_NUMBER () ओवर (कुंजी द्वारा वैल ऑर्डर द्वारा विभाजन) डीबीओ से आरएन के रूप में। टी 2), रेंज एएस (प्रीवकी के रूप में कीकॉल 2 चुनें, वैल 2 एएस प्रचलित, लीड ( keycol2) ओवर (वैल 2 द्वारा ऑर्डर करें) एनएक्सटीकी के रूप में, लीड (वैल 2) ओवर (वैल 2 द्वारा ऑर्डर) टी 2_कोलैप्स्ड जहां आरएन =1) से एनएक्सटीवल के रूप में, बेस्टमैच एएस (चुनें T1.keycol AS keycol1, T1.val AS val1, सबस्ट्रिंग (T1) .othercols, 1, 1) अन्य cols1 के रूप में, मामला जब ABS(T1.val - prevval) <=ABS(T1.val - nxtval) तब prevkey ELSE nxtkey END AS keycol2, CASE जब ABS(T1.val - prevval) <=ABS (T1.val - nxtval) तब प्रचलित ELSE nxtval END AS val2 फ्रॉम रेंज इनर जॉइन dbo.T1 ऑन प्रीवल <=T1.val और T1.valयहाँ इस समाधान के बारे में कामिल का अपना विवरण है:
This solution is close to Radek's idea of checking against low and high range, although the ranges are based purely on actual T2 values. We get each row and the lead values only for each row in collapsed T2 (first step always gets rid of duplicate keys if found, by using row number or min(keycol) grouped by val on t2). The key concepts are how to check low and high range not to get duplicates – lower range inclusive, higher range exclusive, as well as handling the outliers (if present) by creating a separate set looking at the lowest and max values in T2 (UNION ALL bit). The check against the max value is done with inclusive range to complement the case of T1 vals being equal to the top T2 vals.
The plan for Kamil’s variation on Radek’s solution with supporting indexes is shown in Figure 9.
Figure 9:Plan for Kamil’s variation on Radek’s solution with indexes
Here are the performance stats for this plan:
run time:8.59, CPU:8.5, logical reads:3,206,849The plan for Kamil’s variation on Radek’s solution without supporting indexes is shown in Figure 10.
Figure 10:Plan for Kamil’s variation on Radek’s solution without indexes
Here are the performance stats for this plan:
run time:14, CPU:24.5, logical reads:5,870,596The plan in Figure 9 is serial and most of the calculations are done based on preordered data obtained from the supporting indexes. The plan in Figure 10 is parallel, plus there are a few sorts, and even an index spool activity. The performance measures of the latter plan are substantially higher than the former plan, but the performance in both cases is reasonable.
Solution 2 by Kamil Kosno – Using ranking, offset and aggregate window functions
Kamil came up with another original approach that is based on a combination of ranking, offset and aggregate window functions. यहाँ संपूर्ण समाधान का कोड है:
WITH A AS( SELECT 1 AS t, keycol,val, DENSE_RANK() OVER(ORDER BY val) AS rnk FROM dbo.T1 UNION ALL SELECT NULL, MIN(keycol), val, 0 FROM dbo.T2 GROUP BY val),B AS( SELECT t, keycol, val, CASE WHEN t =1 THEN DENSE_RANK() OVER (ORDER BY val, t) - rnk ELSE 0 END AS grp, LAG(CASE WHEN t IS NULL THEN val END) OVER(ORDER BY val, t) AS prev, LAG(CASE WHEN t IS NULL THEN keycol END) OVER(ORDER BY val, t) AS prevkey, LEAD(CASE WHEN t IS NULL THEN val END) OVER(ORDER BY val, t) AS nxt, LEAD(CASE WHEN t IS NULL THEN keycol END) OVER(ORDER BY val, t) AS nxtkey FROM A),C AS( SELECT keycol AS keycol1, val AS val1, MAX (prev) OVER(PARTITION BY grp ORDER BY prev DESC ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS mxprev, MAX (prevkey) OVER(PARTITION BY grp ORDER BY prev DESC ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS mxprevkey, MAX (nxt) OVER(PARTITION BY grp ORDER BY nxt ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) AS mxnxt, MAX (nxtkey) OVER(PARTITION BY grp ORDER BY nxt ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) AS mxnxtkey FROM B WHERE t =1),D AS( SELECT keycol1, val1, CASE WHEN ABS(val1 - mxprev) <=ISNULL(ABS(val1 - mxnxt), mxprev) THEN mxprevkey ELSE mxnxtkey END AS keycol2, CASE WHEN ABS(val1 - mxprev) <=ISNULL(ABS(val1 - mxnxt), mxprev) THEN mxprev ELSE mxnxt END AS val2 FROM C)SELECT D.keycol1, D.val1, D.keycol2, D.val2, SUBSTRING(T1.othercols, 1, 1) AS othercols1, SUBSTRING(T2.othercols, 1, 1) AS othercols2FROM D INNER JOIN dbo.T1 ON T1.keycol =D.keycol1 INNER JOIN dbo.T2 ON T2.keycol =D.keycol2;In Step 1 of the solution you unify the following result sets:
- Rows from T1 with a result column called t assigned with the constant 1 and column rnk representing dense rank values ordered by val
- Group rows from T2 by val and return val and min keycol for each group, with the result column t assigned with NULL and rnk with 0
इस चरण को लागू करने वाला कोड यहां दिया गया है:
SELECT 1 AS t, keycol,val, DENSE_RANK() OVER(ORDER BY val) AS rnkFROM dbo.T1UNION ALLSELECT NULL, MIN(keycol), val, 0FROM dbo.T2GROUP BY val;यह कोड निम्न आउटपुट उत्पन्न करता है:
t keycol val rnk----------- ----------- ----------- --------------------1 1 1 11 2 1 11 3 3 21 4 3 21 5 5 31 6 8 41 7 13 51 8 16 61 9 18 71 10 20 81 11 21 9NULL 1 2 0NULL 4 3 0NULL 3 7 0NULL 6 11 0NULL 8 13 0NULL 9 17 0NULL 10 19 0In Step 2 of the solution you define a CTE called A based on the query from Step 1. You query A and compute group identifiers (grp) for T1 values that fall between ranges of distinct T2 values. This is done using an islands technique where you subtract rnk (dense ranks for only T1 values) from rnk2 (dense ranks for unified T1 values and distinct T2 values).
Using the LAG and LEAD functions, you compute for each T1 row the prev/next val and keycol values from T2, if present, and NULL otherwise. These calculations return values for the first and last rows in each group, if present, but NULLs for intermediate rows in groups.
इस चरण को लागू करने वाला कोड यहां दिया गया है:
SELECT t, keycol, val, rnk, DENSE_RANK() OVER (ORDER BY val) AS rnk2, CASE WHEN t =1 THEN DENSE_RANK() OVER (ORDER BY val, t) - rnk ELSE 0 END AS grp, LAG(CASE WHEN t IS NULL THEN val END) OVER(ORDER BY val, t) AS prev, LAG(CASE WHEN t IS NULL THEN keycol END) OVER(ORDER BY val, t) AS prevkey, LEAD(CASE WHEN t IS NULL THEN val END) OVER(ORDER BY val, t) AS nxt, LEAD(CASE WHEN t IS NULL THEN keycol END) OVER(ORDER BY val, t) AS nxtkeyFROM A;यह कोड निम्न आउटपुट उत्पन्न करता है:
t keycol val rnk rnk2 grp prev prevkey nxt nxtkey----- ------ --- --- ---- --- ----- ------- ----- -------1 1 1 1 1 0 NULL NULL NULL NULL1 2 1 1 1 0 NULL NULL 2 1NULL 1 2 0 2 0 NULL NULL 3 4NULL 4 3 0 3 0 2 1 NULL NULL1 3 3 2 3 2 3 4 NULL NULL1 4 3 2 3 2 NULL NULL NULL NULL1 5 5 3 4 2 NULL NULL 7 3NULL 3 7 0 5 0 NULL NULL NULL NULL1 6 8 4 6 3 7 3 11 6NULL 6 11 0 7 0 NULL NULL 13 8NULL 8 13 0 8 0 11 6 NULL NULL1 7 13 5 8 5 13 8 NULL NULL1 8 16 6 9 5 NULL NULL 17 9NULL 9 17 0 10 0 NULL NULL NULL NULL1 9 18 7 11 6 17 9 19 10NULL 10 19 0 12 0 NULL NULL NULL NULL1 10 20 8 13 7 19 10 NULL NULL1 11 21 9 14 7 NULL NULL NULL NULLIn Step 3 you define a CTE called B based on the query from Step 2. You Query B and filter only original T1 rows (where t =1). In each group, you return the first row's prev and prevkey values, and last row's nxt and nxtkey values. Instead of just using a window partition clause, a window frame specification is used to define the minimal frame with the desired row to reduce I/O against the spool.
इस चरण को लागू करने वाला कोड यहां दिया गया है:
SELECT keycol AS keycol1, val AS val1, MAX (prev) OVER(PARTITION BY grp ORDER BY prev DESC ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS mxprev, MAX (prevkey) OVER(PARTITION BY grp ORDER BY prev DESC ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS mxprevkey, MAX (nxt) OVER(PARTITION BY grp ORDER BY nxt ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) AS mxnxt, MAX (nxtkey) OVER(PARTITION BY grp ORDER BY nxt ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) AS mxnxtkeyFROM BWHERE t =1;यह कोड निम्न आउटपुट उत्पन्न करता है:
keycol1 val1 mxprev mxprevkey mxnxt mxnxtkey----------- ----------- ----------- ----------- ----------- -----------2 1 NULL NULL 2 11 1 NULL NULL 2 15 5 3 4 7 33 3 3 4 7 34 3 3 4 7 36 8 7 3 11 68 16 13 8 17 97 13 13 8 17 99 18 17 9 19 1010 20 19 10 NULL NULL11 21 19 10 NULL NULLIn Step 4 you define a CTE called C based on the query from Step 3. You query C to compute keycol2 and val2 based on the closest match.
इस चरण को लागू करने वाला कोड यहां दिया गया है:
SELECT keycol1, val1, CASE WHEN ABS(val1 - mxprev) <=ISNULL(ABS(val1 - mxnxt), mxprev) THEN mxprevkey ELSE mxnxtkey END AS keycol2, CASE WHEN ABS(val1 - mxprev) <=ISNULL(ABS(val1 - mxnxt), mxprev) THEN mxprev ELSE mxnxt END AS val2FROM C;यह कोड निम्न आउटपुट उत्पन्न करता है:
keycol1 val1 keycol2 val2----------- ----------- ----------- -----------2 1 1 21 1 1 25 5 4 33 3 4 34 3 4 36 8 3 78 16 9 177 13 8 139 18 9 1710 20 10 1911 21 10 19In Step 5 you define a CTE called D based on the query from Step 4. You then join D with T1 and T2 to get the other needed columns.
इस चरण को लागू करने वाला कोड यहां दिया गया है:
SELECT D.keycol1, D.val1, D.keycol2, D.val2, SUBSTRING(T1.othercols, 1, 1) AS othercols1, SUBSTRING(T2.othercols, 1, 1) AS othercols2FROM D INNER JOIN dbo.T1 ON T1.keycol =D.keycol1 INNER JOIN dbo.T2 ON T2.keycol =D.keycol2;The plan for solution 2 by Kamil with supporting indexes is shown in Figure 11.
Figure 11:Plan for solution 2 by Kamil with indexes
Here are the performance stats for this plan:
run time:18.1, CPU:34.4, logical reads:56,736The plan for solution 2 by Kamil without supporting indexes is shown in Figure 12.
Figure 12:Plan for solution 2 by Kamil without indexes
Here are the performance stats for this plan:
run time:20.3, CPU:38.9, logical reads:59,012As you can see, the Plan in Figure 12 involves a couple of extra sorts compared to the plan in Figure 1 to order the data for the two queries in the CTE A—one to support the dense rank calculation and the other to support the grouped query. Other than that, the rest of the work is similar in both. In the grand scheme of things, there’s a bit of extra CPU work and consequentially time penalty, but still both queries perform fairly reasonably.
निष्कर्ष
This article concludes the series on the closest match challenge. It’s great to see the different creative ideas that Kamil, Alejandro and Radek came up with. Thanks to all of you for sending your solutions!