2014-10-28 25 views
15

Sto lavorando con SQL Server 2008 R2, cercando di calcolare una media mobile. Per ogni record a mio avviso, vorrei raccogliere i valori dei 250 record precedenti e quindi calcolare la media per questa selezione.T-SQL calcolare media mobile

mio avviso colonne sono le seguenti:

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 è unico. Per ogni TransactionID, vorrei calcolare la media per il valore della colonna, rispetto ai precedenti 250 record. Quindi per TransactionID 300, raccogli tutti i valori delle precedenti 250 righe (la vista è ordinata decrescente per TransactionID) e quindi nella colonna MovAvg scrivi il risultato della media di questi valori. Sto cercando di raccogliere dati in una serie di record.

+2

Cerca in "PARTITION BY' e" ROW_NUMBER' – Mihai

+0

Grazie. Qualche possibilità che tu abbia un suggerimento su come farlo? – RunW

+0

@RunW fa lo stesso ID di transazione si ripete con un valore diverso, c'è la colonna di data/ora o la colonna di identità a parte l'ID della transazione? – radar

risposta

20

Le funzioni della finestra in SQL 2008 sono piuttosto limitate rispetto alle versioni successive e, se ricordo corretto, è possibile solo partizionare e non è possibile utilizzare alcun limite di righe/intervallo di frame ma penso che questo potrebbe essere ciò che si desidera:

;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 

Si noti che applica una sottoquery correlata a ogni riga e le prestazioni potrebbero non essere eccezionali.

Con le versioni successive si potrebbe avere usato le funzioni di telaio della finestra e fatto qualcosa di simile:

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 
+0

Grazie mille. Certamente è il modo per farlo, ma come dici tu, le prestazioni sono piuttosto negative. Apprezzo il tuo aiuto. – RunW

+0

Esiste un modo più efficiente per eseguire questa operazione in SQL 2008 che non esegue una sottoquery correlata su ogni riga? Mi sto tormentando per cercare di trovare una soluzione che diminuisca il mio tempo di esecuzione, ma mi è venuta meno. – mitchimus

+0

@mitchimus Potrebbero esserci, ma non ne sono a conoscenza - non ci ho pensato molto anche se ci sono opzioni migliori con versioni successive del server. – jpw

5

Utilizzare un Common Table Expression (CTE) per includere il rownum per ogni transazione, quindi unire il CTE contro se stessa sul numero di riga in modo puoi ottenere i valori precedenti per calcolare la media con.

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