Database
 sql >> डेटाबेस >  >> RDS >> Database

चुनौती चालू है! सबसे तेज संख्या श्रृंखला जनरेटर बनाने के लिए सामुदायिक कॉल

टेबल एक्सप्रेशन पर मेरी श्रृंखला के भाग 5 में मैंने सीटीई, एक टेबल वैल्यू कंस्ट्रक्टर और क्रॉस जॉइन का उपयोग करके संख्याओं की एक श्रृंखला उत्पन्न करने के लिए निम्नलिखित समाधान प्रदान किया:

DECLARE @low AS BIGINT = 1001, @high AS BIGINT = 1010;
 
WITH
  L0 AS ( SELECT 1 AS c FROM (VALUES(1),(1)) AS D(c) ),
  L1 AS ( SELECT 1 AS c FROM L0 AS A CROSS JOIN L0 AS B ),
  L2 AS ( SELECT 1 AS c FROM L1 AS A CROSS JOIN L1 AS B ),
  L3 AS ( SELECT 1 AS c FROM L2 AS A CROSS JOIN L2 AS B ),
  L4 AS ( SELECT 1 AS c FROM L3 AS A CROSS JOIN L3 AS B ),
  L5 AS ( SELECT 1 AS c FROM L4 AS A CROSS JOIN L4 AS B ),
  Nums AS ( SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS rownum
            FROM L5 )
SELECT TOP(@high - @low + 1) @low + rownum - 1 AS n
FROM Nums
ORDER BY rownum;

ऐसे उपकरण के लिए कई व्यावहारिक उपयोग के मामले हैं, जिसमें दिनांक और समय मानों की एक श्रृंखला उत्पन्न करना, नमूना डेटा बनाना, और बहुत कुछ शामिल है। सामान्य आवश्यकता को स्वीकार करते हुए, कुछ प्लेटफ़ॉर्म एक अंतर्निहित टूल प्रदान करते हैं, जैसे कि PostgreSQL का Generate_series फ़ंक्शन। लेखन के समय, टी-एसक्यूएल इस तरह के एक अंतर्निहित उपकरण प्रदान नहीं करता है, लेकिन भविष्य में इस तरह के एक उपकरण को जोड़ने के लिए हमेशा आशा और वोट दिया जा सकता है।

मेरे लेख पर एक टिप्पणी में, मार्कोस किर्चनर ने उल्लेख किया कि उन्होंने अलग-अलग टेबल वैल्यू कंस्ट्रक्टर कार्डिनैलिटी के साथ मेरे समाधान का परीक्षण किया, और अलग-अलग कार्डिनैलिटी के लिए अलग-अलग निष्पादन समय प्राप्त किया।

मैंने हमेशा अपने समाधान का उपयोग बेस टेबल वैल्यू कंस्ट्रक्टर कार्डिनैलिटी 2 के साथ किया, लेकिन मार्कोस की टिप्पणी ने मुझे सोचने पर मजबूर कर दिया। यह टूल इतना उपयोगी है कि एक समुदाय के रूप में हमें सबसे तेज़ संस्करण बनाने की कोशिश करने के लिए सेना में शामिल होना चाहिए जो हम संभवतः कर सकते हैं। विभिन्न आधार तालिका कार्डिनैलिटी का परीक्षण करना कोशिश करने के लिए सिर्फ एक आयाम है। कई अन्य भी हो सकते हैं। मैं अपने समाधान के साथ किए गए प्रदर्शन परीक्षण प्रस्तुत करूंगा। मैंने मुख्य रूप से अलग-अलग टेबल वैल्यू कंस्ट्रक्टर कार्डिनैलिटी के साथ प्रयोग किया, सीरियल बनाम समानांतर प्रसंस्करण के साथ, और पंक्ति मोड बनाम बैच मोड प्रोसेसिंग के साथ। हालांकि, यह हो सकता है कि एक पूरी तरह से अलग समाधान मेरे सबसे अच्छे संस्करण से भी तेज हो। तो, चुनौती चालू है! मैं सभी जेडी, पदवान, जादूगर और प्रशिक्षु को समान रूप से बुला रहा हूं। सबसे अच्छा प्रदर्शन करने वाला समाधान क्या है जिसके बारे में आप अनुमान लगा सकते हैं? क्या आपके पास अब तक पोस्ट किए गए सबसे तेज़ समाधान को मात देने की क्षमता है? यदि ऐसा है, तो इस लेख पर टिप्पणी के रूप में अपना साझा करें, और दूसरों द्वारा पोस्ट किए गए किसी भी समाधान में सुधार करने के लिए स्वतंत्र महसूस करें।

