sql >> Databáze >  >> RDS >> Sqlserver

Vypočítat průběžný součet v SQL Server

Aktualizovat , pokud používáte SQL Server 2012, viz:https://stackoverflow.com/a/10309947

Problém je v tom, že implementace klauzule Over SQL Server je poněkud omezená.

Oracle (a ANSI-SQL) vám umožňují dělat věci jako:

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

SQL Server vám neposkytuje žádné čisté řešení tohoto problému. Moje nitro mi říká, že toto je jeden z těch vzácných případů, kdy je kurzor nejrychlejší, i když u velkých výsledků budu muset provést nějaké srovnání.

Aktualizační trik je praktický, ale mám pocit, že je poměrně křehký. Zdá se, že pokud aktualizujete úplnou tabulku, bude to pokračovat v pořadí primárního klíče. Pokud tedy nastavíte datum jako primární klíč vzestupně, probably být v bezpečí. Ale spoléháte se na nezdokumentovaný detail implementace SQL Serveru (také pokud dotaz skončí prováděný dvěma procesy, zajímalo by mě, co se stane, viz:MAXDOP):

Kompletní pracovní ukázka:

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

Ptali jste se na benchmark, toto je lowdown.

Nejrychlejším BEZPEČNÝM způsobem, jak toho dosáhnout, by byl kurzor, je řádově rychlejší než korelovaný dílčí dotaz cross-join.

Absolutně nejrychlejší způsob je trik UPDATE. Zajímá mě pouze to, že si nejsem jistý, že za všech okolností bude aktualizace probíhat lineárně. V dotazu není nic, co by to výslovně říkalo.

Sečteno a podtrženo, pro výrobní kód bych šel s kurzorem.

Testovací data:

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. Oracle číslo na C# desítkové

  2. Podmíněné pořadí podle

  3. Co je MEZI logickým operátorem v SQL Server - SQL Server / Výukový program TSQL, část 124

  4. Co je schéma v SQL a jak jej vytvořit?