Sqlserver
 sql >> Baza danych >  >> RDS >> Sqlserver

Oblicz sumę bieżącą w SQL Server

Aktualizacja , jeśli używasz SQL Server 2012, zobacz:https://stackoverflow.com/a/10309947

Problem polega na tym, że implementacja klauzuli Over w SQL Server jest nieco ograniczona.

Oracle (i ANSI-SQL) pozwalają robić takie rzeczy jak:

 SELECT somedate, somevalue,
  SUM(somevalue) OVER(ORDER BY somedate 
     ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) 
          AS RunningTotal
  FROM Table

SQL Server nie daje czystego rozwiązania tego problemu. Moje przeczucie podpowiada mi, że jest to jeden z tych rzadkich przypadków, w których kursor jest najszybszy, chociaż będę musiał przeprowadzić analizę porównawczą dużych wyników.

Sztuczka z aktualizacją jest przydatna, ale wydaje mi się, że jest dość delikatna. Wygląda na to, że jeśli aktualizujesz pełną tabelę, będzie ona postępować w kolejności klucza podstawowego. Więc jeśli ustawisz datę jako klucz główny rosnąco, probably bądź bezpieczny. Ale polegasz na nieudokumentowanych szczegółach implementacji SQL Server (również jeśli zapytanie zostanie wykonane przez dwie procedury, zastanawiam się, co się stanie, zobacz:MAXDOP):

Pełna próbka robocza:

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

Poprosiłeś o benchmark, to jest dół.

Najszybszym BEZPIECZNYM sposobem wykonania tego byłby kursor, który jest o rząd wielkości szybszy niż skorelowane podzapytanie połączenia krzyżowego.

Absolutnie najszybszym sposobem jest sztuczka UPDATE. Moim jedynym zmartwieniem jest to, że nie jestem pewien, czy w każdych okolicznościach aktualizacja będzie przebiegać liniowo. W zapytaniu nie ma nic, co by to wyraźnie mówiło.

Podsumowując, dla kodu produkcyjnego poszedłbym z kursorem.

Dane testowe:

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

Test 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 

Test 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

Test 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

Test 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


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Tworzenie tabeli SQL Server z C# datatable

  2. Błąd serwera SQL 113:Brak znaku komentarza końcowego „*/”

  3. Jaka jest prawidłowa nazwa tabeli asocjacji (relacja wiele-do-wielu)

  4. Jak korzystać z wyszukiwania semantycznego w SQL Server

  5. Jak wyświetlić sortowanie kolumny w SQL Server (T-SQL)