Sqlserver
 sql >> база данни >  >> RDS >> Sqlserver

Изчислете текущата сума в SQL Server

Актуализиране , ако използвате 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


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Защо cast/convert от int връща звездичка

  2. Как да направите ЛЯВО ANTI SEMI JOIN в SQL Server

  3. Обяснение на НЯКОИ оператор на SQL Server

  4. Защо имената на таблици в SQL Server започват с dbo?

  5. Ефективност на условно агрегиране