SQL सर्वर में चल रहे योग की गणना करने का एक बेहतरीन संसाधन यह दस्तावेज़
है इट्ज़िक बेन गण द्वारा जो कि SQL सर्वर टीम को OVER
प्राप्त करने के उनके अभियान के हिस्से के रूप में सबमिट किया गया था क्लॉज को इसके प्रारंभिक SQL सर्वर 2005 कार्यान्वयन से आगे बढ़ाया गया। इसमें वह दिखाता है कि एक बार जब आप हजारों पंक्तियों में पहुंच जाते हैं तो कर्सर सेट आधारित समाधान करते हैं। SQL सर्वर 2012 ने वास्तव में OVER
. का विस्तार किया था इस प्रकार की क्वेरी को अधिक आसान बनाने वाला खंड।
SELECT col1,
SUM(col1) OVER (ORDER BY ind ROWS UNBOUNDED PRECEDING)
FROM @tmp
चूंकि आप SQL सर्वर 2005 पर हैं, हालांकि यह आपके लिए उपलब्ध नहीं है।
एडम मचानिक यहां दिखाता है मानक TSQL कर्सर के प्रदर्शन में सुधार के लिए CLR का उपयोग कैसे किया जा सकता है।
इस तालिका परिभाषा के लिए
CREATE TABLE RunningTotals
(
ind int identity(1,1) primary key,
col1 int
)
मैं ALLOW_SNAPSHOT_ISOLATION ON
वाले डेटाबेस में 2,000 और 10,000 दोनों पंक्तियों के साथ टेबल बनाता हूं और इस सेटिंग के साथ एक (इसका कारण यह है कि मेरे शुरुआती परिणाम एक डीबी में थे, जिस पर सेटिंग के साथ परिणामों का एक हैरान करने वाला पहलू था)।
सभी तालिकाओं के लिए संकुल अनुक्रमणिका में केवल 1 मूल पृष्ठ था। प्रत्येक के लिए पत्ती पृष्ठों की संख्या नीचे दिखाई गई है।
+-------------------------------+-----------+------------+
| | 2,000 row | 10,000 row |
+-------------------------------+-----------+------------+
| ALLOW_SNAPSHOT_ISOLATION OFF | 5 | 22 |
| ALLOW_SNAPSHOT_ISOLATION ON | 8 | 39 |
+-------------------------------+-----------+------------+
मैंने निम्नलिखित मामलों का परीक्षण किया (लिंक निष्पादन योजनाएँ दिखाते हैं)
- लेफ्ट जॉइन और ग्रुप बाय
- सहसंबंधित सबक्वेरी 2000 पंक्ति योजना ,10000 पंक्ति योजना
- Mikael's (अपडेटेड) उत्तर से CTE
- नीचे सीटीई
अतिरिक्त सीटीई विकल्प को शामिल करने का कारण एक सीटीई समाधान प्रदान करना था जो तब भी काम करेगा जब ind
कॉलम की क्रमिक गारंटी नहीं थी।
SET STATISTICS IO ON;
SET STATISTICS TIME ON;
DECLARE @col1 int, @sumcol1 bigint;
WITH RecursiveCTE
AS (
SELECT TOP 1 ind, col1, CAST(col1 AS BIGINT) AS Total
FROM RunningTotals
ORDER BY ind
UNION ALL
SELECT R.ind, R.col1, R.Total
FROM (
SELECT T.*,
T.col1 + Total AS Total,
rn = ROW_NUMBER() OVER (ORDER BY T.ind)
FROM RunningTotals T
JOIN RecursiveCTE R
ON R.ind < T.ind
) R
WHERE R.rn = 1
)
SELECT @col1 =col1, @sumcol1=Total
FROM RecursiveCTE
OPTION (MAXRECURSION 0);
सभी प्रश्नों में CAST(col1 AS BIGINT)
. था रनटाइम पर अतिप्रवाह त्रुटियों से बचने के लिए जोड़ा गया। इसके अतिरिक्त उन सभी के लिए मैंने परिणामों को ऊपर के रूप में चरों को सौंपा ताकि विचार से परिणाम वापस भेजने में लगने वाले समय को समाप्त किया जा सके।
परिणाम
+------------------+----------+--------+------------+---------------+------------+---------------+-------+---------+
| | | | Base Table | Work Table | Time |
+------------------+----------+--------+------------+---------------+------------+---------------+-------+---------+
| | Snapshot | Rows | Scan count | logical reads | Scan count | logical reads | cpu | elapsed |
| Group By | On | 2,000 | 2001 | 12709 | | | 1469 | 1250 |
| | On | 10,000 | 10001 | 216678 | | | 30906 | 30963 |
| | Off | 2,000 | 2001 | 9251 | | | 1140 | 1160 |
| | Off | 10,000 | 10001 | 130089 | | | 29906 | 28306 |
+------------------+----------+--------+------------+---------------+------------+---------------+-------+---------+
| Sub Query | On | 2,000 | 2001 | 12709 | | | 844 | 823 |
| | On | 10,000 | 2 | 82 | 10000 | 165025 | 24672 | 24535 |
| | Off | 2,000 | 2001 | 9251 | | | 766 | 999 |
| | Off | 10,000 | 2 | 48 | 10000 | 165025 | 25188 | 23880 |
+------------------+----------+--------+------------+---------------+------------+---------------+-------+---------+
| CTE No Gaps | On | 2,000 | 0 | 4002 | 2 | 12001 | 78 | 101 |
| | On | 10,000 | 0 | 20002 | 2 | 60001 | 344 | 342 |
| | Off | 2,000 | 0 | 4002 | 2 | 12001 | 62 | 253 |
| | Off | 10,000 | 0 | 20002 | 2 | 60001 | 281 | 326 |
+------------------+----------+--------+------------+---------------+------------+---------------+-------+---------+
| CTE Alllows Gaps | On | 2,000 | 2001 | 4009 | 2 | 12001 | 47 | 75 |
| | On | 10,000 | 10001 | 20040 | 2 | 60001 | 312 | 413 |
| | Off | 2,000 | 2001 | 4006 | 2 | 12001 | 94 | 90 |
| | Off | 10,000 | 10001 | 20023 | 2 | 60001 | 313 | 349 |
+------------------+----------+--------+------------+---------------+------------+---------------+-------+---------+
सहसंबंधित सबक्वेरी और GROUP BY
. दोनों RunningTotals
पर क्लस्टर इंडेक्स स्कैन द्वारा संचालित "त्रिकोणीय" नेस्टेड लूप जॉइन का संस्करण उपयोग टेबल (T1
) और, उस स्कैन द्वारा लौटाई गई प्रत्येक पंक्ति के लिए, तालिका में वापस आना (T2
) T2.ind<=T1.ind
. पर सेल्फ जॉइनिंग ।
इसका मतलब है कि एक ही पंक्तियों को बार-बार संसाधित किया जाता है। जब T1.ind=1000
पंक्ति को संसाधित किया जाता है सेल्फ जॉइन पुनर्प्राप्त करता है और सभी पंक्तियों को ind <= 1000
. के साथ जोड़ता है , फिर अगली पंक्ति के लिए जहां T1.ind=1001
वही 1000 पंक्तियां फिर से प्राप्त की जाती हैं और एक अतिरिक्त पंक्ति वगैरह के साथ सारांशित किया गया।
2,000 पंक्ति तालिका के लिए ऐसे संचालन की कुल संख्या 2,001,000 है, 10k पंक्तियों के लिए 50,005,000 या अधिक आम तौर पर (n² + n) / 2
जो स्पष्ट रूप से तेजी से बढ़ता है।
2,000 पंक्ति के मामले में GROUP BY
. के बीच मुख्य अंतर और सबक्वेरी संस्करण यह है कि पूर्व में शामिल होने के बाद स्ट्रीम एग्रीगेट होता है और इसलिए इसमें तीन कॉलम फीड होते हैं (T1.ind
, T2.col1
, T2.col1
) और एक GROUP BY
T1.ind
. की संपत्ति जबकि बाद वाले की गणना एक अदिश समुच्चय के रूप में की जाती है, जिसमें शामिल होने से पहले स्ट्रीम समुच्चय के साथ, केवल T2.col1
होता है इसमें फीड कर रहा है और इसका कोई GROUP BY
नहीं है संपत्ति बिल्कुल सेट। कम CPU समय के संदर्भ में इस सरल व्यवस्था को मापने योग्य लाभ के रूप में देखा जा सकता है।
10,000 पंक्ति मामले के लिए उप क्वेरी योजना में एक अतिरिक्त अंतर है। यह एक उत्सुक स्पूल
जोड़ता है जो सभी ind,cast(col1 as bigint)
. को कॉपी करता है मान tempdb
. में . इस मामले में कि स्नैपशॉट अलगाव इस पर क्लस्टर इंडेक्स संरचना की तुलना में अधिक कॉम्पैक्ट काम करता है और शुद्ध प्रभाव लगभग 25% तक रीड की संख्या को कम करना है (क्योंकि बेस टेबल संस्करण जानकारी के लिए काफी खाली जगह को सुरक्षित रखता है), जब यह विकल्प बंद होता है तो यह कम कॉम्पैक्ट होता है (संभवतः bigint
. के कारण) बनाम int
अंतर) और अधिक परिणाम पढ़ता है। यह उप क्वेरी और समूह द्वारा संस्करणों के बीच के अंतर को कम करता है लेकिन उप क्वेरी अभी भी जीत जाती है।
हालांकि स्पष्ट विजेता रिकर्सिव सीटीई था। "नो गैप्स" संस्करण के लिए बेस टेबल से तार्किक रीड अब 2 x (n + 1)
हैं n
. को दर्शाता है सूचकांक सभी पंक्तियों को पुनः प्राप्त करने के लिए 2 स्तर के सूचकांक की तलाश करता है और अंत में अतिरिक्त एक जो कुछ भी नहीं देता है और रिकर्सन को समाप्त करता है। इसका मतलब अभी भी 20,002 पढ़ता है, हालांकि 22 पृष्ठ तालिका को संसाधित करता है!
पुनरावर्ती सीटीई संस्करण के लिए तार्किक कार्य तालिका बहुत अधिक है। ऐसा लगता है कि प्रति स्रोत पंक्ति 6 वर्कटेबल रीड पर काम करता है। ये इंडेक्स स्पूल से आते हैं जो पिछली पंक्ति के आउटपुट को स्टोर करता है और फिर अगले पुनरावृत्ति में पढ़ा जाता है (उमाचंदर जयचंद्रन द्वारा इसकी अच्छी व्याख्या यहां ) उच्च संख्या के बावजूद यह अभी भी सबसे अच्छा प्रदर्शन करने वाला है।