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

रनिंग टोटल / रनिंग बैलेंस की गणना करें

SQL सर्वर 2012 या इसके बाद के संस्करण का उपयोग नहीं करने वालों के लिए, एक कर्सर संभवतः सबसे कुशल समर्थित है और गारंटीकृत सीएलआर के बाहर विधि। "विचित्र अद्यतन" जैसे अन्य दृष्टिकोण हैं जो थोड़े तेज़ हो सकते हैं लेकिन भविष्य में काम करने की गारंटी नहीं है, और निश्चित रूप से हाइपरबोलिक प्रदर्शन प्रोफाइल के साथ सेट-आधारित दृष्टिकोण जैसे तालिका बड़ी हो जाती है, और पुनरावर्ती सीटीई विधियों को अक्सर प्रत्यक्ष की आवश्यकता होती है #tempdb I/O या स्पिल में परिणाम जो लगभग समान प्रभाव उत्पन्न करते हैं।

INNER JOIN - ऐसा न करें:

धीमा, सेट-आधारित दृष्टिकोण इस प्रकार का है:

SELECT t1.TID, t1.amt, RunningTotal = SUM(t2.amt)
FROM dbo.Transactions AS t1
INNER JOIN dbo.Transactions AS t2
  ON t1.TID >= t2.TID
GROUP BY t1.TID, t1.amt
ORDER BY t1.TID;

कारण यह धीमा है? जैसे-जैसे तालिका बड़ी होती जाती है, प्रत्येक वृद्धिशील पंक्ति को तालिका में n-1 पंक्तियों को पढ़ने की आवश्यकता होती है। यह घातीय है और विफलताओं, समयबाह्य, या केवल नाराज उपयोगकर्ताओं के लिए बाध्य है।

सहसंबंधित सबक्वेरी - ऐसा भी न करें:

इसी तरह के दर्दनाक कारणों के लिए सबक्वेरी फॉर्म समान रूप से दर्दनाक है।

SELECT TID, amt, RunningTotal = amt + COALESCE(
(
  SELECT SUM(amt)
    FROM dbo.Transactions AS i
    WHERE i.TID < o.TID), 0
)
FROM dbo.Transactions AS o
ORDER BY TID;

अजीब अपडेट - इसे अपने जोखिम पर करें:

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

DECLARE @t TABLE
(
  TID INT PRIMARY KEY,
  amt INT,
  RunningTotal INT
);
 
DECLARE @RunningTotal INT = 0;
 
INSERT @t(TID, amt, RunningTotal)
  SELECT TID, amt, RunningTotal = 0
  FROM dbo.Transactions
  ORDER BY TID;
 
UPDATE @t
  SET @RunningTotal = RunningTotal = @RunningTotal + amt
  FROM @t;
 
SELECT TID, amt, RunningTotal
  FROM @t
  ORDER BY TID;

पुनरावर्ती सीटीई

यह पहला टीआईडी ​​पर निर्भर करता है ताकि वह सन्निहित हो, कोई अंतराल न हो:

;WITH x AS
(
  SELECT TID, amt, RunningTotal = amt
    FROM dbo.Transactions
    WHERE TID = 1
  UNION ALL
  SELECT y.TID, y.amt, x.RunningTotal + y.amt
   FROM x 
   INNER JOIN dbo.Transactions AS y
   ON y.TID = x.TID + 1
)
SELECT TID, amt, RunningTotal
  FROM x
  ORDER BY TID
  OPTION (MAXRECURSION 10000);

यदि आप इस पर भरोसा नहीं कर सकते हैं, तो आप इस भिन्नता का उपयोग कर सकते हैं, जो ROW_NUMBER() का उपयोग करके बस एक सन्निहित अनुक्रम बनाता है। :

