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

लूप के बिना एक सेट या अनुक्रम उत्पन्न करें - भाग 1

SQL सर्वर में मानों का अनुक्रम उत्पन्न करने के लिए कई उपयोग के मामले हैं। मैं एक सतत IDENTITY के बारे में बात नहीं कर रहा हूँ कॉलम (या नया SEQUENCE SQL सर्वर 2012 में), बल्कि एक क्षणिक सेट का उपयोग केवल एक क्वेरी के जीवनकाल के लिए किया जाना है। या यहां तक ​​​​कि सबसे सरल मामले - जैसे परिणाम में प्रत्येक पंक्ति में केवल एक पंक्ति संख्या जोड़ना - जिसमें ROW_NUMBER() जोड़ना शामिल हो सकता है क्वेरी के लिए कार्य (या, बेहतर अभी तक, प्रस्तुति स्तर में, जो परिणाम पंक्ति-दर-पंक्ति के माध्यम से वैसे भी लूप करना है)।

मैं थोड़ा और जटिल मामलों के बारे में बात कर रहा हूँ। उदाहरण के लिए, आपके पास एक रिपोर्ट हो सकती है जो तिथि के अनुसार बिक्री दिखाती है। एक सामान्य क्वेरी हो सकती है:

SELECT 
  OrderDate  = CONVERT(DATE, OrderDate),
  OrderCount = COUNT(*)
FROM dbo.Orders
GROUP BY CONVERT(DATE, OrderDate)
ORDER BY OrderDate;

इस प्रश्न के साथ समस्या यह है कि, यदि किसी निश्चित दिन पर कोई आदेश नहीं है, तो उस दिन के लिए कोई पंक्ति नहीं होगी। इससे डेटा के डाउनस्ट्रीम उपभोक्ताओं के लिए भ्रम, भ्रामक डेटा, या यहां तक ​​कि गलत गणना (दैनिक औसत लगता है) हो सकती है।

इसलिए उन अंतरालों को उन तारीखों से भरने की जरूरत है जो डेटा में मौजूद नहीं हैं। और कभी-कभी लोग अपने डेटा को #temp तालिका में भर देंगे और WHILE . का उपयोग करेंगे लापता तिथियों को एक-एक करके भरने के लिए लूप या कर्सर। मैं यहां वह कोड नहीं दिखाऊंगा क्योंकि मैं इसके उपयोग की वकालत नहीं करना चाहता, लेकिन मैंने इसे हर जगह देखा है।

इससे पहले कि हम तारीखों की गहराई में उतरें, आइए पहले संख्याओं के बारे में बात करें, क्योंकि आप तारीखों के क्रम को प्राप्त करने के लिए हमेशा संख्याओं के अनुक्रम का उपयोग कर सकते हैं।

नंबर तालिका

मैं लंबे समय से डिस्क पर एक सहायक "संख्या तालिका" संग्रहीत करने का समर्थक रहा हूं (और, उस मामले के लिए, एक कैलेंडर तालिका भी)।

1,000,000 मानों वाली एक साधारण संख्या तालिका बनाने का एक तरीका यहां दिया गया है:

SELECT TOP (1000000) n = CONVERT(INT, ROW_NUMBER() OVER (ORDER BY s1.[object_id]))
INTO dbo.Numbers
FROM sys.all_objects AS s1 CROSS JOIN sys.all_objects AS s2
OPTION (MAXDOP 1);
 
CREATE UNIQUE CLUSTERED INDEX n ON dbo.Numbers(n)
-- WITH (DATA_COMPRESSION = PAGE)
;
पर अद्वितीय क्लस्टर इंडेक्स बनाएं

MAXDOP 1 क्यों? पंक्ति लक्ष्यों से संबंधित पॉल व्हाइट का ब्लॉग पोस्ट और उनका कनेक्ट आइटम देखें।

हालांकि, कई लोग सहायक तालिका दृष्टिकोण का विरोध कर रहे हैं। उनका तर्क:जब वे डेटा ऑन-द-फ्लाई उत्पन्न कर सकते हैं तो डिस्क पर (और स्मृति में) उस सभी डेटा को क्यों स्टोर करें? मेरा काउंटर यथार्थवादी होना है और इस बारे में सोचना है कि आप क्या अनुकूलित कर रहे हैं; गणना महंगी हो सकती है, और क्या आप सुनिश्चित हैं कि फ्लाई पर संख्याओं की एक श्रृंखला की गणना करना हमेशा सस्ता होगा? जहाँ तक स्थान की बात है, Numbers तालिका केवल 11 एमबी संपीड़ित, और 17 एमबी असंपीड़ित होती है। और यदि तालिका को बार-बार पर्याप्त रूप से संदर्भित किया जाता है, तो यह हमेशा स्मृति में होनी चाहिए, जिससे पहुंच तेजी से हो।

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