आवश्यकताएँ:

  • अपने समाधान को dbo.GetNumsYourName नामक एक इनलाइन तालिका-मूल्यवान फ़ंक्शन (iTVF) के रूप में लागू करें, जिसमें @low AS BIGINT और @high AS BIGINT पैरामीटर हों। उदाहरण के तौर पर, इस लेख के अंत में मेरे द्वारा सबमिट किए गए लेख देखें।
  • यदि आवश्यक हो तो आप उपयोगकर्ता डेटाबेस में सहायक तालिकाएँ बना सकते हैं।
  • आप आवश्यकतानुसार संकेत जोड़ सकते हैं।
  • जैसा कि बताया गया है, समाधान को BIGINT प्रकार के सीमांकक का समर्थन करना चाहिए, लेकिन आप 4,294,967,296 की अधिकतम श्रृंखला कार्डिनैलिटी मान सकते हैं।
  • आपके समाधान के प्रदर्शन का मूल्यांकन करने और दूसरों के साथ इसकी तुलना करने के लिए, मैं SSMS में निष्पादन सक्षम होने के बाद परिणामों को त्यागने के साथ, 1 से 100,000,000 की सीमा के साथ इसका परीक्षण करूंगा।

हम सभी के लिए शुभकामनाए! सर्वश्रेष्ठ समुदाय की जीत हो।;)

बेस टेबल वैल्यू कंस्ट्रक्टर के लिए अलग-अलग कार्डिनैलिटी

मैंने आधार सीटीई की अलग-अलग कार्डिनैलिटी के साथ प्रयोग किया, जो 2 से शुरू हुआ और एक लघुगणकीय पैमाने में आगे बढ़ रहा था, प्रत्येक चरण में पिछली कार्डिनैलिटी का वर्ग:2, 4, 16 और 256।

इससे पहले कि आप विभिन्न आधार कार्डिनैलिटी के साथ प्रयोग करना शुरू करें, यह एक ऐसा फॉर्मूला होना मददगार हो सकता है जो बेस कार्डिनैलिटी और अधिकतम रेंज कार्डिनैलिटी को देखते हुए आपको बताए कि आपको सीटीई के कितने स्तर चाहिए। प्रारंभिक कदम के रूप में, पहले एक सूत्र के साथ आना आसान है जो आधार कार्डिनैलिटी और सीटीई के स्तरों की संख्या को देखते हुए, गणना करता है कि अधिकतम परिणामी सीमा कार्डिनैलिटी क्या है। यहाँ ऐसा सूत्र T-SQL में व्यक्त किया गया है:

DECLARE @basecardinality AS INT = 2, @levels AS INT = 5;
 
SELECT POWER(1.*@basecardinality, POWER(2., @levels));

उपरोक्त नमूना इनपुट मानों के साथ यह अभिव्यक्ति 4,294,967,296 की अधिकतम सीमा कार्डिनैलिटी उत्पन्न करती है।

फिर, आवश्यक सीटीई स्तरों की संख्या की गणना करने के लिए व्युत्क्रम सूत्र में दो लॉग फ़ंक्शन को नेस्ट करना शामिल है, जैसे:

DECLARE @basecardinality AS INT = 2, @seriescardinality AS BIGINT = 4294967296;
 
SELECT CEILING(LOG(LOG(@seriescardinality, @basecardinality), 2));

