संख्या श्रृंखला जनरेटर चुनौती के समाधान के बारे में श्रृंखला में यह चौथा भाग है। अपने विचारों और टिप्पणियों को साझा करने के लिए एलन बर्स्टीन, जो ओबिश, एडम मचानिक, क्रिस्टोफर फोर्ड, जेफ मोडन, चार्ली, नोएमजीआर, कामिल कोस्नो, डेव मेसन, जॉन नेल्सन #2, एड वैगनर, माइकल बरबी और पॉल व्हाइट को बहुत-बहुत धन्यवाद।पी>
मुझे पॉल व्हाइट का काम पसंद है। मैं उसकी खोजों से चौंक जाता हूं, और सोचता हूं कि वह कैसे पता लगाता है कि वह क्या करता है। मुझे उनकी कुशल और वाक्पटु लेखन शैली भी पसंद है। अक्सर उनके लेख या पोस्ट पढ़ते समय, मैं अपना सिर हिलाता हूं और अपनी पत्नी लिलाच से कहता हूं कि जब मैं बड़ा हो जाऊंगा, तो मैं पॉल की तरह बनना चाहता हूं।
जब मैंने मूल रूप से चुनौती पोस्ट की थी, तो मैं गुप्त रूप से उम्मीद कर रहा था कि पॉल एक समाधान पोस्ट करेगा। मुझे पता था कि अगर उन्होंने ऐसा किया तो यह बहुत खास होगा। खैर, उसने किया, और यह आकर्षक है! इसका उत्कृष्ट प्रदर्शन है, और बहुत कुछ है जो आप इससे सीख सकते हैं। यह लेख पॉल के समाधान के लिए समर्पित है।
मैं अपना परीक्षण tempdb में करूँगा, जिससे I/O और समय के आँकड़े सक्षम होंगे:
नोकाउंट चालू करें; टेम्पर्ड का उपयोग करें; सांख्यिकी समय सेट करें, IO ON;
पहले के विचारों की सीमाएं
पहले के समाधानों का मूल्यांकन, अच्छा प्रदर्शन प्राप्त करने में महत्वपूर्ण कारकों में से एक बैच प्रोसेसिंग को नियोजित करने की क्षमता थी। लेकिन क्या हमने इसका यथासंभव अधिकतम दोहन किया है?
आइए पहले के दो समाधानों की योजनाओं की जांच करें जो बैच प्रोसेसिंग का उपयोग करते थे। भाग 1 में मैंने dbo.GetNumsAlanCharlieItzikBatch समारोह को कवर किया, जिसमें एलन, चार्ली और मेरे विचारों को मिला दिया गया था।
यहाँ फ़ंक्शन की परिभाषा है:
-- हेल्पर डमी टेबलड्रॉप टेबल अगर मौजूद है dbo.BatchMe;GO CREATE TABLE dbo.BatchMe(col1 INT NOT NULL, INDEX idx_cs CLUSTERED COLUMNSTORE);GO -- Function definitionCREATE or ALTER FUNCTION dbo.harlieItzikBa(@ कम ASCHLIItzikBach) 1, @high AS BIGINT) L0 AS के साथ टेबल रिटर्न लौटाता है (से 1 के रूप में चुनें (मूल्य (1), (1), (1), (1), (1), (1), (1), (1) ), (1),(1),(1),(1),(1),(1),(1),(1)) AS D(c) ), L1 AS ( L0 से 1 AS c चुनें एक क्रॉस जॉइन एल0 एएस बी के रूप में), एल 2 एएस (एल 1 से एक क्रॉस जॉइन एल 1 एएस बी के रूप में चुनें), एल 3 एएस (एक क्रॉस जॉइन एल 2 एएस बी के रूप में एल 2 से सी चुनें), अंक एएस (चयन ROW_NUMBER () ओवर (ऑर्डर बाय (सेलेक्ट न्यूल)) एएस राउनम फ्रॉम एल 3) सेलेक्ट टॉप(@high - @low + 1) Rownum AS rn, @high + 1 - rownum AS op, @low - 1 + rownum AS n FROM Nums बायां बाहरी dbo.BatchMe ON 1 =0 पर शामिल हों rownum द्वारा ऑर्डर करें;GO
यह समाधान 16 पंक्तियों के साथ एक बेस टेबल-वैल्यू कंस्ट्रक्टर को परिभाषित करता है, और क्रॉस जॉइन के साथ कैस्केडिंग सीटीई की एक श्रृंखला संभावित रूप से 4 बी तक पंक्तियों की संख्या को बढ़ाने के लिए मिलती है। समाधान ROW_NUMBER फ़ंक्शन का उपयोग Nums नामक CTE में संख्याओं का आधार अनुक्रम बनाने के लिए और वांछित संख्या श्रृंखला कार्डिनैलिटी को फ़िल्टर करने के लिए TOP फ़िल्टर का उपयोग करता है। बैच प्रोसेसिंग को सक्षम करने के लिए, समाधान Nums CTE और dbo.BatchMe नामक एक टेबल के बीच एक झूठी स्थिति के साथ एक डमी लेफ्ट जॉइन का उपयोग करता है, जिसमें एक कॉलमस्टोर इंडेक्स होता है।
चर असाइनमेंट तकनीक के साथ फ़ंक्शन का परीक्षण करने के लिए निम्न कोड का उपयोग करें:
घोषित @n के रूप में BIGINT; dbo से @n =n चुनें। GetNumsAlanCharlieItzikBatch(1, 100000000) विकल्प (MAXDOP 1);
वास्तविक . का प्लान एक्सप्लोरर चित्रण इस निष्पादन की योजना चित्र 1 में दिखाई गई है।
चित्र 1:dbo.GetNumsAlanCharlieItzikBatch फ़ंक्शन के लिए योजना
बैच मोड बनाम रो मोड प्रोसेसिंग का विश्लेषण करते समय, उच्च स्तर पर एक योजना को देखकर यह बताने में सक्षम होना काफी अच्छा है कि प्रत्येक ऑपरेटर किस प्रोसेसिंग मोड का उपयोग करता है। दरअसल, प्लान एक्सप्लोरर ऑपरेटर के निचले बाएं हिस्से में एक हल्का नीला बैच आंकड़ा दिखाता है, जब उसका वास्तविक निष्पादन मोड बैच होता है। जैसा कि आप चित्र 1 में देख सकते हैं, बैच मोड का उपयोग करने वाला एकमात्र ऑपरेटर विंडो एग्रीगेट ऑपरेटर है, जो पंक्ति संख्याओं की गणना करता है। पंक्ति मोड में अन्य ऑपरेटरों द्वारा अभी भी बहुत काम किया गया है।
यहाँ प्रदर्शन संख्याएँ हैं जो मुझे अपने परीक्षण में मिलीं:
CPU समय =10032 ms, बीता हुआ समय =10025 ms.तार्किक पढ़ता है 0
यह पहचानने के लिए कि किन ऑपरेटरों को निष्पादित करने में सबसे अधिक समय लगा, या तो SSMS में वास्तविक निष्पादन योजना विकल्प का उपयोग करें, या प्लान एक्सप्लोरर में वास्तविक योजना प्राप्त करें विकल्प का उपयोग करें। सुनिश्चित करें कि आपने पॉल का हालिया लेख एक्ज़ीक्यूशन प्लान ऑपरेटर टाइमिंग को समझना पढ़ा है। लेख बताता है कि सही संख्या प्राप्त करने के लिए रिपोर्ट किए गए ऑपरेटर निष्पादन समय को कैसे सामान्य किया जाए।
चित्र 1 की योजना में, अधिकांश समय सबसे बाईं ओर नेस्टेड लूप्स और शीर्ष ऑपरेटरों द्वारा बिताया जाता है, दोनों पंक्ति मोड में निष्पादित होते हैं। सीपीयू गहन संचालन के लिए बैच मोड की तुलना में पंक्ति मोड कम कुशल होने के अलावा, यह भी ध्यान रखें कि पंक्ति से बैच मोड में स्विच करने और वापस जाने पर अतिरिक्त टोल लगता है।
भाग 2 में मैंने एक और समाधान को कवर किया है जो बैच प्रोसेसिंग को नियोजित करता है, जिसे dbo.GetNumsJohn2DaveObbishAlanCharlieItzik2 फ़ंक्शन में लागू किया गया है। इस समाधान ने जॉन नंबर 2, डेव मेसन, जो ओबिश, एलन, चार्ली और मेरे विचारों को संयुक्त किया। पिछले समाधान और इस एक के बीच मुख्य अंतर यह है कि आधार इकाई के रूप में, पूर्व एक वर्चुअल टेबल-वैल्यू कंस्ट्रक्टर का उपयोग करता है और बाद वाला एक कॉलमस्टोर इंडेक्स के साथ एक वास्तविक तालिका का उपयोग करता है, जिससे आपको "मुफ्त में" बैच प्रोसेसिंग मिलती है। यहां वह कोड है जो तालिका बनाता है और इसे संपीड़ित करने के लिए 102,400 पंक्तियों के साथ INSERT कथन का उपयोग करके इसे पॉप्युलेट करता है:
ड्रॉप टेबल अगर मौजूद है dbo.NullBits102400;GO CREATE TABLE dbo.NullBits102400(b BIT NULL, INDEX cc_NullBits102400 CLUSTERED COLUMNSTORE विथ (DATA_COMPRESSION =COLUMNSTORE B ASNULL_ARCHIVE (LAST) के रूप में COLUMNSTORE B AS (L ASCHIVE) चुनें; मान(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),( 1),(1),(1),(1)) एएस डी(बी)), एल1 एएस (एल0 से एबी को क्रॉस जॉइन एल0 एएस बी के रूप में चुनें), नल (बी) एएस (एल 1 एएस ए से एबी चुनें) क्रॉस जॉइन एल 1 एएस बी क्रॉस जॉइन एल 1 एएस सी) डीबीओ में डालें। नलबिट्स102400 (टैबलॉक) के साथ (बी) नल से टॉप (102400) बी चुनें; जाओ
यहाँ फ़ंक्शन की परिभाषा है:
क्रिएट या अल्टर फंक्शन dbo.GetNumsJohn2DaveObbishAlanCharlieItzik2 (@low AS BIGINT =1, @high AS BIGINT) रिटर्न्स टेबल्स रिटर्न्स विद नंबर्स एएस (सेलेक्ट ROW_NUMBER () ओवर (ऑर्डर बाय (सेलेक्ट न्यूल)) एएस रोवनम के रूप में। क्रॉस जॉइन dbo.NullBits102400 AS B) सेलेक्ट टॉप(@high - @low + 1) rownum AS rn, @high + 1 - rownum AS op, @low - 1 + rownum AS n से नंबर ऑर्डर द्वारा rownum;GO
आधार तालिका के दो उदाहरणों के बीच एक एकल क्रॉस जुड़ना 4B पंक्तियों की वांछित क्षमता से अधिक उत्पादन करने के लिए पर्याप्त है। यहां फिर से, समाधान संख्याओं के आधार अनुक्रम को उत्पन्न करने के लिए ROW_NUMBER फ़ंक्शन का उपयोग करता है और वांछित संख्या श्रृंखला कार्डिनैलिटी को प्रतिबंधित करने के लिए TOP फ़िल्टर का उपयोग करता है।
चर असाइनमेंट तकनीक का उपयोग करके फ़ंक्शन का परीक्षण करने के लिए कोड यहां दिया गया है:
घोषित @n के रूप में BIGINT; dbo से @n =n चुनें। GetNumsJohn2DaveObbishAlanCharlieItzik2(1, 100000000) विकल्प (MAXDOP 1);
इस निष्पादन की योजना चित्र 2 में दिखाई गई है।
चित्र 2:dbo के लिए योजना। GetNumsJohn2DaveObbishAlanCharlieItzik2 फ़ंक्शन
ध्यान दें कि इस योजना में केवल दो ऑपरेटर बैच मोड का उपयोग करते हैं- तालिका के क्लस्टर्ड कॉलमस्टोर इंडेक्स का शीर्ष स्कैन, जिसका उपयोग नेस्टेड लूप्स के बाहरी इनपुट के रूप में किया जाता है, और विंडो एग्रीगेट ऑपरेटर, जिसका उपयोग आधार पंक्ति संख्याओं की गणना करने के लिए किया जाता है। ।
मुझे अपने परीक्षण के लिए निम्नलिखित प्रदर्शन संख्याएँ मिलीं:
CPU समय =9812 ms, बीता हुआ समय =9813 ms.तालिका 'NullBits102400'। स्कैन काउंट 2, लॉजिकल रीड्स 0, फिजिकल रीड्स 0, पेज सर्वर रीड्स 0, रीड-फॉरवर्ड रीड्स 0, पेज सर्वर रीड-फॉरवर्ड रीड्स 0, लोब लॉजिकल रीड्स 8, लोब फिजिकल रीड्स 0, लोब पेज सर्वर रीड्स 0, लोब रीड- आगे 0 पढ़ता है, लोब पृष्ठ सर्वर आगे पढ़ता है 0 पढ़ता है।
तालिका 'NullBits102400'। खंड 2 पढ़ता है, खंड 0 छोड़ दिया गया है।
फिर से, इस योजना के निष्पादन में अधिकांश समय सबसे बाएं नेस्टेड लूप्स और शीर्ष ऑपरेटरों द्वारा व्यतीत किया जाता है, जो पंक्ति मोड में निष्पादित होते हैं।
पॉल का समाधान
इससे पहले कि मैं पॉल का समाधान प्रस्तुत करूं, मैं उस विचार प्रक्रिया के बारे में अपने सिद्धांत से शुरू करूंगा जिससे वह गुजरा। यह वास्तव में एक महान सीखने का अभ्यास है, और मेरा सुझाव है कि आप समाधान को देखने से पहले इसे पढ़ लें। पॉल ने एक योजना के दुर्बल प्रभावों को पहचाना जो बैच और पंक्ति मोड दोनों को मिलाता है, और एक समाधान के साथ आने के लिए खुद को एक चुनौती देता है जो एक ऑल-बैच-मोड योजना प्राप्त करता है। सफल होने पर, इस तरह के समाधान के अच्छे प्रदर्शन की संभावना काफी अधिक है। यह देखना निश्चित रूप से दिलचस्प है कि क्या ऐसा लक्ष्य भी प्राप्य है, क्योंकि अभी भी कई ऑपरेटर हैं जो अभी तक बैच मोड का समर्थन नहीं करते हैं और कई कारक जो बैच प्रोसेसिंग को रोकते हैं। उदाहरण के लिए, लेखन के समय, बैच प्रोसेसिंग का समर्थन करने वाला एकमात्र जॉइन एल्गोरिथम हैश जॉइन एल्गोरिथम है। एक क्रॉस जॉइन नेस्टेड लूप एल्गोरिदम का उपयोग करके अनुकूलित किया गया है। इसके अलावा, शीर्ष ऑपरेटर वर्तमान में केवल पंक्ति मोड में लागू किया गया है। ये दो तत्व महत्वपूर्ण मूलभूत तत्व हैं जिनका उपयोग मैंने अब तक कई समाधानों के लिए योजनाओं में किया है, जिनमें उपरोक्त दो भी शामिल हैं।
यह मानते हुए कि आपने ऑल-बैच-मोड योजना के साथ एक समाधान बनाने की चुनौती को एक अच्छा प्रयास दिया है, आइए दूसरे अभ्यास पर आगे बढ़ें। मैं सबसे पहले पॉल के समाधान को उनकी इनलाइन टिप्पणियों के साथ प्रस्तुत करूंगा जैसा उन्होंने प्रदान किया था। मैं इसे अन्य समाधानों के साथ इसके प्रदर्शन की तुलना करने के लिए भी चलाऊंगा। मैंने उनके समाधान के पुनर्निर्माण और पुनर्निर्माण के द्वारा बहुत कुछ सीखा, एक समय में एक कदम, यह सुनिश्चित करने के लिए कि मैं ध्यान से समझ गया कि उसने अपनी प्रत्येक तकनीक का उपयोग क्यों किया। मेरा सुझाव है कि आप मेरी व्याख्याओं को पढ़ने के लिए आगे बढ़ने से पहले ऐसा ही करें।
यहां पॉल का समाधान है, जिसमें dbo.CS नामक एक सहायक कॉलमस्टोर तालिका और dbo.GetNums_SQLkiwi नामक एक फ़ंक्शन शामिल है:
-- हेल्पर कॉलमस्टोर टेबलड्रॉप टेबल अगर मौजूद है dbo.CS; - 64K पंक्तियाँ (क्रॉस में शामिल होने पर 4B पंक्तियों के लिए पर्याप्त) - कॉलम 1 हमेशा शून्य होता है - कॉलम 2 (1...65536) होता है - पूर्णांक के रूप में टाइप करें NOT NULL - (सब कुछ 64 बिट्स में सामान्यीकृत होता है कॉलमस्टोर/बैच मोड वैसे भी) n1 =ISNULL (कन्वर्ट (पूर्णांक, 0)), n2 =ISNULL (कन्वर्ट (पूर्णांक, N.rn), 0) डीबीओ में। @@ SPID) मास्टर.dbo.spt_values से SV1 क्रॉस के रूप में शामिल हों master.dbo.spt_values के रूप में RN ASC ऑफ़सेट द्वारा SV2 ऑर्डर के रूप में 0 पंक्तियाँ फ़ेच अगले 65536 पंक्तियाँ केवल) N के रूप में; - 65,536 पंक्तियों का सिंगल कंप्रेस्ड रोग्रुप CREATE COLUSTERED COLUMNSTORE INDEX CCI ON dbo.CS विद (MAXDOP =1); GO - फंक्शनक्रिएट या अल्टर फंक्शन dbo.GetNums_SQLkiwi(@low bigint =1, @high bigint) रिटर्न्स टेबल चुनें। .rn, n =@low - 1 + N.rn, op =@high + 1 - N.rn FROM ( सेलेक्ट - यदि आप अपने सभी प्रश्नों को पसंद करते हैं तो @@ SPID के बजाय @@ TRANCOUNT का उपयोग करें rn =ROW_NUMBER() ओवर (ऑर्डर बाय @@SPID ASC) dbo.CS से N1 के रूप में शामिल हों dbo.CS AS N2 में शामिल हों - बैच मोड हैश क्रॉस जॉइन - पूर्णांक नहीं डेटा प्रकार हैश जांच अवशिष्ट से बचें - यह हमेशा N2 पर 0 =0 होता है। n1 =N1.n1 जहां - नकारात्मक संख्याओं पर SQRT से बचने और सरलीकरण को सक्षम करने का प्रयास करें - एकल निरंतर स्कैन के लिए यदि @low> @high (शाब्दिक के साथ) - बैच मोड में कोई स्टार्ट-अप फ़िल्टर नहीं @high>=@low -- मोटे फिल्टर:-- . के प्रत्येक पक्ष को सीमित करें क्रॉस SQRT (पंक्तियों की लक्ष्य संख्या) में शामिल हो जाता है - IIF मापदंडों के साथ ऋणात्मक संख्याओं पर SQRT से बचता है और N1.n2 <=CONVERT (पूर्णांक, सीलिंग (SQRT (कन्वर्ट (फ्लोट, IIF (@high> =@low, @high)) - @low + 1, 0))))) और N2.n2 <=CONVERT (पूर्णांक, सीलिंग (SQRT (कन्वर्ट (फ्लोट, IIF (@high> =@low, @high - @low + 1, 0))) )))) एन जहां के रूप में - सटीक फिल्टर:- बैच मोड सीमित क्रॉस जॉइन को आवश्यक पंक्तियों की सटीक संख्या में फ़िल्टर करता है - ऑप्टिमाइज़र को एक पंक्ति-मोड पेश करने से बचाता है निम्नलिखित पंक्ति मोड के साथ शीर्ष गणना स्कैला @low - 2 + N.rn <@high;GO
यहाँ वह कोड है जिसका उपयोग मैंने चर असाइनमेंट तकनीक के साथ फ़ंक्शन का परीक्षण करने के लिए किया था:
घोषित @n के रूप में BIGINT; dbo से @n =n चुनें। GetNums_SQLkiwi(1, 100000000);
मुझे अपने परीक्षण के लिए चित्र 3 में दिखाया गया प्लान मिला है।
चित्र 3:dbo.GetNums_SQLkiwi फ़ंक्शन के लिए योजना
यह एक ऑल-बैच-मोड योजना है! यह काफी प्रभावशाली है।
मेरी मशीन पर इस परीक्षण के लिए मुझे जो प्रदर्शन संख्याएँ मिलीं, वे यहाँ हैं:
CPU समय =7812 ms, बीता हुआ समय =7876 ms.टेबल 'सीएस'। स्कैन काउंट 2, लॉजिकल रीड्स 0, फिजिकल रीड्स 0, पेज सर्वर रीड्स 0, रीड-फॉरवर्ड रीड्स 0, पेज सर्वर रीड-फॉरवर्ड रीड्स 0, लोब लॉजिकल रीड्स 44, लोब फिजिकल रीड्स 0, लोब पेज सर्वर रीड्स 0, लोब रीड- आगे 0 पढ़ता है, लोब पृष्ठ सर्वर आगे पढ़ता है 0 पढ़ता है।
टेबल 'सीएस'। खंड 2 पढ़ता है, खंड 0 छोड़ दिया गया है।
आइए यह भी सत्यापित करें कि यदि आपको n द्वारा क्रमित संख्याओं को वापस करने की आवश्यकता है, तो समाधान rn के संबंध में क्रम-संरक्षण है - कम से कम जब स्थिरांक को इनपुट के रूप में उपयोग करते हैं - और इस प्रकार योजना में स्पष्ट छँटाई से बचें। आदेश के साथ इसका परीक्षण करने के लिए कोड यहां दिया गया है:
घोषित @n के रूप में BIGINT; dbo से @n =n चुनें। GetNums_SQLkiwi(1, 100000000) n द्वारा ऑर्डर करें;
आपको चित्र 3 की तरह ही योजना मिलती है, और इसलिए समान प्रदर्शन संख्याएँ:
CPU समय =7765 ms, बीता हुआ समय =7822 ms.टेबल 'सीएस'। स्कैन काउंट 2, लॉजिकल रीड्स 0, फिजिकल रीड्स 0, पेज सर्वर रीड्स 0, रीड-फॉरवर्ड रीड्स 0, पेज सर्वर रीड-फॉरवर्ड रीड्स 0, लोब लॉजिकल रीड्स 44, लोब फिजिकल रीड्स 0, लोब पेज सर्वर रीड्स 0, लोब रीड- आगे 0 पढ़ता है, लोब पृष्ठ सर्वर आगे पढ़ता है 0 पढ़ता है।
टेबल 'सीएस'। खंड 2 पढ़ता है, खंड 0 छोड़ दिया गया है।
यह समाधान का एक महत्वपूर्ण पक्ष है।
हमारी परीक्षण पद्धति को बदलना
पॉल के समाधान का प्रदर्शन पिछले दो समाधानों की तुलना में बीता हुआ समय और सीपीयू समय दोनों में एक अच्छा सुधार है, लेकिन ऐसा नहीं लगता है कि सभी बैच-मोड योजना से अधिक नाटकीय सुधार की उम्मीद होगी। शायद हमें कुछ याद आ रहा है?
आइए चित्र 4 में दिखाए गए अनुसार SSMS में वास्तविक निष्पादन योजना को देखकर ऑपरेटर निष्पादन समय का प्रयास करें और उसका विश्लेषण करें।
चित्र 4:dbo.GetNums_SQLkiwi फ़ंक्शन के लिए ऑपरेटर निष्पादन समय
पॉल के लेख में ऑपरेटर निष्पादन समय का विश्लेषण करने के बारे में उन्होंने बताया कि बैच मोड ऑपरेटरों के साथ प्रत्येक अपने स्वयं के निष्पादन समय की रिपोर्ट करता है। यदि आप इस वास्तविक योजना में सभी ऑपरेटरों के निष्पादन समय को जोड़ते हैं, तो आपको 2.878 सेकंड मिलेंगे, फिर भी योजना को निष्पादित करने में 7.876 का समय लगा। निष्पादन समय के 5 सेकंड गायब प्रतीत होते हैं। इसका उत्तर वेरिएबल असाइनमेंट के साथ परीक्षण तकनीक में निहित है जिसका हम उपयोग कर रहे हैं। याद रखें कि हमने इस तकनीक का उपयोग सर्वर से कॉलर तक सभी पंक्तियों को भेजने की आवश्यकता को समाप्त करने और I/Os से बचने के लिए करने का निर्णय लिया है जो एक तालिका में परिणाम लिखने में शामिल होगा। यह एकदम सही विकल्प की तरह लग रहा था। हालांकि, इस योजना में परिवर्तनीय असाइनमेंट की वास्तविक लागत छिपी हुई है, और निश्चित रूप से, यह पंक्ति मोड में निष्पादित होती है। रहस्य सुलझाया।
जाहिर है दिन के अंत में एक अच्छा परीक्षण एक ऐसा परीक्षण है जो समाधान के आपके उत्पादन उपयोग को पर्याप्त रूप से दर्शाता है। यदि आप आमतौर पर किसी तालिका में डेटा लिखते हैं, तो आपको इसे दर्शाने के लिए अपने परीक्षण की आवश्यकता होगी। यदि आप कॉलर को परिणाम भेजते हैं, तो आपको यह दर्शाने के लिए अपने परीक्षण की आवश्यकता होगी। किसी भी दर पर, चर असाइनमेंट हमारे परीक्षण में निष्पादन समय के एक बड़े हिस्से का प्रतिनिधित्व करता है, और स्पष्ट रूप से फ़ंक्शन के विशिष्ट उत्पादन उपयोग का प्रतिनिधित्व करने की संभावना नहीं है। पॉल ने सुझाव दिया कि परिवर्तनशील असाइनमेंट के बजाय परीक्षण MAX जैसे साधारण समुच्चय को लौटाए गए नंबर कॉलम (n/rn/op) पर लागू कर सकता है। एक समग्र ऑपरेटर बैच प्रोसेसिंग का उपयोग कर सकता है, इसलिए योजना में इसके उपयोग के परिणामस्वरूप बैच से पंक्ति मोड में रूपांतरण शामिल नहीं होगा, और कुल रन टाइम में इसका योगदान काफी छोटा और ज्ञात होना चाहिए।
तो आइए इस लेख में शामिल सभी तीन समाधानों का पुन:परीक्षण करें। dbo.GetNumsAlanCharlieItzikBatch फ़ंक्शन का परीक्षण करने के लिए कोड यहां दिया गया है:
dbo से MAX(n) AS mx चुनें।GetNumsAlanCharlieItzikBatch(1, 100000000) OPTION(MAXDOP 1);
मुझे इस परीक्षण के लिए चित्र 5 में दिखाया गया प्लान मिला है।
चित्र 5:कुल मिलाकर dbo.GetNumsAlanCharlieItzikBatch फ़ंक्शन के लिए योजना
इस परीक्षण के लिए मुझे जो प्रदर्शन संख्याएँ मिली हैं, वे यहाँ हैं:
CPU समय =8469 ms, बीता हुआ समय =8733 ms.तार्किक पढ़ता है 0
ध्यान दें कि कुल तकनीक का उपयोग करके चर असाइनमेंट तकनीक का उपयोग करके रन टाइम 10.025 सेकंड से घटकर 8.733 हो गया है। यह निष्पादन समय के एक सेकंड से थोड़ा अधिक है जिसे हम यहां चर असाइनमेंट के लिए जिम्मेदार ठहरा सकते हैं।
dbo.GetNumsJohn2DaveObbishAlanCharlieItzik2 फ़ंक्शन का परीक्षण करने के लिए यहां कोड है:
dbo से MAX(n) AS mx चुनें।GetNumsJohn2DaveObbishAlanCharlieItzik2(1, 100000000) OPTION(MAXDOP 1);
मुझे इस परीक्षण के लिए चित्र 6 में दिखाया गया प्लान मिला है।
चित्र 6:dbo के लिए योजना। GetNumsJohn2DaveObbishAlanCharlieItzik2 कुल मिलाकर कार्य करता है
इस परीक्षण के लिए मुझे जो प्रदर्शन संख्याएँ मिली हैं, वे यहाँ हैं:
CPU समय =7031 ms, बीता हुआ समय =7053 ms.तालिका 'NullBits102400'। स्कैन काउंट 2, लॉजिकल रीड्स 0, फिजिकल रीड्स 0, पेज सर्वर रीड्स 0, रीड-फॉरवर्ड रीड्स 0, पेज सर्वर रीड-फॉरवर्ड रीड्स 0, लोब लॉजिकल रीड्स 8, लोब फिजिकल रीड्स 0, लोब पेज सर्वर रीड्स 0, लोब रीड- आगे 0 पढ़ता है, लोब पृष्ठ सर्वर आगे पढ़ता है 0 पढ़ता है।
तालिका 'NullBits102400'। खंड 2 पढ़ता है, खंड 0 छोड़ दिया गया है।
ध्यान दें कि कुल तकनीक का उपयोग करते हुए चर असाइनमेंट तकनीक का उपयोग करके रन टाइम 9.813 सेकंड से घटकर 7.053 हो गया है। यह निष्पादन समय के दो सेकंड से थोड़ा अधिक है जिसे हम यहां चर असाइनमेंट के लिए जिम्मेदार ठहरा सकते हैं।
और यहाँ पॉल के समाधान का परीक्षण करने के लिए कोड है:
dbo से MAX(n) AS mx चुनें।GetNums_SQLkiwi(1, 100000000) OPTION(MAXDOP 1);
मुझे इस परीक्षण के लिए चित्र 7 में दिखाया गया प्लान मिलता है।
चित्र 7:कुल मिलाकर dbo.GetNums_SQLkiwi फ़ंक्शन के लिए योजना
और अब बड़े पल के लिए। मुझे इस परीक्षण के लिए निम्नलिखित प्रदर्शन संख्याएँ मिली हैं:
CPU समय =3125 ms, बीता हुआ समय =3149 ms.टेबल 'सीएस'। स्कैन काउंट 2, लॉजिकल रीड्स 0, फिजिकल रीड्स 0, पेज सर्वर रीड्स 0, रीड-फॉरवर्ड रीड्स 0, पेज सर्वर रीड-फॉरवर्ड रीड्स 0, लोब लॉजिकल रीड्स 44, लोब फिजिकल रीड्स 0, लोब पेज सर्वर रीड्स 0, लोब रीड- आगे 0 पढ़ता है, लोब पृष्ठ सर्वर आगे पढ़ता है 0 पढ़ता है।
टेबल 'सीएस'। खंड 2 पढ़ता है, खंड 0 छोड़ दिया गया है।
रन टाइम 7.822 सेकेंड से घटकर 3.149 सेकेंड हो गया! आइए एसएसएमएस में वास्तविक योजना में ऑपरेटर निष्पादन समय की जांच करें, जैसा कि चित्र 8 में दिखाया गया है।
चित्र 8:dbo के लिए संचालिका निष्पादन समय। कुल मिलाकर GetNums कार्य करता है
अब यदि आप अलग-अलग ऑपरेटरों के निष्पादन समय जमा करते हैं तो आपको कुल योजना निष्पादन समय के समान संख्या प्राप्त होगी।
चित्र 9 में चर असाइनमेंट और समग्र परीक्षण तकनीकों दोनों का उपयोग करते हुए तीन समाधानों के बीच बीता हुआ समय के संदर्भ में एक प्रदर्शन तुलना है।
चित्र 9:प्रदर्शन तुलना
पॉल का समाधान एक स्पष्ट विजेता है, और समग्र परीक्षण तकनीक का उपयोग करते समय यह विशेष रूप से स्पष्ट है। क्या शानदार कारनामा है!
पॉल के समाधान का पुनर्निर्माण और पुनर्निर्माण
पॉल के समाधान को फिर से बनाना और फिर से बनाना एक महान अभ्यास है, और ऐसा करते समय आपको बहुत कुछ सीखने को मिलता है। जैसा कि पहले सुझाव दिया गया था, मेरा सुझाव है कि पढ़ना जारी रखने से पहले आप स्वयं इस प्रक्रिया से गुजरें।
पहली पसंद जो आपको करने की आवश्यकता है वह वह तकनीक है जिसका उपयोग आप 4B की पंक्तियों की वांछित संभावित संख्या उत्पन्न करने के लिए करेंगे। पॉल ने एक कॉलमस्टोर टेबल का उपयोग करना चुना और इसे आवश्यक संख्या के वर्गमूल के रूप में कई पंक्तियों के साथ पॉप्युलेट किया, जिसका अर्थ है 65,536 पंक्तियाँ, ताकि एक एकल क्रॉस जॉइन के साथ, आपको आवश्यक संख्या मिल जाए। शायद आप सोच रहे हैं कि 102,400 से कम पंक्तियों के साथ आपको एक संपीड़ित पंक्ति समूह नहीं मिलेगा, लेकिन यह तब लागू होता है जब आप तालिका को INSERT कथन के साथ पॉप्युलेट करते हैं जैसे हमने तालिका dbo.NullBits102400 के साथ किया था। यह तब लागू नहीं होता जब आप किसी पूर्व-पॉप्युलेटेड टेबल पर कॉलमस्टोर इंडेक्स बनाते हैं। इसलिए पॉल ने 65,536 पंक्तियों के साथ एक पंक्ति-भंडार-आधारित हीप के रूप में तालिका बनाने और पॉप्युलेट करने के लिए एक सेलेक्ट INTO स्टेटमेंट का उपयोग किया, और फिर एक क्लस्टर्ड कॉलमस्टोर इंडेक्स बनाया, जिसके परिणामस्वरूप एक संपीड़ित पंक्ति समूह बना।
अगली चुनौती यह पता लगाना है कि बैच मोड ऑपरेटर के साथ संसाधित होने के लिए क्रॉस जॉइन कैसे प्राप्त करें। इसके लिए आपको हैश होने के लिए जॉइन एल्गोरिथम की आवश्यकता है। याद रखें कि एक क्रॉस जॉइन नेस्टेड लूप एल्गोरिदम का उपयोग करके अनुकूलित किया गया है। आपको किसी तरह से ऑप्टिमाइज़र को यह सोचने के लिए चकमा देना होगा कि आप एक आंतरिक इक्विजॉइन का उपयोग कर रहे हैं (हैश के लिए कम से कम एक समानता-आधारित विधेय की आवश्यकता है), लेकिन अभ्यास में क्रॉस जॉइन प्राप्त करें।
एक स्पष्ट पहला प्रयास एक कृत्रिम जुड़ाव के साथ एक आंतरिक जुड़ाव का उपयोग करना है जो हमेशा सत्य होता है, जैसे:
चुनें *dbo.CS से N1 इनर हैश के रूप में शामिल हों dbo.CS के रूप में N2 0 =0 पर;
लेकिन यह कोड निम्न त्रुटि के साथ विफल हो जाता है:
Msg 8622, Level 16, State 1, Line 246क्वेरी प्रोसेसर इस क्वेरी में परिभाषित संकेतों के कारण क्वेरी प्लान नहीं बना सका। बिना किसी संकेत के और SET FORCEPLAN का उपयोग किए बिना क्वेरी को फिर से सबमिट करें।
SQL सर्वर का ऑप्टिमाइज़र यह मानता है कि यह एक कृत्रिम इनर जॉइन प्रेडिकेट है, एक क्रॉस जॉइन के लिए इनर जॉइन को सरल करता है, और यह कहते हुए एक त्रुटि उत्पन्न करता है कि यह हैश जॉइन एल्गोरिथम को बाध्य करने के लिए संकेत को समायोजित नहीं कर सकता है।
इसे हल करने के लिए, पॉल ने अपनी dbo.CS तालिका में n1 नामक एक INT NOT NULL कॉलम (इस विनिर्देश पर शीघ्र ही अधिक क्यों) बनाया, और इसे सभी पंक्तियों में 0 के साथ पॉप्युलेट किया। इसके बाद उन्होंने हैश जॉइन एल्गोरिथम के लिए न्यूनतम आवश्यकताओं का अनुपालन करते हुए, सभी मैच मूल्यांकनों में प्रभावी ढंग से प्रस्ताव 0 =0 प्राप्त करने के लिए जॉइन विधेय N2.n1 =N1.n1 का उपयोग किया।
यह काम करता है और एक ऑल-बैच-मोड प्लान तैयार करता है:
चुनें *dbo.CS से N1 इनर हैश के रूप में शामिल हों dbo.CS N2 पर N2.n1 =N1.n1;
n1 को INT NOT NULL के रूप में परिभाषित करने के कारण के लिए; एनयूएलएल को क्यों अस्वीकार करें, और बिगिनट का उपयोग क्यों न करें? इन विकल्पों का कारण हैश जांच अवशिष्ट (एक अतिरिक्त फ़िल्टर जो हैश जॉइन ऑपरेटर द्वारा मूल जॉइन विधेय से परे लागू किया जाता है) से बचने के लिए है, जिसके परिणामस्वरूप अतिरिक्त अनावश्यक लागत हो सकती है। विवरण के लिए पॉल का लेख जॉइन परफॉर्मेंस, इंप्लिक्ट कन्वर्सेशन और अवशिष्ट देखें। यहाँ लेख का वह हिस्सा है जो हमारे लिए प्रासंगिक है:
"यदि जॉइन टिनिंट, स्मॉलिंट, या पूर्णांक के रूप में टाइप किए गए एकल कॉलम पर है और यदि दोनों कॉलम न्यूल होने के लिए बाध्य हैं, तो हैश फ़ंक्शन 'परफेक्ट' है - जिसका अर्थ हैश टकराव का कोई मौका नहीं है, और क्वेरी प्रोसेसर यह सुनिश्चित करने के लिए कि वे वास्तव में मेल खाते हैं, मानों को दोबारा जांचना आवश्यक नहीं है।ध्यान दें कि यह अनुकूलन बिगिन्ट कॉलम पर लागू नहीं होता है।"
इस पहलू की जांच करने के लिए, आइए dbo.CS2 नामक एक और तालिका बनाएं, जिसमें एक अशक्त n1 कॉलम हो:
ड्रॉप टेबल अगर मौजूद है dbo.CS2; चुनें * dbo.CS2 से dbo.CS में; ALTER TABLE dbo.CS2 ALTER COLUMN n1 INT NULL; dbo.CS2with (MAXDOP =1);पर क्लस्टर किए गए कॉलमस्टोर इंडेक्स सीसीआई बनाएं
आइए पहले dbo.CS (जहाँ n1 को INT NOT NULL के रूप में परिभाषित किया गया है) के खिलाफ एक क्वेरी का परीक्षण करें, rn नामक कॉलम में 4B आधार पंक्ति संख्याएँ उत्पन्न करें, और MAX एग्रीगेट को कॉलम पर लागू करें:
सेलेक्ट एमएक्स =मैक्स (एन.आरएन) से (सेलेक्ट आरएन =ROW_NUMBER () ओवर (ऑर्डर @@ ट्रांकाउंट एएससी द्वारा) डीबीओ से। एएस एन;
हम इस क्वेरी के लिए योजना की तुलना dbo.CS2 (जहां n1 को INT NULL के रूप में परिभाषित किया गया है) के समान क्वेरी के लिए योजना के साथ करेंगे:
सेलेक्ट एमएक्स =मैक्स (एन.आरएन) से (सेलेक्ट आरएन =ROW_NUMBER () ओवर (ऑर्डर @@ ट्रांकाउंट एएससी द्वारा) डीबीओ से। एएस एन;
दोनों प्रश्नों की योजना चित्र 10 में दिखाई गई है।
चित्र 10:NOT NULL बनाम NULL जॉइन की के लिए योजना तुलना
आप दूसरी योजना में लागू की गई अतिरिक्त जांच को स्पष्ट रूप से देख सकते हैं लेकिन पहली नहीं।
मेरी मशीन पर dbo.CS के विरुद्ध क्वेरी 91 सेकंड में पूरी हुई, और dbo.CS2 के विरुद्ध क्वेरी 92 सेकंड में पूरी हुई। पॉल के लेख में उन्होंने इस्तेमाल किए गए उदाहरण के लिए NOT NULL मामले के पक्ष में 11% अंतर की रिपोर्ट की।
BTW, आपमें से जिन लोगों ने गहरी नज़र रखी है, उन्होंने ROW_NUMBER फ़ंक्शन के ऑर्डरिंग विनिर्देश के रूप में ORDER BY @@ TRANCOUNT के उपयोग पर ध्यान दिया होगा। यदि आप उनके समाधान में पॉल की इनलाइन टिप्पणियों को ध्यान से पढ़ते हैं, तो उन्होंने उल्लेख किया है कि @@ TRANCOUNT फ़ंक्शन का उपयोग समानांतरवाद अवरोधक है, जबकि @@ SPID का उपयोग नहीं है। इसलिए जब आप किसी सीरियल प्लान को ज़बरदस्ती करना चाहते हैं और जब आप ऐसा नहीं करते हैं तो @@ SPID को ऑर्डरिंग विनिर्देश में रन टाइम स्थिरांक के रूप में @@TRANCOUNT का उपयोग कर सकते हैं।
जैसा कि उल्लेख किया गया है, यह मेरी मशीन पर चलने के लिए dbo.CS 91 सेकंड के खिलाफ क्वेरी लेता है। इस बिंदु पर सच्चे क्रॉस जॉइन के साथ समान कोड का परीक्षण करना दिलचस्प हो सकता है, जिससे ऑप्टिमाइज़र को पंक्ति-मोड नेस्टेड लूप्स एल्गोरिथम का उपयोग करने देता है:
सेलेक्ट एमएक्स =मैक्स (एन.आरएन)फ्रॉम (सेलेक्ट आरएन =ROW_NUMBER() ओवर (ऑर्डर बाय @@ ट्रैनकाउंट एएससी) डीबीओ से।मेरी मशीन पर इस कोड को पूरा करने में 104 सेकंड का समय लगा। इसलिए हमें बैच-मोड हैश जॉइन के साथ एक बड़ा प्रदर्शन सुधार मिलता है।
हमारी अगली समस्या यह है कि पंक्तियों की वांछित संख्या को फ़िल्टर करने के लिए TOP का उपयोग करते समय, आपको एक पंक्ति-मोड शीर्ष ऑपरेटर के साथ एक योजना मिलती है। यहाँ एक टॉप फ़िल्टर के साथ dbo.GetNums_SQLkiwi फ़ंक्शन को लागू करने का प्रयास किया गया है:
क्रिएट या अल्टर फंक्शन dbo.GetNums_SQLkiwi (@low bigint =1, @high bigint) रिटर्न टेबल ASRETURN SELECT N.rn, n =@low - 1 + N.rn, op =@high + 1 - N.rn FROM (सेलेक्ट टॉप (10000000-1 + 1) rn =ROW_NUMBER () ओवर (ऑर्डर @@SPID ASC द्वारा) N1 के रूप में dbo.CS से N2.n1 पर dbo.CS के रूप में शामिल हों। एन;जाओआइए फ़ंक्शन का परीक्षण करें:
dbo से MAX(n) चुनें।GetNums_SQLkiwi(1, 100000000);मुझे इस परीक्षण के लिए चित्र 11 में दिखाया गया प्लान मिला है।
चित्र 11:टॉप फिल्टर के साथ योजना
ध्यान दें कि योजना में केवल शीर्ष ऑपरेटर ही पंक्ति-मोड प्रसंस्करण का उपयोग करता है।
मुझे इस निष्पादन के लिए निम्नलिखित समय के आँकड़े मिले:
CPU समय =6078 ms, बीता हुआ समय =6071 ms.इस प्लान में रन टाइम का सबसे बड़ा हिस्सा रो-मोड टॉप ऑपरेटर द्वारा खर्च किया जाता है और यह तथ्य कि प्लान को बैच-टू-पंक्ति-मोड रूपांतरण और वापस जाने की आवश्यकता है।
हमारी चुनौती पंक्ति-मोड TOP के बैच-मोड फ़िल्टरिंग विकल्प का पता लगाना है। WHERE क्लॉज के साथ लागू किए गए प्रेडिकेट आधारित फिल्टर संभावित रूप से बैच प्रोसेसिंग के साथ नियंत्रित किए जा सकते हैं।
पॉल का दृष्टिकोण एक दूसरे आईएनटी-टाइप किए गए कॉलम को पेश करना था (इनलाइन टिप्पणी देखें "कॉलमस्टोर/बैच मोड में वैसे भी सब कुछ 64-बिट के लिए सामान्यीकृत है" ) n2 को टेबल dbo.CS पर बुलाया जाता है, और इसे पूर्णांक अनुक्रम 1 से 65,536 तक पॉप्युलेट करता है। समाधान के कोड में उन्होंने दो विधेय-आधारित फ़िल्टर का उपयोग किया। एक आंतरिक क्वेरी में एक मोटे फ़िल्टर है जिसमें शामिल होने के दोनों किनारों से कॉलम n2 शामिल है। यह मोटे फिल्टर कुछ झूठी सकारात्मक परिणाम दे सकते हैं। इस तरह के फ़िल्टर का पहला सरल प्रयास यहां दिया गया है:
WHERE -- मोटे फ़िल्टर:N1.n2 <=CONVERT(पूर्णांक, सीलिंग(SQRT(CONVERT(float, @high - @low + 1))) और N2.n2 <=CONVERT(पूर्णांक, सीलिंग(SQRT) (CONVERT(float, @high - @low + 1))))इनपुट 1 और 100,000,000 @low और @high के रूप में, आपको कोई झूठी सकारात्मकता नहीं मिलती है। लेकिन 1 और 100,000,001 के साथ प्रयास करें, और आपको कुछ मिलता है। आपको 100,000,001 के बजाय 100,020,001 संख्याओं का एक क्रम मिलेगा।
झूठी सकारात्मकता को खत्म करने के लिए, पॉल ने बाहरी क्वेरी में कॉलम rn को शामिल करते हुए एक दूसरा, सटीक, फ़िल्टर जोड़ा। इस तरह के सटीक फ़िल्टर का पहला सरल प्रयास यहां दिया गया है:
WHERE -- सटीक फ़िल्टर:N.rn <@high - @low + 2आइए टॉप के बजाय उपरोक्त विधेय-आधारित फ़िल्टर का उपयोग करने के लिए फ़ंक्शन की परिभाषा को संशोधित करें, 1:
लेंक्रिएट या अल्टर फंक्शन dbo.GetNums_SQLkiwi (@low bigint =1, @high bigint) रिटर्न टेबल ASRETURN SELECT N.rn, n =@low - 1 + N.rn, op =@high + 1 - N.rn से (सेलेक्ट rn =ROW_NUMBER() ओवर (ऑर्डर @@ ट्रैनकाउंट ASC द्वारा) dbo.CS से N1 के रूप में dbo.CS के रूप में N2 पर N2.n1 =N1.n1 पर शामिल हों - मोटे फ़िल्टर:N1.n2 <=CONVERT( पूर्णांक, CEILING(SQRT(CONVERT(float, @high - @low + 1)))) और N2.n2 <=CONVERT(पूर्णांक, सीलिंग(SQRT(CONVERT(float, @high - @low + 1))))) ) जैसे एन जहां - सटीक फ़िल्टर:एन.आरएन <@high - @low + 2;GOआइए फ़ंक्शन का परीक्षण करें:
dbo से MAX(n) चुनें।GetNums_SQLkiwi(1, 100000000);मुझे इस परीक्षण के लिए चित्र 12 में दिखाया गया प्लान मिला है।
चित्र 12:WHERE फ़िल्टर के साथ योजना बनाएं, 1 लें
काश, कुछ स्पष्ट रूप से गलत हो जाता। SQL सर्वर ने हमारे विधेय-आधारित फ़िल्टर को rn कॉलम को शामिल करते हुए एक TOP-आधारित फ़िल्टर में परिवर्तित कर दिया, और इसे एक शीर्ष ऑपरेटर के साथ अनुकूलित किया - जो कि ठीक उसी तरह से है जिससे हमने बचने की कोशिश की थी। चोट के अपमान को जोड़ने के लिए, ऑप्टिमाइज़र ने जुड़ने के लिए नेस्टेड लूप एल्गोरिथम का उपयोग करने का भी निर्णय लिया।
इस कोड को मेरी मशीन पर समाप्त होने में 18.8 सेकंड का समय लगा। अच्छा नहीं लगता।
नेस्टेड लूप में शामिल होने के संबंध में, यह कुछ ऐसा है जिसे हम आसानी से आंतरिक क्वेरी में शामिल होने के संकेत का उपयोग कर सकते हैं। केवल प्रदर्शन प्रभाव को देखने के लिए, परीक्षण क्वेरी में ही उपयोग किए गए ज़बरदस्त हैश जॉइन क्वेरी संकेत के साथ एक परीक्षण है:
dbo से MAX(n) चुनें।GetNums_SQLkiwi(1, 100000000) OPTION(HASH JOIN);रन टाइम 13.2 सेकंड तक कम हो जाता है।
हमारे पास अभी भी WHERE फ़िल्टर को rn के विरुद्ध एक TOP फ़िल्टर में बदलने की समस्या है। आइए समझने की कोशिश करते हैं कि यह कैसे हुआ। हमने बाहरी क्वेरी में निम्न फ़िल्टर का उपयोग किया है:
जहां N.rn <@high - @low + 2याद रखें कि rn एक अप्रबंधित ROW_NUMBER-आधारित व्यंजक का प्रतिनिधित्व करता है। इस तरह के एक गैर-मैनिपुलेटेड एक्सप्रेशन के आधार पर एक फ़िल्टर को अक्सर एक शीर्ष ऑपरेटर के साथ अनुकूलित किया जाता है, जो हमारे लिए रो-मोड प्रोसेसिंग के उपयोग के कारण बुरी खबर है।
पॉल का समाधान एक समान विधेय का उपयोग करना था, लेकिन वह जो rn में हेरफेर लागू करता है, जैसे:
WHERE @low - 2 + N.rn <@highROW_NUMBER-आधारित व्यंजक में हेरफेर जोड़ने वाले व्यंजक को फ़िल्टर करने से विधेय-आधारित फ़िल्टर को TOP-आधारित फ़िल्टर में बदलने से रोकता है। यह शानदार है!
आइए उपरोक्त WHERE विधेय का उपयोग करने के लिए फ़ंक्शन की परिभाषा को संशोधित करें, 2 लें:
क्रिएट या अल्टर फंक्शन dbo.GetNums_SQLkiwi (@low bigint =1, @high bigint) रिटर्न टेबल ASRETURN SELECT N.rn, n =@low - 1 + N.rn, op =@high + 1 - N.rn से (सेलेक्ट rn =ROW_NUMBER() ओवर (ऑर्डर @@ ट्रैनकाउंट ASC द्वारा) dbo.CS से N1 के रूप में dbo.CS के रूप में N2 पर N2.n1 =N1.n1 पर शामिल हों - मोटे फ़िल्टर:N1.n2 <=CONVERT( पूर्णांक, CEILING(SQRT(CONVERT(float, @high - @low + 1)))) और N2.n2 <=CONVERT(पूर्णांक, सीलिंग(SQRT(CONVERT(float, @high - @low + 1))))) ) यथा एन जहां -- सटीक फ़िल्टर:@low - 2 + N.rn <@high;GOबिना किसी विशेष संकेत या किसी भी चीज़ के फ़ंक्शन को फिर से जांचें:
dbo से MAX(n) चुनें।GetNums_SQLkiwi(1, 100000000);यह स्वाभाविक रूप से एक हैश जॉइन एल्गोरिथ्म और कोई शीर्ष ऑपरेटर के साथ एक ऑल-बैच-मोड योजना प्राप्त करता है, जिसके परिणामस्वरूप 3.177 सेकंड का निष्पादन समय होता है। अच्छा लग रहा है।
अगला कदम यह जांचना है कि समाधान खराब इनपुट को अच्छी तरह से संभालता है या नहीं। आइए इसे एक नकारात्मक डेल्टा के साथ आज़माएं:
dbo से MAX(n) चुनें।GetNums_SQLkiwi(100000000, 1);यह निष्पादन निम्न त्रुटि के साथ विफल हो जाता है।
Msg 3623, Level 16, State 1, Line 436
एक अमान्य फ़्लोटिंग पॉइंट ऑपरेशन हुआ।
विफलता एक ऋणात्मक संख्या के वर्गमूल को लागू करने के प्रयास के कारण है।पॉल के समाधान में दो अतिरिक्त शामिल थे। एक निम्नलिखित विधेय को आंतरिक क्वेरी के WHERE क्लॉज में जोड़ना है:
@high>=@lowयह फ़िल्टर शुरुआत में जितना दिखता है, उससे कहीं अधिक करता है। यदि आपने पॉल की इनलाइन टिप्पणियों को ध्यान से पढ़ा है, तो आपको यह भाग मिल सकता है:
"नकारात्मक संख्याओं पर SQRT से बचने की कोशिश करें और यदि @low> @high (शाब्दिक के साथ) एकल निरंतर स्कैन के लिए सरलीकरण को सक्षम करें। बैच मोड में कोई स्टार्ट-अप फ़िल्टर नहीं है।"यहां दिलचस्प हिस्सा इनपुट के रूप में स्थिरांक के साथ निरंतर स्कैन का उपयोग करने की क्षमता है। मैं जल्द ही इस पर पहुंच जाऊंगा।
अन्य जोड़ IIF फ़ंक्शन को SQRT फ़ंक्शन के इनपुट एक्सप्रेशन में लागू करना है। यह हमारे नंबर फ़ंक्शन के इनपुट के रूप में गैर-स्थिरांक का उपयोग करते समय नकारात्मक इनपुट से बचने के लिए किया जाता है, और यदि ऑप्टिमाइज़र विधेय @high>=@low से पहले SQRT को शामिल करने वाले विधेय को संभालने का निर्णय लेता है।
उदाहरण के लिए, IIF जोड़ने से पहले, N1.n2 को शामिल करने वाला विधेय इस तरह दिखता था:
N1.n2 <=CONVERT(integer, CEILING(SQRT(CONVERT(float, @high - @low + 1))))After adding IIF, it looks like this:
N1.n2 <=CONVERT(integer, CEILING(SQRT(CONVERT(float, IIF(@high>=@low, @high - @low + 1, 0)))))With these two additions, we’re now basically ready for the final definition of the dbo.GetNums_SQLkiwi function:
CREATE OR ALTER FUNCTION dbo.GetNums_SQLkiwi( @low bigint =1, @high bigint)RETURNS table ASRETURN SELECT N.rn, n =@low - 1 + N.rn, op =@high + 1 - N.rn FROM ( SELECT -- Use @@TRANCOUNT instead of @@SPID if you like all your queries serial rn =ROW_NUMBER() OVER (ORDER BY @@SPID ASC) FROM dbo.CS AS N1 JOIN dbo.CS AS N2 -- Batch mode hash cross join -- Integer not null data type avoid hash probe residual -- This is always 0 =0 ON N2.n1 =N1.n1 WHERE -- Try to avoid SQRT on negative numbers and enable simplification -- to single constant scan if @low> @high (with literals) -- No start-up filters in batch mode @high>=@low -- Coarse filter:-- Limit each side of the cross join to SQRT(target number of rows) -- IIF avoids SQRT on negative numbers with parameters AND N1.n2 <=CONVERT(integer, CEILING(SQRT(CONVERT(float, IIF(@high>=@low, @high - @low + 1, 0))))) AND N2.n2 <=CONVERT(integer, CEILING(SQRT(CONVERT(float, IIF(@high>=@low, @high - @low + 1, 0))))) ) AS N WHERE -- Precise filter:-- Batch mode filter the limited cross join to the exact number of rows needed -- Avoids the optimizer introducing a row-mode Top with following row mode compute scalar @low - 2 + N.rn <@high;GONow back to the comment about the constant scan. When using constants that result in a negative range as inputs to the function, e.g., 100,000,000 and 1 as @low and @high, after parameter embedding the WHERE filter of the inner query looks like this:
WHERE 1>=100000000 AND ...The whole plan can then be simplified to a single Constant Scan operator. Try it:
SELECT MAX(n) FROM dbo.GetNums_SQLkiwi(100000000, 1);The plan for this execution is shown in Figure 13.
Figure 13:Plan with constant scan
Unsurprisingly, I got the following performance numbers for this execution:
CPU time =0 ms, elapsed time =0 ms.logical reads 0
When passing a negative range with nonconstants as inputs, the use of the IIF function will prevent any attempt to compute a square root of a negative input.
Now let’s test the function with a valid input range:
SELECT MAX(n) FROM dbo.GetNums_SQLkiwi(1, 100000000);You get the all-batch-mode plan shown in Figure 14.
Figure 14:Plan for dbo.GetNums_SQLkiwi function
This is the same plan you saw earlier in Figure 7.
I got the following time statistics for this execution:
CPU time =3000 ms, elapsed time =3111 ms.Ladies and gentlemen, I think we have a winner! :)
निष्कर्ष
I’ll have what Paul’s having.
Are we done yet, or is this series going to last forever?
No and no.