नोट:यह पोस्ट मूल रूप से केवल हमारी ईबुक, SQL सर्वर के लिए उच्च प्रदर्शन तकनीक, खंड 3 में प्रकाशित हुई थी। आप हमारी ई-पुस्तकों के बारे में यहां जान सकते हैं।
एक आवश्यकता जो मुझे कभी-कभी दिखाई देती है, वह यह है कि ग्राहक द्वारा समूहीकृत आदेशों के साथ एक क्वेरी लौटाई जाए, जो किसी भी आदेश के लिए देखे गए अधिकतम देय राशि ("रनिंग मैक्स") को दिखाती है। तो इन नमूना पंक्तियों की कल्पना करें:
| SalesOrderID | ग्राहक आईडी | आदेश दिनांक | कुल देय |
|---|---|---|---|
| 12 | 2 | 2014-01-01 | 37.55 |
| 23 | 1 | 2014-01-02 | 45.29 |
| 31 | 2 | 2014-01-03 | 24.56 |
| 32 | 2 | 2014-01-04 | 89.84 |
| 37 | 1 | 2014-01-05 | 32.56 |
| 44 | 2 | 2014-01-06 | 45.54 |
| 55 | 1 | 2014-01-07 | 99.24 |
| 62 | 2 | 2014-01-08 | 12.55 |
नमूना डेटा की कुछ पंक्तियाँ
बताई गई आवश्यकताओं से वांछित परिणाम इस प्रकार हैं - सादे शब्दों में, प्रत्येक ग्राहक के आदेशों को तिथि के अनुसार क्रमबद्ध करें, और प्रत्येक आदेश को सूचीबद्ध करें। यदि उस तिथि तक देखे गए सभी आदेशों के लिए यह उच्चतम TotalDue मान है, तो उस आदेश का कुल प्रिंट करें, अन्यथा पिछले सभी आदेशों से उच्चतम TotalDue मान प्रिंट करें:
| SalesOrderID | ग्राहक आईडी | आदेश दिनांक | कुल देय | MaxTotalDue |
|---|---|---|---|---|
| 12 | 1 | 2014-01-02 | 45.29 | 45.29 |
| 23 | 1 | 2014-01-05 | 32.56 | 45.29 |
| 31 | 1 | 2014-01-07 | 99.24 | 99.24 |
| 32 | 2 | 2014-01-01 | 37.55 | 37.55 |
| 37 | 2 | 2014-01-03 | 24.56 | 37.55 |
| 44 | 2 | 2014-01-04 | 89.84 | 89.84 |
| 55 | 2 | 2014-01-06 | 45.54 | 89.84 |
| 62 | 2 | 2014-01-08 | 12.55 | 89.84 |
नमूना वांछित परिणाम
बहुत से लोग सहज रूप से इसे पूरा करने के लिए कर्सर या लूप का उपयोग करना चाहेंगे, लेकिन ऐसे कई दृष्टिकोण हैं जिनमें इन निर्माणों को शामिल नहीं किया गया है।
सहसंबंधित सबक्वेरी
यह दृष्टिकोण समस्या के लिए सबसे सरल और सबसे सीधा दृष्टिकोण प्रतीत होता है, लेकिन यह बार-बार साबित हुआ है कि स्केल नहीं है, क्योंकि जैसे-जैसे तालिका बड़ी होती जाती है, रीडिंग तेजी से बढ़ती है:
SELECT /* Correlated Subquery */ SalesOrderID, CustomerID, OrderDate, TotalDue,
MaxTotalDue = (SELECT MAX(TotalDue)
FROM Sales.SalesOrderHeader
WHERE CustomerID = h.CustomerID
AND SalesOrderID <= h.SalesOrderID)
FROM Sales.SalesOrderHeader AS h
ORDER BY CustomerID, SalesOrderID; यहाँ SQL संतरी योजना एक्सप्लोरर का उपयोग करते हुए AdventureWorks2014 के खिलाफ योजना है:
सहसंबद्ध उपश्रेणी के लिए निष्पादन योजना (विस्तार करने के लिए क्लिक करें)
स्व-संदर्भित क्रॉस लागू करें
यह दृष्टिकोण सिंटैक्स, योजना आकार और पैमाने पर प्रदर्शन के संदर्भ में सहसंबद्ध सबक्वेरी दृष्टिकोण के लगभग समान है।
SELECT /* CROSS APPLY */ h.SalesOrderID, h.CustomerID, h.OrderDate, h.TotalDue, x.MaxTotalDue
FROM Sales.SalesOrderHeader AS h
CROSS APPLY
(
SELECT MaxTotalDue = MAX(TotalDue)
FROM Sales.SalesOrderHeader AS i
WHERE i.CustomerID = h.CustomerID
AND i.SalesOrderID <= h.SalesOrderID
) AS x
ORDER BY h.CustomerID, h.SalesOrderID; योजना काफी हद तक सहसंबद्ध उपश्रेणी योजना के समान है, केवल एक प्रकार का स्थान होने का अंतर है:
क्रॉस एप्लाई के लिए निष्पादन योजना (विस्तार करने के लिए क्लिक करें)
पुनरावर्ती CTE
पर्दे के पीछे, यह लूप का उपयोग करता है, लेकिन जब तक हम वास्तव में इसे नहीं चलाते हैं, तब तक हम ऐसा दिखावा कर सकते हैं कि यह नहीं है (हालांकि यह आसानी से कोड का सबसे जटिल टुकड़ा है जिसे मैं इस विशेष समस्या को हल करने के लिए लिखना चाहता हूं):पी>
;WITH /* Recursive CTE */ cte AS
(
SELECT SalesOrderID, CustomerID, OrderDate, TotalDue, MaxTotalDue
FROM
(
SELECT SalesOrderID, CustomerID, OrderDate, TotalDue, MaxTotalDue = TotalDue,
rn = ROW_NUMBER() OVER (PARTITION BY CustomerID ORDER BY SalesOrderID)
FROM Sales.SalesOrderHeader
) AS x
WHERE rn = 1
UNION ALL
SELECT r.SalesOrderID, r.CustomerID, r.OrderDate, r.TotalDue,
MaxTotalDue = CASE
WHEN r.TotalDue > cte.MaxTotalDue THEN r.TotalDue
ELSE cte.MaxTotalDue
END
FROM cte
CROSS APPLY
(
SELECT SalesOrderID, CustomerID, OrderDate, TotalDue,
rn = ROW_NUMBER() OVER (PARTITION BY CustomerID ORDER BY SalesOrderID)
FROM Sales.SalesOrderHeader AS h
WHERE h.CustomerID = cte.CustomerID
AND h.SalesOrderID > cte.SalesOrderID
) AS r
WHERE r.rn = 1
)
SELECT SalesOrderID, CustomerID, OrderDate, TotalDue, MaxTotalDue
FROM cte
ORDER BY CustomerID, SalesOrderID
OPTION (MAXRECURSION 0); आप तुरंत देख सकते हैं कि योजना पिछले दो की तुलना में अधिक जटिल है, जो कि अधिक जटिल प्रश्न को देखते हुए आश्चर्यजनक नहीं है:
रिकर्सिव सीटीई के लिए निष्पादन योजना (विस्तार करने के लिए क्लिक करें)
कुछ खराब अनुमानों के कारण, हम एक साथ की खोज के साथ एक इंडेक्स की तलाश करते हैं, जिसे शायद दोनों को एक ही स्कैन से बदल दिया जाना चाहिए था, और हमें एक सॉर्ट ऑपरेशन भी मिलता है जिसे अंततः tempdb पर फैलाने की आवश्यकता होती है (आप इसे टूलटिप में देख सकते हैं) यदि आप चेतावनी आइकन के साथ सॉर्ट ऑपरेटर पर होवर करते हैं):