उपरोक्त नमूना इनपुट मानों के साथ यह अभिव्यक्ति 5 उत्पन्न करती है। ध्यान दें कि यह संख्या बेस सीटीई के अतिरिक्त है जिसमें टेबल वैल्यू कन्स्ट्रक्टर है, जिसे मैंने अपने समाधान में एल0 (स्तर 0 के लिए) नाम दिया है।

मुझसे यह मत पूछो कि मुझे ये सूत्र कैसे मिले। मैं जिस कहानी से जुड़ा हूं, वह यह है कि गंडालफ ने उन्हें मेरे सपनों में एल्विश में कहा था।

आइए प्रदर्शन परीक्षण के लिए आगे बढ़ें। सुनिश्चित करें कि आप ग्रिड, परिणाम के अंतर्गत अपने SSMS क्वेरी विकल्प संवाद में निष्पादन के बाद परिणाम छोड़ें सक्षम करते हैं। 2 के आधार सीटीई कार्डिनैलिटी के साथ परीक्षण चलाने के लिए निम्नलिखित कोड का उपयोग करें (सीटीई के 5 अतिरिक्त स्तरों की आवश्यकता है):

DECLARE @low AS BIGINT = 1, @high AS BIGINT = 100000000;
 
WITH
  L0 AS ( SELECT 1 AS c FROM (VALUES(1),(1)) AS D(c) ),
  L1 AS ( SELECT 1 AS c FROM L0 AS A CROSS JOIN L0 AS B ),
  L2 AS ( SELECT 1 AS c FROM L1 AS A CROSS JOIN L1 AS B ),
  L3 AS ( SELECT 1 AS c FROM L2 AS A CROSS JOIN L2 AS B ),
  L4 AS ( SELECT 1 AS c FROM L3 AS A CROSS JOIN L3 AS B ),
  L5 AS ( SELECT 1 AS c FROM L4 AS A CROSS JOIN L4 AS B ),
  Nums AS ( SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS rownum
            FROM L5 )
SELECT TOP(@high - @low + 1) @low + rownum - 1 AS n
FROM Nums
ORDER BY rownum;

मुझे इस निष्पादन के लिए चित्र 1 में दिखाया गया प्लान मिला है।

चित्र 1:आधार सीटीई कार्डिनैलिटी 2 के लिए योजना

योजना सीरियल है, और योजना में सभी ऑपरेटर डिफ़ॉल्ट रूप से पंक्ति मोड प्रसंस्करण का उपयोग करते हैं। यदि आप डिफ़ॉल्ट रूप से समानांतर योजना प्राप्त कर रहे हैं, उदाहरण के लिए, आईटीवीएफ में समाधान को समाहित करते समय और एक बड़ी रेंज का उपयोग करते समय, अभी के लिए MAXDOP 1 संकेत के साथ एक सीरियल योजना को लागू करें।

गौर कीजिए कि कैसे CTE के अनपैकिंग के परिणामस्वरूप लगातार स्कैन ऑपरेटर के 32 उदाहरण सामने आए, जिनमें से प्रत्येक दो पंक्तियों वाली तालिका का प्रतिनिधित्व करता है।

मुझे इस निष्पादन के लिए निम्नलिखित प्रदर्शन आँकड़े मिले:

CPU time = 30188 ms,  elapsed time = 32844 ms.

4 के आधार सीटीई कार्डिनैलिटी के साथ समाधान का परीक्षण करने के लिए निम्नलिखित कोड का उपयोग करें, जिसके लिए हमारे सूत्र के अनुसार सीटीई के चार स्तरों की आवश्यकता होती है:

DECLARE @low AS BIGINT = 1, @high AS BIGINT = 100000000;
 
WITH
  L0 AS ( SELECT 1 AS c FROM (VALUES(1),(1),(1),(1)) AS D(c) ),
  L1 AS ( SELECT 1 AS c FROM L0 AS A CROSS JOIN L0 AS B ),
  L2 AS ( SELECT 1 AS c FROM L1 AS A CROSS JOIN L1 AS B ),
  L3 AS ( SELECT 1 AS c FROM L2 AS A CROSS JOIN L2 AS B ),
  L4 AS ( SELECT 1 AS c FROM L3 AS A CROSS JOIN L3 AS B ),
  Nums AS ( SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS rownum
            FROM L4 )
