Оновлення , якщо ви використовуєте SQL Server 2012, див. Https://stackoverflow.com/a/10309947
Проблема полягає в тому, що реалізація пункту Over на SQL Server дещо обмежена .
Oracle (і ANSI-SQL) дозволяють робити такі речі, як:
SELECT somedate, somevalue,
SUM(somevalue) OVER(ORDER BY somedate
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
AS RunningTotal
FROM Table
SQL Server не дає чіткого рішення цієї проблеми. Моя кишка говорить мені, що це один з тих рідкісних випадків, коли курсор найшвидший, хоча мені доведеться зробити певний тест на великі результати.
Трюк оновлення зручний, але я вважаю його досить крихким. Здається, що якщо ви оновлюєте повну таблицю, то вона пройде в порядку первинного ключа. Тож якщо ви встановите дату в якості основного ключа у порядку зростання, ви будете в probably
безпеці. Але ви покладаєтесь на недокументовану деталь реалізації SQL Server (також якщо запит виконується двома документами, мені цікаво, що буде, див. MAXDOP):
Повний робочий зразок:
drop table #t
create table #t ( ord int primary key, total int, running_total int)
insert #t(ord,total) values (2,20)
-- notice the malicious re-ordering
insert #t(ord,total) values (1,10)
insert #t(ord,total) values (3,10)
insert #t(ord,total) values (4,1)
declare @total int
set @total = 0
update #t set running_total = @total, @total = @total + total
select * from #t
order by ord
ord total running_total
----------- ----------- -------------
1 10 10
2 20 30
3 10 40
4 1 41
Ви попросили орієнтир, це низький рівень.
Найшвидший БЕЗПЕЧНИЙ спосіб зробити це курсор, це на порядок швидше, ніж корельований підзапит на перехресне з'єднання.
Абсолютний найшвидший спосіб - трюк UPDATE. Моє єдине занепокоєння з цього приводу полягає в тому, що я не впевнений, що за будь-яких обставин оновлення буде проходити лінійно. У запиті немає нічого прямо сказаного.
Підсумок, для виробничого коду я б пішов курсором.
Дані тесту:
create table #t ( ord int primary key, total int, running_total int)
set nocount on
declare @i int
set @i = 0
begin tran
while @i < 10000
begin
insert #t (ord, total) values (@i, rand() * 100)
set @i = @i +1
end
commit
Тест 1:
SELECT ord,total,
(SELECT SUM(total)
FROM #t b
WHERE b.ord <= a.ord) AS b
FROM #t a
-- CPU 11731, Reads 154934, Duration 11135
Тест 2:
SELECT a.ord, a.total, SUM(b.total) AS RunningTotal
FROM #t a CROSS JOIN #t b
WHERE (b.ord <= a.ord)
GROUP BY a.ord,a.total
ORDER BY a.ord
-- CPU 16053, Reads 154935, Duration 4647
Тест 3:
DECLARE @TotalTable table(ord int primary key, total int, running_total int)
DECLARE forward_cursor CURSOR FAST_FORWARD
FOR
SELECT ord, total
FROM #t
ORDER BY ord
OPEN forward_cursor
DECLARE @running_total int,
@ord int,
@total int
SET @running_total = 0
FETCH NEXT FROM forward_cursor INTO @ord, @total
WHILE (@@FETCH_STATUS = 0)
BEGIN
SET @running_total = @running_total + @total
INSERT @TotalTable VALUES(@ord, @total, @running_total)
FETCH NEXT FROM forward_cursor INTO @ord, @total
END
CLOSE forward_cursor
DEALLOCATE forward_cursor
SELECT * FROM @TotalTable
-- CPU 359, Reads 30392, Duration 496
Тест 4:
declare @total int
set @total = 0
update #t set running_total = @total, @total = @total + total
select * from #t
-- CPU 0, Reads 58, Duration 139