sql >> Database teknologi >  >> RDS >> Sqlserver

Beregn en løbende total i SQL Server

Opdater , hvis du kører SQL Server 2012, se:https://stackoverflow.com/a/10309947

Problemet er, at SQL Server-implementeringen af ​​Over-klausulen er noget begrænset.

Oracle (og ANSI-SQL) giver dig mulighed for at gøre ting som:

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

SQL Server giver dig ingen ren løsning på dette problem. Min mavefornemmelse fortæller mig, at dette er et af de sjældne tilfælde, hvor en markør er den hurtigste, selvom jeg bliver nødt til at lave nogle benchmarking på store resultater.

Opdateringstricket er praktisk, men jeg føler, det er ret skrøbeligt. Det ser ud til, at hvis du opdaterer en fuld tabel, vil den fortsætte i rækkefølgen af ​​den primære nøgle. Så hvis du indstiller din dato som en primær nøgle stigende, vil du probably pas godt på dig selv. Men du er afhængig af en udokumenteret SQL Server-implementeringsdetalje (også hvis forespørgslen ender med at blive udført af to procs, spekulerer jeg på, hvad der vil ske, se:MAXDOP):

Komplet arbejdseksempel:

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

Du bad om et benchmark, dette er lowdown.

Den hurtigste SIKRE måde at gøre dette på ville være markøren, den er en størrelsesorden hurtigere end den korrelerede underforespørgsel til cross-join.

Den absolut hurtigste måde er UPDATE-tricket. Min eneste bekymring med det er, at jeg ikke er sikker på, at opdateringen under alle omstændigheder vil forløbe på en lineær måde. Der er intet i forespørgslen, der udtrykkeligt siger det.

Nederste linje, for produktionskode ville jeg gå med markøren.

Testdata:

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. Sådan fungerer ELT() i MariaDB

  2. Arbejde med ikke-ASCII JDBC-data i Talend

  3. En indekseret visningsfejl med skalære aggregater

  4. Postgresql opretter ikke db med "createdb" som superbruger, men udsender ikke fejl