Mise à jour Si vous utilisez SQL Server 2012, voyez : https://stackoverflow.com/a/10309947
Le problème est que l'implémentation de la clause Over dans le serveur SQL est assez limité .
Oracle (et ANSI-SQL) vous permet de faire des choses comme :
SELECT somedate, somevalue,
SUM(somevalue) OVER(ORDER BY somedate
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
AS RunningTotal
FROM Table
SQL Server ne vous donne aucune solution propre à ce problème. Mon instinct me dit qu'il s'agit d'un de ces rares cas où un curseur est le plus rapide, bien que je doive faire quelques analyses comparatives sur des résultats importants.
L'astuce de mise à jour est pratique mais je pense qu'elle est assez fragile. Il semble que si vous mettez à jour une table complète, la mise à jour se fera dans l'ordre de la clé primaire. Ainsi, si vous définissez votre date comme une clé primaire ascendante, vous obtiendrez les résultats suivants probably
être en sécurité. Mais vous vous fiez à un détail d'implémentation non documenté de SQL Server (de plus, si la requête finit par être exécutée par deux procs, je me demande ce qui se passera, voir : MAXDOP) :
Échantillon de travail complet :
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
Vous avez demandé une analyse comparative, voici les résultats.
La méthode SAFE la plus rapide serait le curseur, qui est un ordre de grandeur plus rapide que la sous-requête corrélée de la jonction croisée.
Le moyen le plus rapide est l'astuce UPDATE. Mon seul souci est que je ne suis pas certain que, dans toutes les circonstances, la mise à jour se déroule de manière linéaire. Il n'y a rien dans la requête qui le dit explicitement.
En résumé, pour un code de production, je choisirais le curseur.
Données d'essai :
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
0 votes
blogs.msdn.com/sqltips/archive/2005/07/20/441053.aspx Ajoutez une commande par à votre mise à jour ... et vous obtenez une garantie.
0 votes
Mais la commande par ne peut pas être appliquée à une instruction UPDATE... n'est-ce pas ?
0 votes
Voir aussi sqlperformance.com/2012/07/t-sql-queries/running-totals surtout si vous utilisez SQL Server 2012.