;WITH y AS 
(
  SELECT TID, amt, rn = ROW_NUMBER() OVER (ORDER BY TID)
    FROM dbo.Transactions
), x AS
(
    SELECT TID, rn, amt, rt = amt
      FROM y
      WHERE rn = 1
    UNION ALL
    SELECT y.TID, y.rn, y.amt, x.rt + y.amt
      FROM x INNER JOIN y
      ON y.rn = x.rn + 1
)
SELECT TID, amt, RunningTotal = rt
  FROM x
  ORDER BY x.rn
  OPTION (MAXRECURSION 10000);

डेटा के आकार के आधार पर (उदाहरण के लिए जिन कॉलमों के बारे में हम नहीं जानते हैं), आप संबंधित कॉलम को पहले केवल #temp तालिका में भरकर और बेस टेबल के बजाय उसके विरुद्ध प्रोसेस करके बेहतर समग्र प्रदर्शन पा सकते हैं:

CREATE TABLE #x
(
  rn  INT PRIMARY KEY,
  TID INT,
  amt INT
);

INSERT INTO #x (rn, TID, amt)
SELECT ROW_NUMBER() OVER (ORDER BY TID),
  TID, amt
FROM dbo.Transactions;

;WITH x AS
(
  SELECT TID, rn, amt, rt = amt
    FROM #x
    WHERE rn = 1
  UNION ALL
  SELECT y.TID, y.rn, y.amt, x.rt + y.amt
    FROM x INNER JOIN #x AS y
    ON y.rn = x.rn + 1
)
SELECT TID, amt, RunningTotal = rt
  FROM x
  ORDER BY TID
  OPTION (MAXRECURSION 10000);

DROP TABLE #x;

केवल पहली सीटीई पद्धति विचित्र अद्यतन के मुकाबले प्रदर्शन प्रदान करेगी, लेकिन यह डेटा की प्रकृति (कोई अंतराल नहीं) के बारे में एक बड़ी धारणा बनाती है। अन्य दो विधियां वापस आ जाएंगी और उन मामलों में आप एक कर्सर का भी उपयोग कर सकते हैं (यदि आप सीएलआर का उपयोग नहीं कर सकते हैं और आप अभी तक SQL सर्वर 2012 या इसके बाद के संस्करण पर नहीं हैं)।

कर्सर

हर किसी को बताया जाता है कि शाप देने वाले बुरे होते हैं, और उन्हें हर कीमत पर टाला जाना चाहिए, लेकिन यह वास्तव में अधिकांश अन्य समर्थित तरीकों के प्रदर्शन को मात देता है, और विचित्र अद्यतन की तुलना में सुरक्षित है। कर्सर समाधान पर मैं केवल 2012 और सीएलआर विधियां (नीचे) पसंद करता हूं:

CREATE TABLE #x
(
  TID INT PRIMARY KEY, 
  amt INT, 
  rt INT
);

INSERT #x(TID, amt) 
  SELECT TID, amt
  FROM dbo.Transactions
  ORDER BY TID;

DECLARE @rt INT, @tid INT, @amt INT;
SET @rt = 0;

DECLARE c CURSOR LOCAL STATIC READ_ONLY FORWARD_ONLY
  FOR SELECT TID, amt FROM #x ORDER BY TID;

OPEN c;

FETCH c INTO @tid, @amt;

WHILE @@FETCH_STATUS = 0
BEGIN
  SET @rt = @rt + @amt;
  UPDATE #x SET rt = @rt WHERE TID = @tid;
  FETCH c INTO @tid, @amt;
END

CLOSE c; DEALLOCATE c;

SELECT TID, amt, RunningTotal = rt 
  FROM #x 
  ORDER BY TID;

DROP TABLE #x;

SQL सर्वर 2012 या इसके बाद के संस्करण

SQL सर्वर 2012 में पेश किए गए नए विंडो फ़ंक्शन इस कार्य को बहुत आसान बनाते हैं (और यह उपरोक्त सभी विधियों से भी बेहतर प्रदर्शन करता है):

SELECT TID, amt, 
  RunningTotal = SUM(amt) OVER (ORDER BY TID ROWS UNBOUNDED PRECEDING)