1,000 संख्याओं का एक क्रम बनाना

आसान शुरुआत करते हुए, आइए 1 से 1,000 तक की संख्याओं का एक सेट तैयार करें।

    नंबर तालिका

    बेशक एक संख्या तालिका के साथ यह कार्य बहुत आसान है:

    SELECT TOP (1000) n FROM dbo.Numbers ORDER BY n;

    योजना:

    spt_values

    यह एक तालिका है जिसका उपयोग विभिन्न उद्देश्यों के लिए आंतरिक संग्रहीत प्रक्रियाओं द्वारा किया जाता है। इसका ऑनलाइन उपयोग काफी प्रचलित प्रतीत होता है, भले ही यह अनिर्दिष्ट, असमर्थित है, यह एक दिन गायब हो सकता है, और क्योंकि इसमें केवल मूल्यों का एक सीमित, गैर-अद्वितीय और गैर-सन्निहित सेट होता है। SQL Server 2008 R2 में 2,164 अद्वितीय और 2,508 कुल मान हैं; 2012 में 2,167 अद्वितीय और कुल 2,515 हैं। इसमें डुप्लीकेट, नकारात्मक मान शामिल हैं, और भले ही DISTINCT . का उपयोग कर रहे हों , एक बार जब आप संख्या 2,048 से आगे निकल जाते हैं तो बहुत सारे अंतराल होते हैं। तो इसका समाधान ROW_NUMBER() . का उपयोग करना है तालिका में मानों के आधार पर 1 से शुरू होकर, एक सन्निहित अनुक्रम उत्पन्न करने के लिए।

    SELECT TOP (1000) n = ROW_NUMBER() OVER (ORDER BY number) 
      FROM [master]..spt_values ORDER BY n;

    योजना:

    उस ने कहा, केवल 1,000 मानों के लिए, आप समान अनुक्रम उत्पन्न करने के लिए थोड़ी सरल क्वेरी लिख सकते हैं:

    SELECT DISTINCT n = number FROM master..[spt_values] WHERE number BETWEEN 1 AND 1000;

    यह निश्चित रूप से एक सरल योजना की ओर ले जाता है, लेकिन बहुत जल्दी टूट जाता है (एक बार आपका अनुक्रम 2,048 पंक्तियों से अधिक होना चाहिए):

    किसी भी मामले में, मैं इस तालिका के उपयोग की अनुशंसा नहीं करता; मैं इसे तुलनात्मक उद्देश्यों के लिए शामिल कर रहा हूं, केवल इसलिए कि मुझे पता है कि इसमें से कितना बाहर है, और यह कितना आकर्षक हो सकता है कि आप जिस कोड को देखते हैं उसका पुन:उपयोग करें।

    sys.all_objects

    एक और तरीका जो वर्षों से मेरे पसंदीदा में से एक रहा है, वह है sys.all_objects का उपयोग करना . जैसे spt_values , सीधे एक सन्निहित अनुक्रम उत्पन्न करने का कोई विश्वसनीय तरीका नहीं है, और हमारे पास एक सीमित सेट (केवल SQL Server 2008 R2 में 2,000 पंक्तियों के नीचे और SQL Server 2012 में 2,000 से अधिक पंक्तियों के साथ) से निपटने के समान मुद्दे हैं, लेकिन 1,000 पंक्तियों के लिए हम उसी का उपयोग कर सकते हैं ROW_NUMBER() छल। मुझे इस दृष्टिकोण को पसंद करने का कारण यह है कि (ए) कम चिंता है कि यह दृश्य जल्द ही गायब हो जाएगा, (बी) दृश्य स्वयं दस्तावेज और समर्थित है, और (सी) यह SQL सर्वर के बाद से किसी भी संस्करण पर किसी भी डेटाबेस पर चलेगा 2005 डेटाबेस सीमाओं को पार किए बिना (निहित डेटाबेस सहित)।

    SELECT TOP (1000) n = ROW_NUMBER() OVER (ORDER BY [object_id]) FROM sys.all_objects ORDER BY n;

    योजना:

    स्टैक्ड सीटीई

    मेरा मानना ​​है कि इत्ज़िक बेन-गण इस दृष्टिकोण के लिए अंतिम श्रेय के पात्र हैं; मूल रूप से आप मूल्यों के एक छोटे से सेट के साथ एक सीटीई बनाते हैं, फिर आपको आवश्यक पंक्तियों की संख्या उत्पन्न करने के लिए कार्टेशियन उत्पाद स्वयं के विरुद्ध बनाते हैं। और फिर, अंतर्निहित क्वेरी के भाग के रूप में एक सन्निहित सेट उत्पन्न करने की कोशिश करने के बजाय, हम केवल ROW_NUMBER() लागू कर सकते हैं अंतिम परिणाम के लिए।

    ;WITH e1(n) AS
    (
        SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL 
        SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL 
        SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
    ), -- 10
    e2(n) AS (SELECT 1 FROM e1 CROSS JOIN e1 AS b), -- 10*10
    e3(n) AS (SELECT 1 FROM e1 CROSS JOIN e2) -- 10*100
      SELECT n = ROW_NUMBER() OVER (ORDER BY n) FROM e3 ORDER BY n;

    योजना:

    पुनरावर्ती CTE

    अंत में, हमारे पास एक पुनरावर्ती सीटीई है, जो एंकर के रूप में 1 का उपयोग करता है, और 1 जोड़ता है जब तक हम अधिकतम हिट नहीं करते। सुरक्षा के लिए मैं WHERE . दोनों में अधिकतम निर्दिष्ट करता हूं पुनरावर्ती भाग का खंड, और MAXRECURSION . में सेटिंग। आपको कितने नंबर चाहिए, इसके आधार पर आपको MAXRECURSION . सेट करना पड़ सकता है करने के लिए 0

    ;WITH n(n) AS
    (
        SELECT 1
        UNION ALL
        SELECT n+1 FROM n WHERE n < 1000
    )
    SELECT n FROM n ORDER BY n
    OPTION (MAXRECURSION 1000);

    योजना:

