211 votes

Comment obtenir la somme cumulée

declare  @t table
    (
        id int,
        SomeNumt int
    )

insert into @t
select 1,10
union
select 2,12
union
select 3,3
union
select 4,15
union
select 5,23

select * from @t

la sélection ci-dessus me renvoie le résultat suivant.

id  SomeNumt
1   10
2   12
3   3
4   15
5   23

Comment puis-je obtenir ce qui suit :

id  srome   CumSrome
1   10  10
2   12  22
3   3   25
4   15  40
5   23  63

6 votes

Obtenir des totaux courants en T-SQL n'est pas difficile, il existe de nombreuses réponses correctes, la plupart d'entre elles étant assez simples. Ce qui n'est pas facile (ou même possible à l'heure actuelle), c'est d'écrire une vraie requête en T-SQL pour les totaux courants qui soit efficace. Elles sont toutes O(n^2), bien qu'elles pourraient facilement être O(n), sauf que T-SQL n'est pas optimisé pour ce cas. Vous pouvez obtenir O(n) en utilisant des curseurs et/ou des boucles While, mais alors vous utilisez des curseurs. ( blech ! )

255voto

RedFilter Points 84190
select t1.id, t1.SomeNumt, SUM(t2.SomeNumt) as sum
from @t t1
inner join @t t2 on t1.id >= t2.id
group by t1.id, t1.SomeNumt
order by t1.id

Exemple SQL Fiddle

Output

| ID | SOMENUMT | SUM |
-----------------------
|  1 |       10 |  10 |
|  2 |       12 |  22 |
|  3 |        3 |  25 |
|  4 |       15 |  40 |
|  5 |       23 |  63 |

Editar: Il s'agit d'une solution généralisée qui fonctionnera sur la plupart des plateformes de base de données. S'il existe une meilleure solution disponible pour votre plateforme spécifique (par exemple, celle de Gareth), utilisez-la !

14 votes

@Franklin Seulement rentable pour les petites tables. Le coût croît proportionnellement au carré du nombre de lignes. SQL Server 2012 permet de le faire de manière beaucoup plus efficace.

3 votes

Pour information, un DBA m'a déjà donné des coups de poing en faisant cela. Je pense que la raison en est que cela devient très coûteux, très rapidement. Ceci étant dit, c'est une excellente question d'entretien, car la plupart des analystes de données/scientifiques devraient avoir eu à résoudre ce problème une ou deux fois :)

0 votes

@BenDundee D'accord - J'ai tendance à fournir des solutions SQL généralisées qui fonctionneront sur la plupart des plates-formes de données. Comme toujours, lorsqu'une meilleure approche est disponible, par exemple les gareths, utilisez-la !

232voto

Gareth Adamson Points 211

La dernière version de SQL Server (2012) permet ce qui suit.