SELECT TOP(@high - @low + 1) @low + rownum - 1 AS n
FROM Nums
ORDER BY rownum;

मुझे इस निष्पादन के लिए चित्र 2 में दिखाया गया प्लान मिला है।

चित्र 2:आधार सीटीई कार्डिनैलिटी 4 की योजना

CTE के अनपैकिंग के परिणामस्वरूप 16 लगातार स्कैन ऑपरेटर बने, जिनमें से प्रत्येक 4 पंक्तियों की तालिका का प्रतिनिधित्व करता है।

मुझे इस निष्पादन के लिए निम्नलिखित प्रदर्शन आँकड़े मिले:

CPU time = 23781 ms,  elapsed time = 25435 ms.

यह पिछले समाधान की तुलना में 22.5 प्रतिशत का एक अच्छा सुधार है।

क्वेरी के लिए रिपोर्ट किए गए प्रतीक्षा आंकड़ों की जांच करते हुए, प्रमुख प्रतीक्षा प्रकार SOS_SCHEDULER_YIELD है। दरअसल, पहले समाधान (प्रतीक्षा संख्या 15,280 बनाम 19,800) की तुलना में प्रतीक्षा संख्या में 22.8 प्रतिशत की कमी आई है।

16 के आधार सीटीई कार्डिनैलिटी के साथ समाधान का परीक्षण करने के लिए निम्नलिखित कोड का उपयोग करें, जिसके लिए हमारे सूत्र के अनुसार सीटीई के तीन स्तरों की आवश्यकता होती है:

DECLARE @low AS BIGINT = 1, @high AS BIGINT = 100000000;
 
WITH
  L0 AS ( SELECT 1 AS c 
          FROM (VALUES(1),(1),(1),(1),(1),(1),(1),(1),
                      (1),(1),(1),(1),(1),(1),(1),(1)) AS D(c) ),
  L1 AS ( SELECT 1 AS c FROM L0 AS A CROSS JOIN L0 AS B ),
  L2 AS ( SELECT 1 AS c FROM L1 AS A CROSS JOIN L1 AS B ),
  L3 AS ( SELECT 1 AS c FROM L2 AS A CROSS JOIN L2 AS B ),
  Nums AS ( SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS rownum
            FROM L3 )
SELECT TOP(@high - @low + 1) @low + rownum - 1 AS n
FROM Nums
ORDER BY rownum;

मुझे इस निष्पादन के लिए चित्र 3 में दिखाई गई योजना मिली है।

चित्र 3:16 के आधार CTE कार्डिनैलिटी के लिए योजना

इस बार CTE की अनपैकिंग के परिणामस्वरूप 8 लगातार स्कैन ऑपरेटर बने, जिनमें से प्रत्येक 16 पंक्तियों वाली एक तालिका का प्रतिनिधित्व करता है।

मुझे इस निष्पादन के लिए निम्नलिखित प्रदर्शन आँकड़े मिले:

CPU time = 22968 ms,  elapsed time = 24409 ms.

यह समाधान बीता हुआ समय को और कम कर देता है, यद्यपि केवल कुछ अतिरिक्त प्रतिशत, जो पहले समाधान की तुलना में 25.7 प्रतिशत की कमी है। फिर से, SOS_SCHEDULER_YIELD प्रतीक्षा प्रकार की प्रतीक्षा संख्या गिरती रहती है (12,938)।

हमारे लघुगणकीय पैमाने में आगे बढ़ते हुए, अगले परीक्षण में आधार सीटीई कार्डिनैलिटी 256 शामिल है। यह लंबा और बदसूरत है, लेकिन इसे आज़माएं:

DECLARE @low AS BIGINT = 1, @high AS BIGINT = 100000000;
 