प्रदर्शन

बेशक 1,000 मूल्यों के साथ प्रदर्शन में अंतर नगण्य है, लेकिन यह देखना उपयोगी हो सकता है कि ये विभिन्न विकल्प कैसा प्रदर्शन करते हैं:


रनटाइम, मिलीसेकंड में, 1,000 सन्निहित संख्याएं जेनरेट करने के लिए

मैंने प्रत्येक क्वेरी को 20 बार चलाया और औसत रनटाइम लिया। मैंने dbo.Numbers . का भी परीक्षण किया तालिका, संपीड़ित और असम्पीडित दोनों स्वरूपों में, और ठंडे कैश और गर्म कैश दोनों के साथ। वार्म कैशे के साथ यह अन्य सबसे तेज़ विकल्पों (spt_values .) को बहुत करीब से टक्कर देता है , अनुशंसित नहीं है, और स्टैक्ड सीटीई), लेकिन पहली हिट अपेक्षाकृत महंगी है (हालांकि मैं इसे लगभग हंसते हुए कहता हूं)।

जारी रखने के लिए...

यदि यह आपका विशिष्ट उपयोग मामला है, और आप 1,000 पंक्तियों से आगे नहीं बढ़ेंगे, तो मुझे आशा है कि मैंने उन संख्याओं को उत्पन्न करने के सबसे तेज़ तरीके दिखाए हैं। यदि आपका उपयोग मामला एक बड़ी संख्या है, या यदि आप तिथियों के अनुक्रम उत्पन्न करने के लिए समाधान ढूंढ रहे हैं, तो देखते रहें। बाद में इस श्रृंखला में, मैं 50,000 और 1,000,000 संख्याओं और एक सप्ताह से लेकर एक वर्ष तक की दिनांक सीमाओं के जनरेटिंग अनुक्रमों का पता लगाऊंगा।

[ भाग 1 | भाग 2 | भाग 3 ]


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. लेन-देन लॉग को ट्रिम करना फैट

  2. कैसे सुनिश्चित करें कि डेटाबेस का नियमित रूप से बैकअप लिया जाता है

  3. आयामों के आयाम:डेटा वेयरहाउसिंग के सबसे सामान्य आयामी तालिका प्रकारों पर एक नज़र

  4. SQL में काउंट द्वारा ऑर्डर कैसे करें?

  5. सामान्य त्रुटि:OS संस्करण बेमेल