यदि आपको डेटा स्टोर करने की आवश्यकता नहीं है (जो आपको नहीं करना चाहिए, क्योंकि आपको किसी भी पंक्ति को बदलने, जोड़े जाने या हटाए जाने पर किसी भी समय रनिंग टोटल को अपडेट करने की आवश्यकता होती है), और यदि आपको विचित्र अपडेट पर भरोसा नहीं है (जो आप नहीं होना चाहिए, क्योंकि यह काम करने की गारंटी नहीं है और इसका व्यवहार हॉटफिक्स, सर्विस पैक, अपग्रेड, या यहां तक कि एक अंतर्निहित इंडेक्स या आंकड़े परिवर्तन के साथ बदल सकता है), आप रनटाइम पर इस प्रकार की क्वेरी को आजमा सकते हैं। यह एक तरीका है साथी एमवीपी ह्यूगो कॉर्नेलिस ने "सेट-आधारित पुनरावृत्ति" गढ़ा है (उन्होंने के अपने एक अध्याय में कुछ ऐसा ही पोस्ट किया है। SQL सर्वर MVP डीप डाइव्स ) चूंकि रनिंग टोटल के लिए आम तौर पर पूरे सेट पर एक कर्सर की आवश्यकता होती है, पूरे सेट पर एक विचित्र अपडेट, या एक सिंगल नॉन-लीनियर सेल्फ-जॉइन जो पंक्ति की संख्या बढ़ने के साथ-साथ अधिक से अधिक महंगा हो जाता है, यहाँ ट्रिक कुछ परिमित के माध्यम से लूप करना है सेट में तत्व (इस मामले में, प्रत्येक उपयोगकर्ता के लिए महीने के संदर्भ में प्रत्येक पंक्ति की "रैंक" - और आप उस रैंक पर सभी उपयोगकर्ता/माह संयोजनों के लिए केवल एक बार प्रत्येक रैंक को संसाधित करते हैं, इसलिए 200,000 पंक्तियों के माध्यम से लूपिंग के बजाय, आप 24 बार लूप करते हैं)।
DECLARE @t TABLE
(
[user_id] INT,
[month] TINYINT,
total DECIMAL(10,1),
RunningTotal DECIMAL(10,1),
Rnk INT
);
INSERT @t SELECT [user_id], [month], total, total,
RANK() OVER (PARTITION BY [user_id] ORDER BY [month])
FROM dbo.my_table;
DECLARE @rnk INT = 1, @rc INT = 1;
WHILE @rc > 0
BEGIN
SET @rnk += 1;
UPDATE c SET RunningTotal = p.RunningTotal + c.total
FROM @t AS c INNER JOIN @t AS p
ON c.[user_id] = p.[user_id]
AND p.rnk = @rnk - 1
AND c.rnk = @rnk;
SET @rc = @@ROWCOUNT;
END
SELECT [user_id], [month], total, RunningTotal
FROM @t
ORDER BY [user_id], rnk;
परिणाम:
user_id month total RunningTotal
------- ----- ----- ------------
1 1 2.0 2.0
1 2 1.0 3.0
1 3 3.5 6.5 -- I think your calculation is off
2 1 0.5 0.5
2 2 1.5 2.0
2 3 2.0 4.0
बेशक आप कर सकते हैं इस तालिका चर से आधार तालिका को अपडेट करें, लेकिन परेशान क्यों हों, क्योंकि वे संग्रहीत मान केवल तभी तक अच्छे हैं जब तक कि अगली बार तालिका को किसी DML कथन द्वारा स्पर्श नहीं किया जाता है?
UPDATE mt
SET cumulative_total = t.RunningTotal
FROM dbo.my_table AS mt
INNER JOIN @t AS t
ON mt.[user_id] = t.[user_id]
AND mt.[month] = t.[month];
चूंकि हम किसी भी प्रकार के निहित आदेश पर भरोसा नहीं कर रहे हैं, यह 100% समर्थित है और असमर्थित विचित्र अद्यतन के सापेक्ष प्रदर्शन तुलना के योग्य है। यहां तक कि अगर यह इसे हरा नहीं देता है लेकिन करीब आता है, तो आपको इसे आईएमएचओ का उपयोग करने पर विचार करना चाहिए।
SQL सर्वर 2012 समाधान के लिए, मैट ने RANGE
. का उल्लेख किया है लेकिन चूंकि यह विधि ऑन-डिस्क स्पूल का उपयोग करती है, इसलिए आपको ROWS
. के साथ भी परीक्षण करना चाहिए केवल RANGE
. के साथ चलने के बजाय . आपके मामले के लिए यहां एक त्वरित उदाहरण दिया गया है:
SELECT
[user_id],
[month],
total,
RunningTotal = SUM(total) OVER
(
PARTITION BY [user_id]
ORDER BY [month] ROWS UNBOUNDED PRECEDING
)
FROM dbo.my_table
ORDER BY [user_id], [month];
इसकी तुलना RANGE UNBOUNDED PRECEDING
. से करें या नहीं ROWS\RANGE
बिल्कुल भी (जो RANGE
. का भी उपयोग करेगा ऑन-डिस्क स्पूल)। उपरोक्त की कुल अवधि कम होगी और रास्ता कम I/O, भले ही योजना थोड़ी अधिक जटिल (एक अतिरिक्त अनुक्रम प्रोजेक्ट ऑपरेटर) दिखती हो।
मैंने हाल ही में एक विशिष्ट रनिंग टोटल परिदृश्य के लिए देखे गए कुछ प्रदर्शन अंतरों को रेखांकित करते हुए एक ब्लॉग पोस्ट प्रकाशित किया है:
http://www.sqlperformance.com/2012/07 /t-sql-queries/running-total