WITH
  L0 AS ( SELECT 1 AS c 
          FROM (VALUES(1),(1),(1),(1),(1),(1),(1),(1),
                      (1),(1),(1),(1),(1),(1),(1),(1),
                      (1),(1),(1),(1),(1),(1),(1),(1),
                      (1),(1),(1),(1),(1),(1),(1),(1),
                      (1),(1),(1),(1),(1),(1),(1),(1),
                      (1),(1),(1),(1),(1),(1),(1),(1),
                      (1),(1),(1),(1),(1),(1),(1),(1),
                      (1),(1),(1),(1),(1),(1),(1),(1),
                      (1),(1),(1),(1),(1),(1),(1),(1),
                      (1),(1),(1),(1),(1),(1),(1),(1),
                      (1),(1),(1),(1),(1),(1),(1),(1),
                      (1),(1),(1),(1),(1),(1),(1),(1),
                      (1),(1),(1),(1),(1),(1),(1),(1),
                      (1),(1),(1),(1),(1),(1),(1),(1),
                      (1),(1),(1),(1),(1),(1),(1),(1),
                      (1),(1),(1),(1),(1),(1),(1),(1),
                      (1),(1),(1),(1),(1),(1),(1),(1),
                      (1),(1),(1),(1),(1),(1),(1),(1),
                      (1),(1),(1),(1),(1),(1),(1),(1),
                      (1),(1),(1),(1),(1),(1),(1),(1),
                      (1),(1),(1),(1),(1),(1),(1),(1),
                      (1),(1),(1),(1),(1),(1),(1),(1),
                      (1),(1),(1),(1),(1),(1),(1),(1),
                      (1),(1),(1),(1),(1),(1),(1),(1),
                      (1),(1),(1),(1),(1),(1),(1),(1),
                      (1),(1),(1),(1),(1),(1),(1),(1),
                      (1),(1),(1),(1),(1),(1),(1),(1),
                      (1),(1),(1),(1),(1),(1),(1),(1),
                      (1),(1),(1),(1),(1),(1),(1),(1),
                      (1),(1),(1),(1),(1),(1),(1),(1),
                      (1),(1),(1),(1),(1),(1),(1),(1),
                      (1),(1),(1),(1),(1),(1),(1),(1)) AS D(c) ),
  L1 AS ( SELECT 1 AS c FROM L0 AS A CROSS JOIN L0 AS B ),
  L2 AS ( SELECT 1 AS c FROM L1 AS A CROSS JOIN L1 AS B ),
  Nums AS ( SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS rownum
            FROM L2 )
SELECT TOP(@high - @low + 1) @low + rownum - 1 AS n
FROM Nums
ORDER BY rownum;

मुझे इस निष्पादन के लिए चित्र 4 में दिखाया गया प्लान मिला है।

चित्र 4:256 के आधार सीटीई कार्डिनैलिटी के लिए योजना

इस बार सीटीई के अनपैकिंग के परिणामस्वरूप केवल चार कॉन्स्टेंट स्कैन ऑपरेटर थे, जिनमें से प्रत्येक में 256 पंक्तियाँ थीं।

मुझे इस निष्पादन के लिए निम्नलिखित प्रदर्शन संख्याएँ मिलीं:

CPU time = 23516 ms,  elapsed time = 25529 ms.

इस बार ऐसा लगता है कि 16 के आधार सीटीई कार्डिनैलिटी के साथ पिछले समाधान की तुलना में प्रदर्शन थोड़ा कम हो गया है। वास्तव में, SOS_SCHEDULER_YIELD प्रतीक्षा प्रकार की प्रतीक्षा संख्या थोड़ी बढ़ कर 13,176 हो गई है। तो, ऐसा लगता है कि हमें अपना सुनहरा नंबर—16 मिल गया!

समानांतर बनाम सीरियल प्लान

मैंने संकेत ENABLE_PARALLEL_PLAN_PREFERENCE का उपयोग करके समानांतर योजना को लागू करने के लिए प्रयोग किया, लेकिन यह प्रदर्शन को नुकसान पहुंचा रहा था। वास्तव में, आईटीवीएफ के रूप में समाधान को लागू करते समय, मुझे बड़ी रेंज के लिए डिफ़ॉल्ट रूप से मेरी मशीन पर समानांतर योजना मिली, और इष्टतम प्रदर्शन प्राप्त करने के लिए MAXDOP 1 संकेत के साथ एक सीरियल योजना को मजबूर करना पड़ा।

