web-dev-qa-db-fra.com

Somme du nombre n précédent de colonnes en fonction d'une catégorie

J'ai l'entrée suivante pour laquelle je dois calculer la somme des valeurs pour le nombre x précédent de semaines pour chaque catégorie.

input

Si le x est 3, la sortie ressemblerait à ceci:

enter image description here

Notez que la dernière valeur est 49 car elle n'a ajouté que les valeurs des deux dernières semaines à la semaine en cours depuis x = 3.

Je cherche à écrire le SQL en tant que procédure stockée et j'ai besoin d'aide sur les moyens appropriés de le faire.

Avec l'aide de @sp_BlitzErik, j'ai essayé d'utiliser LAG mais je n'ai pas pu arriver là où je devais. Voici ma requête:

SELECT category
    ,year
    ,week
    ,value
    ,(
        LAG(value, 1, 0) OVER (
            ORDER BY category
                ,year
                ,week
            ) + LAG(value, 2, 0) OVER (
            ORDER BY category
                ,year
                ,week
            ) + value
        ) AS cumulative_value
FROM valuedata

Et la sortie n'est pas encore tout à fait correcte:

enter image description here

4
user3885927

Sur SQL Server 2014 et 2016 1 vous pouvez utiliser une fonction WINDOW (c'est-à-dire une clause OVER ) pour effectuer ce que vous voulez:

SELECT
     category, year, week, value, 
     sum(value) OVER (PARTITION BY category 
                          ORDER BY year, week 
                              ROWS 2 PRECEDING) AS retention_value_3_weeks
FROM
     t 
ORDER BY
    category, year, week ;

Et voici le résultat que vous obtiendrez:

Catégorie 
 | année | semaine | valeur | retention_value_3_weeks 
: ---- ---: | ---: | ----: | ----------------------: 
 a | 2016 | 1 | 5 | 5 
 A | 2016 | 2 | 7 | 12 
 A | 2016 | 3 | 8 | 20 
 B | 2016 | 3 | 6 | 6 
 B | 2016 | 4 | 15 | 21 
 B | 2016 | 5 | 25 | 46 
 C | 2016 | 3 | 25 | 25 
 C | 2016 | 4 | 2 | 27 
 C | 2016 | 5 | 21 | 48 
 C | 2016 | 6 | 26 | 49 

Notez que le x = 3 de votre exemple est traduit en ( la ligne actuelle et la 2 preceding ceux ).

Si, pour une raison quelconque, vous ne pouvez pas utiliser la clause OVER, vous pouvez toujours calculer les mêmes résultats en utilisant des sous-requêtes (assez compliquées):

SELECT
    category, year, week, value,
    (SELECT 
        sum(value)
     FROM
        (SELECT TOP 3                    /* total number of rows to consider */
             value
        FROM 
           t t2
        WHERE
               t2.category = t.category  /* partition by category */
           AND t2.week <= t.week         /* current and preceding rows */
        ORDER BY
           year DESC, week DESC          /* order by criteria */
        ) AS q
     ) AS retention_value_3_weeks
FROM
    t 
ORDER BY
    category, year, week ;

Vérifiez tout sur dbfiddle --- (ici

Si vous souhaitez utiliser @x au lieu de 3, vous pouvez le faire:

DECLARE @x AS INTEGER = 3;

SELECT
    category, year, week, value,
    (SELECT 
        sum(value)
     FROM
        (SELECT TOP (@x)                  /* total number of rows to consider */
             value
        FROM 
           t t2
        WHERE
               t2.category = t.category  /* partition by category */
           AND t2.week <= t.week         /* current and preceding rows */
        ORDER BY
           year DESC, week DESC          /* order by criteria */
        ) AS q
     ) AS retention_value_3_weeks
FROM
    t 
ORDER BY
    category, year, week ;;

dbfiddle ici


1) Impossible de tester avec SQL Server 2012 car je n'en ai pas. La documentation de MS SQL Server indique que devrait être disponible depuis la version 2008.

4
joanolo

Voici ce que j'ai obtenu avec l'indicateur LAG de @sp_BlitzErik et le résultat est exactement ce que je voulais.

SELECT category
    ,year
    ,week
    ,value
    ,(
        LAG(value, 1, 0) OVER (
            PARTITION BY category ORDER BY year
                ,week
            ) + LAG(value, 2, 0) OVER (
            PARTITION BY category ORDER BY year
                ,week
            ) + value
        ) AS cumulative_value
FROM valuedata

Mais ce n'est pas assez générique, disons pour X nombre de semaines. Veuillez poster si vous avez de meilleures réponses.

1
user3885927
SELECT
    category, year, week, value,
    (SELECT 
        sum(t2.value)
     FROM
           t t1 INNER JOIN t t2
        WHERE
               t1.category = t2.category  /* partition by category */
           AND t1.week >= t.week + 2        /* current and preceding rows */
        GROUP BY t1.category, t1.year, t1.week      /* group by criteria */
        ) AS q
     ) AS retention_value_3_weeks
FROM
    t 
ORDER BY
    category, year, week ;
0
Aakash