Comme le titre l'indique, j'ai besoin d'aide pour obtenir un total cumulé dans T-SQL. Le problème est que la somme que je dois faire est la somme d'un compte:
sum(count (distinct (customers)))
Disons que si je comptais seul, le résultat serait:
Day | CountCustomers
----------------------
5/1 | 1
5/2 | 0
5/3 | 5
J'ai besoin d'une sortie avec la somme pour être:
Day | RunningTotalCustomers
----------------------
5/1 | 1
5/2 | 1
5/3 | 6
J'ai exécuté les totaux avant d'utiliser la coalesce
méthode, mais jamais avec un compte. Je ne sais pas comment faire maintenant que j'ai le décompte.
sql-server
t-sql
Aaron Bertrand
la source
la source
Day
une clé et les valeurs sont-elles contiguës?Réponses:
Voici quelques méthodes que vous pouvez comparer. Commençons par configurer une table avec des données factices. Je remplis cela avec un tas de données aléatoires de sys.all_columns. Eh bien, c'est un peu aléatoire - je m'assure que les dates sont contiguës (ce qui n'est vraiment important que pour l'une des réponses).
Résultats:
Les données ressemblent à ceci (5000 lignes) - mais auront une apparence légèrement différente sur votre système en fonction de la version et du numéro de construction:
Et les résultats des totaux cumulés devraient ressembler à ceci (501 lignes):
Donc, les méthodes que je vais comparer sont les suivantes:
auto-rejoindre
C’est ainsi que les gens vous diront de le faire quand ils vous avertiront de rester à l’écart des curseurs, car «les réglages sur les ensembles sont toujours plus rapides». Dans certaines expériences récentes, j'ai constaté que le curseur surpassait cette solution.
cte récursive avec dates
Rappel - cela repose sur des dates contiguës (sans espaces), sur une récurrence allant jusqu'à 10000, et sur la date de début de la plage qui vous intéresse (pour définir l'ancre). Vous pouvez définir l’ancre de manière dynamique en utilisant une sous-requête, bien sûr, mais je voulais garder les choses simples.
cte récursif avec row_number
Le calcul de Row_number est légèrement coûteux ici. Encore une fois, cela prend en charge le niveau maximal de récursivité de 10 000, mais vous n'avez pas besoin d'attribuer l'ancre.
cte récursif avec table temporaire
Voler la réponse de Mikael, comme suggéré, pour l'inclure dans les tests.
mise à jour décalée
Encore une fois, je n'inclue ceci que par souci d'exhaustivité; Personnellement, je ne m'appuierais pas sur cette solution car, comme je l'ai mentionné dans une autre réponse, cette méthode n'est pas garantie du tout et peut complètement casser dans une future version de SQL Server. (Je fais de mon mieux pour contraindre SQL Server à obéir à l'ordre que je souhaite, en utilisant un indice pour le choix d'index.)
le curseur
"Attention, il y a des curseurs ici! Les curseurs sont diaboliques! Vous devriez éviter les curseurs à tout prix!" Non, ce n'est pas moi qui parle, c'est juste des choses que j'entends beaucoup. Contrairement à l'opinion populaire, il existe des cas où les curseurs sont appropriés.
SQL Server 2012
Si vous utilisez la version la plus récente de SQL Server, les améliorations apportées à la fonctionnalité de fenêtrage nous permettent de calculer facilement les totaux cumulés sans le coût exponentiel de l’adhésion automatique (la somme est calculée en un seul passage), la complexité des CTE (y compris la de lignes contiguës pour le CTE le plus performant), la mise à jour décalée non prise en charge et le curseur interdit. Méfiez-vous simplement de la différence entre utiliser
RANGE
etROWS
, ou ne pas spécifier du tout - celaROWS
évite seulement un spool sur disque, ce qui nuirait considérablement aux performances.comparaisons de performance
J'ai pris chaque approche et l'ai emballée un lot en utilisant ce qui suit:
Voici les résultats de la durée totale, en millisecondes (rappelez-vous, cela inclut également les commandes DBCC):
Et je l'ai encore fait sans les commandes DBCC:
Supprimer à la fois le DBCC et les boucles, en mesurant une seule itération brute:
Enfin, j'ai multiplié par 10 le nombre de lignes de la table source (en remplaçant top par 50000 et en ajoutant une autre table en jointure croisée). Les résultats de cela, une seule itération sans commande DBCC (simplement pour gagner du temps):
J'ai seulement mesuré la durée - je laisserai au lecteur le soin de comparer ces approches sur leurs données, en comparant d'autres métriques pouvant être importantes (ou pouvant varier avec leurs schémas / données). Avant de tirer des conclusions de cette réponse, il vous appartiendra de la tester par rapport à vos données et à votre schéma ... ces résultats changeront presque certainement à mesure que le nombre de lignes augmentera.
démo
J'ai ajouté un sqlfiddle . Résultats:
conclusion
Dans mes tests, le choix serait:
Mais encore une fois, vous devriez les tester contre votre schéma et vos données. Comme il s’agissait d’un test artificiel avec un nombre de lignes relativement faible, il se peut tout aussi bien que ce soit un pet dans le vent. J'ai fait d'autres tests avec différents nombres de schémas et de lignes, et l'heuristique des performances était très différente ... C'est pourquoi j'ai posé autant de questions complémentaires à votre question initiale.
MISE À JOUR
J'ai blogué plus à ce sujet ici:
Meilleures approches pour les totaux cumulés - mises à jour pour SQL Server 2012
la source
C'est apparemment la solution optimale
la source
day
par exemple.Juste une autre manière, coûteuse, mais indépendante de la version. Il n'utilise pas de tables temporaires ni de variables.
la source