FROM dbo.Transactions
ORDER BY TID;

ध्यान दें कि बड़े डेटा सेट पर, आप पाएंगे कि उपरोक्त दो विकल्पों में से किसी एक से बेहतर प्रदर्शन करता है, क्योंकि RANGE ऑन-डिस्क स्पूल का उपयोग करता है (और डिफ़ॉल्ट RANGE का उपयोग करता है)। हालांकि यह भी ध्यान रखना महत्वपूर्ण है कि व्यवहार और परिणाम भिन्न हो सकते हैं, इसलिए सुनिश्चित करें कि वे दोनों इस अंतर के आधार पर निर्णय लेने से पहले सही परिणाम देते हैं।

SELECT TID, amt, 
  RunningTotal = SUM(amt) OVER (ORDER BY TID)
FROM dbo.Transactions
ORDER BY TID;

SELECT TID, amt, 
  RunningTotal = SUM(amt) OVER (ORDER BY TID RANGE UNBOUNDED PRECEDING)
FROM dbo.Transactions
ORDER BY TID;

सीएलआर

पूर्णता के लिए, मैं Pavel Pawlowski की CLR पद्धति के लिए एक लिंक की पेशकश कर रहा हूं, जो कि SQL Server 2012 (लेकिन स्पष्ट रूप से 2000 नहीं) से पहले के संस्करणों पर बेहतर तरीका है।

http://www.pawlowski.cz/2010/09/sql-server-and-fastest-running-totals-using-clr/

निष्कर्ष

यदि आप SQL सर्वर 2012 या इसके बाद के संस्करण पर हैं, तो विकल्प स्पष्ट है - नए SUM() OVER() का उपयोग करें निर्माण (ROWS . के साथ) बनाम RANGE ) पुराने संस्करणों के लिए, आप अपने स्कीमा, डेटा पर वैकल्पिक दृष्टिकोणों के प्रदर्शन की तुलना करना चाहेंगे और - गैर-प्रदर्शन-संबंधी कारकों को ध्यान में रखते हुए - निर्धारित करें कि आपके लिए कौन सा दृष्टिकोण सही है। यह बहुत अच्छी तरह से सीएलआर दृष्टिकोण हो सकता है। वरीयता क्रम में मेरी सिफारिशें यहां दी गई हैं:

  1. SUM() OVER() ... ROWS , यदि 2012 या उससे अधिक पर
  2. सीएलआर विधि, यदि संभव हो तो
  3. पहली पुनरावर्ती सीटीई विधि, यदि संभव हो तो
  4. कर्सर
  5. अन्य पुनरावर्ती सीटीई विधियां
  6. अजीब अपडेट
  7. शामिल हों और/या सहसंबद्ध सबक्वेरी

इन विधियों के प्रदर्शन की तुलना के बारे में अधिक जानकारी के लिए, इस प्रश्न को http://dba.stackexchange.com पर देखें:

https://dba.stackexchange.com/questions/19507/running-total-with-count

मैंने यहां इन तुलनाओं के बारे में अधिक विवरण ब्लॉग किया है:

http://www.sqlperformance.com/2012/07/t-sql-queries/running-totals

इसके अलावा समूहीकृत/विभाजित चल रहे योगों के लिए, निम्नलिखित पोस्ट देखें:

http://sqlperformance.com/2014/01/t-sql-queries/grouped-running-totals

कुल योग क्वेरी में विभाजन परिणाम

ग्रुप बाय के साथ मल्टीपल रनिंग टोटल



  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. टी-एसक्यूएल:गोल से निकटतम 15 मिनट के अंतराल तक

  3. SQL सर्वर:DELETE बनाम TRUNCATE

  4. SQL सर्वर के साथ जाओ ड्राइवर सफलतापूर्वक कनेक्ट करने में असमर्थ है, लॉगिन विफल

  5. प्रत्येक पंक्ति में स्तंभ मान के आधार पर पंक्तियों की नकल करना