बैच प्रोसेसिंग

मेरे समाधान के लिए योजनाओं में उपयोग किया जाने वाला मुख्य संसाधन सीपीयू है। यह देखते हुए कि बैच प्रोसेसिंग सभी सीपीयू दक्षता में सुधार के बारे में है, खासकर जब बड़ी संख्या में पंक्तियों से निपटते हैं, तो इस विकल्प को आजमाने के लिए उपयुक्त है। यहां मुख्य गतिविधि जो बैच प्रोसेसिंग से लाभान्वित हो सकती है, वह है पंक्ति संख्या गणना। मैंने SQL सर्वर 2019 एंटरप्राइज़ संस्करण में अपने समाधानों का परीक्षण किया। SQL सर्वर ने डिफ़ॉल्ट रूप से पहले दिखाए गए सभी समाधानों के लिए पंक्ति मोड प्रसंस्करण को चुना। जाहिरा तौर पर, यह समाधान रोस्टोर पर बैच मोड को सक्षम करने के लिए आवश्यक अनुमानों को पारित नहीं करता है। यहां बैच प्रोसेसिंग का उपयोग करने के लिए SQL सर्वर प्राप्त करने के कुछ तरीके हैं।

विकल्प 1 समाधान में कॉलमस्टोर इंडेक्स वाली तालिका को शामिल करना है। आप इसे एक कॉलमस्टोर इंडेक्स के साथ एक डमी टेबल बनाकर प्राप्त कर सकते हैं और हमारे न्यूम्स सीटीई और उस टेबल के बीच सबसे बाहरी क्वेरी में एक डमी लेफ्ट जॉइन पेश कर सकते हैं। यहाँ डमी टेबल की परिभाषा है:

CREATE TABLE dbo.BatchMe(col1 INT NOT NULL, INDEX idx_cs CLUSTERED COLUMNSTORE);

फिर नंबर्स के विरुद्ध बाहरी क्वेरी को संशोधित करें ताकि नंबर से बाएं बाहरी जॉइन dbo.BatchMe ON 1 =0 का उपयोग किया जा सके। यहां 16 के आधार CTE कार्डिनैलिटी के साथ एक उदाहरण दिया गया है:

DECLARE @low AS BIGINT = 1, @high AS BIGINT = 100000000;
 
WITH
  L0 AS ( SELECT 1 AS c 
          FROM (VALUES(1),(1),(1),(1),(1),(1),(1),(1),
                      (1),(1),(1),(1),(1),(1),(1),(1)) AS D(c) ),
  L1 AS ( SELECT 1 AS c FROM L0 AS A CROSS JOIN L0 AS B ),
  L2 AS ( SELECT 1 AS c FROM L1 AS A CROSS JOIN L1 AS B ),
  L3 AS ( SELECT 1 AS c FROM L2 AS A CROSS JOIN L2 AS B ),
  Nums AS ( SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS rownum
            FROM L3 )
SELECT TOP(@high - @low + 1) @low + rownum - 1 AS n
FROM Nums LEFT OUTER JOIN dbo.BatchMe ON 1 = 0
ORDER BY rownum;

मुझे इस निष्पादन के लिए चित्र 5 में दिखाया गया प्लान मिला है।

चित्र 5:बैच प्रोसेसिंग के साथ योजना बनाएं

पंक्ति संख्याओं की गणना करने के लिए बैच मोड विंडो एग्रीगेट ऑपरेटर के उपयोग पर ध्यान दें। यह भी देखें कि योजना में डमी टेबल शामिल नहीं है। अनुकूलक ने इसे अनुकूलित किया।

विकल्प 1 का उल्टा यह है कि यह सभी SQL सर्वर संस्करणों में काम करता है और SQL सर्वर 2016 या उसके बाद के संस्करण में प्रासंगिक है, क्योंकि बैच मोड विंडो एग्रीगेट ऑपरेटर को SQL Server 2016 में पेश किया गया था। नकारात्मक पक्ष डमी तालिका बनाने और शामिल करने की आवश्यकता है समाधान में।

