Nếu bạn không cần LƯU TRỮ dữ liệu (điều bạn không nên làm, vì bạn cần cập nhật tổng số đang chạy bất kỳ lúc nào bất kỳ hàng nào được thay đổi, thêm hoặc xóa) và nếu bạn không tin tưởng vào bản cập nhật kỳ quặc (mà bạn không nên, vì nó không được đảm bảo hoạt động và hành vi của nó có thể thay đổi với một hotfix, gói dịch vụ, nâng cấp hoặc thậm chí thay đổi chỉ mục hoặc thống kê cơ bản), bạn có thể thử loại truy vấn này trong thời gian chạy. Đây là một phương pháp mà đồng nghiệp MVP Hugo Kornelis đặt ra "lặp lại dựa trên bộ" (anh ấy đã đăng nội dung tương tự trong một trong các chương của mình của Tìm hiểu sâu về MVP của SQL Server ). Vì việc chạy tổng thường yêu cầu một con trỏ trên toàn bộ tập hợp, một bản cập nhật kỳ quặc trên toàn bộ tập hợp hoặc một phép tự liên kết phi tuyến tính đơn lẻ ngày càng trở nên đắt hơn khi số hàng tăng lên, mẹo ở đây là lặp lại một số phần tử trong tập hợp (trong trường hợp này là "xếp hạng" của mỗi hàng theo tháng, cho mỗi người dùng - và bạn chỉ xử lý mỗi xếp hạng một lần cho tất cả các kết hợp người dùng / tháng ở xếp hạng đó, vì vậy thay vì lặp lại 200.000 hàng, bạn lặp lại tối đa 24 lần).
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;
Kết quả:
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
Tất nhiên bạn có thể cập nhật bảng cơ sở từ biến bảng này, nhưng tại sao phải bận tâm, vì các giá trị được lưu trữ đó chỉ tốt cho đến lần tiếp theo bảng được chạm vào bởi bất kỳ câu lệnh DML nào?
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];
Vì chúng tôi không dựa vào thứ tự ngầm dưới bất kỳ hình thức nào, nên điều này được hỗ trợ 100% và xứng đáng được so sánh hiệu suất so với bản cập nhật kỳ quặc không được hỗ trợ. Ngay cả khi nó không đánh bại nó nhưng đến gần, bạn vẫn nên cân nhắc sử dụng nó IMHO.
Đối với giải pháp SQL Server 2012, Matt đề cập đến RANGE
nhưng vì phương pháp này sử dụng bộ đệm trên đĩa nên bạn cũng nên kiểm tra với ROWS
thay vì chỉ chạy với RANGE
. Dưới đây là một ví dụ nhanh cho trường hợp của bạn:
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];
So sánh điều này với RANGE UNBOUNDED PRECEDING
hoặc không có ROWS\RANGE
ở tất cả (cũng sẽ sử dụng RANGE
trên đĩa đệm). Ở trên sẽ có thời lượng tổng thể thấp hơn và cách I / O ít hơn, mặc dù kế hoạch có vẻ phức tạp hơn một chút (một nhà điều hành dự án trình tự bổ sung).
Gần đây, tôi đã xuất bản một bài đăng trên blog phác thảo một số khác biệt về hiệu suất mà tôi đã quan sát được đối với một kịch bản tổng số đang chạy cụ thể:
http://www.sqlperformance.com/2012/07 / t-sql-queries / running-totals