MAX() ओवर (पंक्तियां अनबाउंडेड)
यह एक समाधान केवल SQL Server 2012 और उच्चतर में उपलब्ध है, क्योंकि यह विंडो फ़ंक्शंस के लिए नए पेश किए गए एक्सटेंशन का उपयोग करता है।
SELECT /* MAX() OVER() */ SalesOrderID, CustomerID, OrderDate, TotalDue,
MaxTotalDue = MAX(TotalDue) OVER
(
PARTITION BY CustomerID ORDER BY SalesOrderID
ROWS UNBOUNDED PRECEDING
)
FROM Sales.SalesOrderHeader
ORDER BY CustomerID, SalesOrderID; योजना वास्तव में दिखाती है कि यह अन्य सभी की तुलना में बेहतर क्यों है; इसमें केवल एक क्लस्टर इंडेक्स स्कैन ऑपरेशन होता है, दो के विपरीत (या पुनरावर्ती सीटीई के मामले में स्कैन और सीक + लुकअप का खराब विकल्प):
MAX() OVER() के लिए निष्पादन योजना (विस्तार करने के लिए क्लिक करें)
प्रदर्शन तुलना
योजनाएं निश्चित रूप से हमें विश्वास दिलाती हैं कि नया MAX() OVER() SQL सर्वर 2012 में क्षमता एक वास्तविक विजेता है, लेकिन मूर्त रनटाइम मेट्रिक्स के बारे में कैसे? यहां बताया गया है कि निष्पादन की तुलना कैसे की जाती है:

पहले दो प्रश्न लगभग समान थे; जबकि इस मामले में CROSS APPLY एक छोटे से अंतर से समग्र अवधि के संदर्भ में बेहतर था, सहसंबद्ध उपश्रेणी कभी-कभी इसके बजाय इसे थोड़ा पीछे कर देती है। रिकर्सिव सीटीई हर बार काफी धीमा होता है, और आप इसमें योगदान करने वाले कारकों को देख सकते हैं - अर्थात्, खराब अनुमान, भारी मात्रा में रीड, कुंजी लुकअप, और अतिरिक्त सॉर्ट ऑपरेशन। और जैसा कि मैंने पहले रनिंग टोटल के साथ प्रदर्शित किया है, SQL Server 2012 समाधान लगभग हर पहलू में बेहतर है।
निष्कर्ष
यदि आप SQL सर्वर 2012 या उससे अधिक पर हैं, तो आप निश्चित रूप से SQL सर्वर 2005 में पहली बार पेश किए गए विंडोिंग फ़ंक्शंस के सभी एक्सटेंशन से परिचित होना चाहते हैं - वे कोड को फिर से देखने पर आपको कुछ गंभीर प्रदर्शन बढ़ा सकते हैं जो अभी भी चल रहा है " पुराना तरीका।" यदि आप इनमें से कुछ नई क्षमताओं के बारे में अधिक जानना चाहते हैं, तो मैं इट्ज़िक बेन-गण की पुस्तक, माइक्रोसॉफ्ट एसक्यूएल सर्वर 2012 हाई-परफॉर्मेंस टी-एसक्यूएल यूजिंग विंडो फंक्शंस की अत्यधिक अनुशंसा करता हूं।
यदि आप अभी तक SQL Server 2012 पर नहीं हैं, तो कम से कम इस परीक्षण में, आप CROSS APPLY के बीच चयन कर सकते हैं और सहसंबद्ध सबक्वेरी। हमेशा की तरह, आपको अपने हार्डवेयर पर अपने डेटा के विरुद्ध विभिन्न विधियों का परीक्षण करना चाहिए।