Mysql
 sql >> Base de données >  >> RDS >> Mysql

Somme cumulée sur un ensemble de lignes dans mysql

MISE À JOUR

MySQL 8.0 introduit les "fonctions de fenêtre", une fonctionnalité équivalente aux "fonctions de fenêtre" de SQL Server (avec le partitionnement et l'ordre fournis par Transact-SQL OVER syntaxe) et les "fonctions analytiques" d'Oracle

Manuel de référence MySQL 12.21 Fonctions de fenêtre https://dev.mysql .com/doc/refman/8.0/en/window-functions.html

La réponse fournie ici est une approche pour les versions MySQL antérieures à 8.0.

RÉPONSE ORIGINALE

MySQL ne fournit pas la fonction analytique de type que vous utiliseriez pour obtenir une "somme cumulée", comme les fonctions analytiques disponibles dans d'autres SGBD (comme Oracle ou SQL Server.)

Mais, il est possible d'émuler certaines fonctions analytiques, en utilisant MySQL.

Il existe (au moins) deux approches réalisables :

La première consiste à utiliser une sous-requête corrélée pour obtenir le sous-total. Cette approche peut être coûteuse sur de grands ensembles et compliquée si les prédicats de la requête externe sont compliqués. Cela dépend vraiment de la complexité de ces "jointures multiples sur plusieurs tables". (Malheureusement, MySQL ne prend pas non plus en charge les CTE.)

L'autre approche consiste à utiliser les variables utilisateur MySQL pour effectuer un traitement de rupture de contrôle. Le "truc" ici consiste à trier les résultats de votre requête (à l'aide d'un ORDER BY), puis à envelopper votre requête dans une autre requête.

Je vais donner un exemple de cette dernière approche.

En raison de l'ordre dans lequel MySQL effectue les opérations, le cumulative_total la colonne doit être calculée avant la valeur de id et day de la ligne actuelle sont enregistrées dans des variables utilisateur. Il est simplement plus simple de placer cette colonne en premier.

La vue en ligne aliasée comme i (dans la requête ci-dessous) est juste là pour initialiser les variables utilisateur, juste au cas où elles seraient déjà définies dans la session. Si ceux-ci ont déjà des valeurs assignées, nous voulons ignorer leurs valeurs actuelles, et le moyen le plus simple de le faire est de les initialiser.

Votre requête d'origine est entourée de parenthèses et reçoit un alias, c dans l'exemple ci-dessous. La seule modification apportée à votre requête d'origine est l'ajout d'une clause ORDER BY, afin que nous puissions être sûrs que nous traitons les lignes de la requête dans l'ordre.

La sélection externe vérifie si le id et day la valeur de la ligne actuelle "correspond" à la ligne précédente. Si tel est le cas, nous ajoutons le amount de la ligne actuelle au sous-total cumulé. S'ils ne correspondent pas, nous réinitialisons le sous-total cumulé à zéro et ajoutons le montant de la ligne actuelle (ou, plus simplement, nous attribuons simplement le montant de la ligne actuelle).

Après avoir fait le calcul du total cumulé, nous sauvegardons le id et day valeurs de la ligne actuelle en variables utilisateur, afin qu'elles soient disponibles lorsque nous traitons la ligne suivante.

Par exemple :

SELECT IF(@prev_id = c.id AND @prev_day = c.day
         ,@cumtotal := @cumtotal + c.amount
         ,@cumtotal := c.amount) AS cumulative_total
     , @prev_id  := c.id  AS `id`
     , @prev_day := c.day AS `day`
     , c.hr
     , c.amount AS `amount'
  FROM ( SELECT @prev_id  := NULL
              , @prev_day := NULL
              , @subtotal := 0
       ) i
  JOIN (

         select id, day, hr, amount from
         ( //multiple joins on multiple tables)a
         left join
         (//unions on multiple tables)b
         on a.id=b.id

         ORDER BY 1,2,3
       ) c

S'il est nécessaire de renvoyer les colonnes dans un ordre différent, avec le total cumulé comme dernière colonne, une option consiste à envelopper cette instruction entière dans un ensemble de parenthèses et à utiliser cette requête comme vue intégrée :

SELECT d.id
     , d.day
     , d.hr
     , d.amount
     , d.cumulative_total
FROM (
       // query from above
     ) d