हमारे समाधान के लिए बैच प्रोसेसिंग प्राप्त करने के लिए विकल्प 2, बशर्ते कि आप SQL सर्वर 2019 एंटरप्राइज संस्करण का उपयोग कर रहे हैं, अनिर्दिष्ट स्व-व्याख्यात्मक संकेत OVERRIDE_BATCH_MODE_HEURISTICS (दिमित्री पिलुगिन के लेख में विवरण) का उपयोग करना है, जैसे:

DECLARE @low AS BIGINT = 1, @high AS BIGINT = 100000000;
 
WITH
  L0 AS ( SELECT 1 AS c 
          FROM (VALUES(1),(1),(1),(1),(1),(1),(1),(1),
                      (1),(1),(1),(1),(1),(1),(1),(1)) AS D(c) ),
  L1 AS ( SELECT 1 AS c FROM L0 AS A CROSS JOIN L0 AS B ),
  L2 AS ( SELECT 1 AS c FROM L1 AS A CROSS JOIN L1 AS B ),
  L3 AS ( SELECT 1 AS c FROM L2 AS A CROSS JOIN L2 AS B ),
  Nums AS ( SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS rownum
            FROM L3 )
SELECT TOP(@high - @low + 1) @low + rownum - 1 AS n
FROM Nums
ORDER BY rownum
OPTION(USE HINT('OVERRIDE_BATCH_MODE_HEURISTICS'));

विकल्प 2 का लाभ यह है कि आपको डमी टेबल बनाने और इसे अपने समाधान में शामिल करने की आवश्यकता नहीं है। डाउनसाइड्स यह है कि आपको एंटरप्राइज़ संस्करण का उपयोग करने की आवश्यकता है, न्यूनतम SQL सर्वर 2019 का उपयोग करें जहां रोस्टोर पर बैच मोड पेश किया गया था, और समाधान में एक अनिर्दिष्ट संकेत का उपयोग करना शामिल है। इन कारणों से, मुझे विकल्प 1 पसंद है।

विभिन्न आधार सीटीई कार्डिनैलिटीज के लिए मुझे जो प्रदर्शन संख्याएं मिली हैं, वे यहां दी गई हैं:

Cardinality 2:   CPU time = 21594 ms,  elapsed time = 22743 ms (down from 32844).

Cardinality 4:   CPU time = 18375 ms,  elapsed time = 19394 ms (down from 25435).

Cardinality 16:  CPU time = 17640 ms,  elapsed time = 18568 ms (down from 24409).

Cardinality 256: CPU time = 17109 ms,  elapsed time = 18507 ms (down from 25529).

चित्र 6 में विभिन्न समाधानों के बीच प्रदर्शन तुलना है:

चित्र 6:प्रदर्शन तुलना

आप पंक्ति मोड समकक्षों की तुलना में 20-30 प्रतिशत का एक अच्छा प्रदर्शन सुधार देख सकते हैं।

उत्सुकता से, 256 के आधार सीटीई कार्डिनैलिटी के साथ समाधान को बैच मोड प्रसंस्करण के साथ सबसे अच्छा किया। हालांकि, यह 16 के आधार सीटीई कार्डिनैलिटी वाले समाधान की तुलना में थोड़ा तेज है। अंतर इतना छोटा है, और कोड संक्षिप्तता के मामले में बाद वाले का स्पष्ट लाभ है, कि मैं 16 तक ही रहूंगा।

इसलिए, मेरे ट्यूनिंग प्रयासों ने मूल समाधान से 43.5 प्रतिशत का सुधार प्राप्त किया, जिसमें पंक्ति मोड प्रसंस्करण का उपयोग करते हुए 2 की आधार कार्डिनैलिटी थी।

चुनौती जारी है!

