web-dev-qa-db-fra.com

T-SQL calcule la moyenne mobile

Je travaille avec SQL Server 2008 R2, essayant de calculer une moyenne mobile. Pour chaque enregistrement à mon avis, je voudrais collecter les valeurs des 250 enregistrements précédents, puis calculer la moyenne de cette sélection.

Mes colonnes d'affichage sont les suivantes:

TransactionID | TimeStamp           | Value | MovAvg
----------------------------------------------------
            1 | 01.09.2014 10:00:12 |     5 |    
            2 | 01.09.2014 10:05:34 |     3 | 
...
          300 | 03.09.2014 09:00:23 |     4 | 

TransactionID est unique. Pour chaque TransactionID, je voudrais calculer la moyenne de la valeur de la colonne, sur les 250 enregistrements précédents. Ainsi, pour TransactionID 300, collectez toutes les valeurs des 250 lignes précédentes (la vue est triée décroissante par TransactionID), puis dans la colonne MovAvg, écrivez le résultat de la moyenne de ces valeurs. Je cherche à collecter des données dans une série d'enregistrements.

16
RunW

Les fonctions de fenêtre dans SQL 2008 sont plutôt limitées par rapport aux versions ultérieures et si je me souviens bien, vous ne pouvez que partitionner et vous ne pouvez pas utiliser de limite de cadre de lignes/plages, mais je pense que cela pourrait être ce que vous voulez:

;WITH cte (rn, transactionid, value) AS (
    SELECT 
       rn = ROW_NUMBER() OVER (ORDER BY transactionid),
       transactionid,
       value
    FROM your_table
)

SELECT 
    transactionid, 
    value, 
    movagv = (
        SELECT AVG(value) 
        FROM cte AS inner_ref
        -- average is calculated for 250 previous to current row inclusive
        -- I might have set the limit one row to large, maybe it should be 249
        WHERE inner_ref.rn BETWEEN outer_ref.rn-250 AND outer_ref.rn
        ) 
FROM cte AS outer_ref

Notez qu'il applique une sous-requête corrélée à chaque ligne et que les performances peuvent ne pas être excellentes.

Avec les versions ultérieures, vous auriez pu utiliser les fonctions de cadre de fenêtre et faire quelque chose comme ceci:

SELECT 
    transactionid, 
    value,
    -- avg over the 250 rows counting from the previous row
    AVG(value) OVER (ORDER BY transactionid  
                     ROWS BETWEEN 251 PRECEDING AND 1 PRECEDING),
    -- or 250 rows counting from current
    AVG(value) OVER (ORDER BY transactionid  
                     ROWS BETWEEN 250 PRECEDING AND CURRENT ROW)
FROM your_table
21
jpw

Utilisez un Common Table Expression (CTE) pour inclure le rownum pour chaque transaction, puis joignez le CTE contre lui-même sur le numéro de ligne afin que vous puissiez obtenir les valeurs précédentes pour calculer la moyenne avec.

CREATE TABLE MyTable (TransactionId INT, Value INT)

;with Data as
(
  SELECT TransactionId, 
         Value, 
         ROW_NUMBER() OVER (ORDER BY TransactionId ASC) as rownum
  FROM MyTable
)
SELECT d.TransactionId , Avg(h.Value) as MovingAverage
FROM Data d
JOIN Data h on h.rownum between d.rownum-250 and d.rownum-1
GROUP BY d.TransactionId 
6
Adam Porad