SELECT 
    RowID, 
    Col1,
    SUM(Col1) OVER(ORDER BY RowId ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS Col2
FROM tablehh
ORDER BY RowId

o

SELECT 
    GroupID, 
    RowID, 
    Col1,
    SUM(Col1) OVER(PARTITION BY GroupID ORDER BY RowId ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS Col2
FROM tablehh
ORDER BY RowId

C'est encore plus rapide. La version partitionnée se termine en 34 secondes sur 5 millions de lignes pour moi.

Merci à Peso, qui a commenté le fil de discussion de l'équipe SQL mentionné dans une autre réponse.

26 votes

Par souci de concision, vous pouvez utiliser ROWS UNBOUNDED PRECEDING au lieu de ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW .

2 votes

Note : Si la colonne que vous voulez additionner de manière cumulative est déjà une somme ou un compte, vous pouvez soit envelopper l'ensemble dans une requête interne, soit effectuer la procédure suivante SUM(COUNT(*)) OVER (ORDER BY RowId ROWS UNBOUNDED PRECEDING) AS CumulativeSum . Il n'était pas immédiatement évident pour moi que cela fonctionnerait, mais cela a marché :-)

0 votes

Disponible dans PostgreSQL à partir de 8.4 : postgresql.org/docs/8.4/sql-select.html

18voto

Créons d'abord une table avec des données fictives :

Create Table CUMULATIVESUM (id tinyint , SomeValue tinyint)

Insérons maintenant quelques données dans le tableau ;

Insert Into CUMULATIVESUM
    Select 1, 10 union 
    Select 2, 2  union
    Select 3, 6  union
    Select 4, 10 

Ici, je joins la même table (auto-jonction).

Select c1.ID, c1.SomeValue, c2.SomeValue
From CumulativeSum c1, CumulativeSum c2
Where c1.id >= c2.ID
Order By c1.id Asc

Résultat :

ID  SomeValue   SomeValue
-------------------------
1   10          10
2   2           10
2   2            2
3   6           10
3   6            2
3   6            6
4   10          10
4   10           2
4   10           6
4   10          10

Nous y voilà, il suffit d'additionner les valeurs de t2 et nous aurons la réponse :

Select c1.ID, c1.SomeValue, Sum(c2.SomeValue) CumulativeSumValue
From CumulativeSum c1,  CumulativeSum c2
Where c1.id >= c2.ID
Group By c1.ID, c1.SomeValue
Order By c1.id Asc

Pour SQL Server 2012 et supérieur (performances bien meilleures) :

Select 
    c1.ID, c1.SomeValue, 
    Sum (SomeValue) Over (Order By c1.ID )
From CumulativeSum c1
Order By c1.id Asc

Résultat souhaité :

ID  SomeValue   CumlativeSumValue
---------------------------------
1   10          10
2   2           12
3   6           18
4   10          28

Drop Table CumulativeSum

0 votes

Veuillez modifier votre réponse et formater le code pour le rendre lisible.

0 votes

Que se passe-t-il si les valeurs de mi "ID" sont répétées ? (elles ne sont évidemment pas des clés primaires dans ma table) Je n'ai pas réussi à adapter cette requête à ce cas ?

0 votes

AFAIK vous avez besoin d'un identifiant unique pour la somme cumulative, et vous pouvez l'obtenir en utilisant row_number. vérifiez le code ci-dessous : avec NewTBLWITHUNiqueID comme ( select row_number() over(order by id , somevalue) UniqueID , * From CUMULATIVESUMwithoutPK )

13voto

Damir Sudarevic Points 14125

Une version CTE, juste pour le plaisir :

;
WITH  abcd
        AS ( SELECT id
                   ,SomeNumt
                   ,SomeNumt AS MySum
             FROM   @t
             WHERE  id = 1
             UNION ALL
             SELECT t.id
                   ,t.SomeNumt
                   ,t.SomeNumt + a.MySum AS MySum
             FROM   @t AS t
                    JOIN abcd AS a ON a.id = t.id - 1
           )
  SELECT  *  FROM    abcd
OPTION  ( MAXRECURSION 1000 ) -- limit recursion here, or 0 for no limit.

Les retours :

id          SomeNumt    MySum
----------- ----------- -----------
1           10          10
2           12          22
3           3           25
4           15          40
5           23          63

5voto

Ritesh Khatri Points 376
Select 
    *, 
    (Select Sum(SOMENUMT) 
     From @t S 
     Where S.id <= M.id)
From @t M

0 votes

C'est une façon très intelligente d'obtenir le résultat, et vous pouvez ajouter plusieurs conditions à la somme.

2 votes

@RaRdEvA Ce n'est pas génial pour les performances cependant, ça fonctionne que correlated subquery pour chaque ligne du jeu de résultats, en analysant de plus en plus de lignes au fur et à mesure. Elle ne garde pas un total courant et n'analyse pas les données une seule fois comme le font les fonctions de fenêtre.

3 votes

@Davos vous avez raison, si vous l'utilisez, il devient très lent au-delà de 100 000 enregistrements.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X