मैं इस चुनौती में अपने समुदाय के योगदान के रूप में दो समाधान प्रस्तुत करता हूं। यदि आप SQL सर्वर 2016 या उसके बाद के संस्करण पर चल रहे हैं, और उपयोगकर्ता डेटाबेस में एक तालिका बनाने में सक्षम हैं, तो निम्न डमी तालिका बनाएं:

CREATE TABLE dbo.BatchMe(col1 INT NOT NULL, INDEX idx_cs CLUSTERED COLUMNSTORE);

और निम्नलिखित iTVF परिभाषा का उपयोग करें:

CREATE OR ALTER FUNCTION dbo.GetNumsItzikBatch(@low AS BIGINT, @high AS BIGINT)
  RETURNS TABLE
AS
RETURN
  WITH
    L0 AS ( SELECT 1 AS c 
            FROM (VALUES(1),(1),(1),(1),(1),(1),(1),(1),
                        (1),(1),(1),(1),(1),(1),(1),(1)) AS D(c) ),
    L1 AS ( SELECT 1 AS c FROM L0 AS A CROSS JOIN L0 AS B ),
    L2 AS ( SELECT 1 AS c FROM L1 AS A CROSS JOIN L1 AS B ),
    L3 AS ( SELECT 1 AS c FROM L2 AS A CROSS JOIN L2 AS B ),
    Nums AS ( SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS rownum
              FROM L3 )
 
  SELECT TOP(@high - @low + 1) @low + rownum - 1 AS n
  FROM Nums LEFT OUTER JOIN dbo.BatchMe ON 1 = 0
  ORDER BY rownum;
GO

इसका परीक्षण करने के लिए निम्नलिखित कोड का उपयोग करें (सुनिश्चित करें कि निष्पादन की जाँच के बाद परिणाम छोड़ें):

SELECT n FROM dbo.GetNumsItzikBatch(1, 100000000) OPTION(MAXDOP 1);

यह कोड मेरी मशीन पर 18 सेकंड में समाप्त हो जाता है।

यदि किसी कारण से आप बैच प्रोसेसिंग समाधान की आवश्यकताओं को पूरा नहीं कर सकते हैं, तो मैं अपने दूसरे समाधान के रूप में निम्नलिखित फ़ंक्शन परिभाषा प्रस्तुत करता हूं:

CREATE OR ALTER FUNCTION dbo.GetNumsItzik(@low AS BIGINT, @high AS BIGINT)
  RETURNS TABLE
AS
RETURN
  WITH
    L0 AS ( SELECT 1 AS c 
            FROM (VALUES(1),(1),(1),(1),(1),(1),(1),(1),
                        (1),(1),(1),(1),(1),(1),(1),(1)) AS D(c) ),
    L1 AS ( SELECT 1 AS c FROM L0 AS A CROSS JOIN L0 AS B ),
    L2 AS ( SELECT 1 AS c FROM L1 AS A CROSS JOIN L1 AS B ),
    L3 AS ( SELECT 1 AS c FROM L2 AS A CROSS JOIN L2 AS B ),
    Nums AS ( SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS rownum
              FROM L3 )
  SELECT TOP(@high - @low + 1) @low + rownum - 1 AS n
  FROM Nums
  ORDER BY rownum;
GO

इसका परीक्षण करने के लिए निम्नलिखित कोड का प्रयोग करें:

SELECT n FROM dbo.GetNumsItzik(1, 100000000) OPTION(MAXDOP 1);

यह कोड मेरी मशीन पर 24 सेकंड में समाप्त हो जाता है।

आपकी बारी!


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. SQL, डेटा और तालिकाओं को कैसे हटाएं

  2. स्वादिष्ट भोजन परोसना (और डेटा) - रेस्तरां के लिए एक डेटा मॉडल

  3. कार किराए पर लेना ड्राइविंग जितना आसान है:कार रेंटल कंपनी के लिए एक डेटा मॉडल

  4. डोमिनोज़ का रहस्य, या एक डोमिनोज़ गेम डेटा मॉडल

  5. IAM नीति टेम्पलेट का उपयोग करके AWS पर स्केलग्रिड अनुमतियों को कॉन्फ़िगर करना