2014-10-28 21 views
15

Pracuję z SQL Server 2008 R2, próbując obliczyć średnią ruchomą. Dla każdego rekordu w moim widoku chciałbym zebrać wartości 250 poprzednich rekordów, a następnie obliczyć średnią dla tego wyboru.T-SQL oblicza średnią ruchomą

moim zdaniem kolumny są następujące:

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 jest wyjątkowy. Dla każdego TransactionID chciałbym obliczyć średnią dla wartości kolumny, powyżej 250 rekordów. Tak więc dla TransactionID 300, zebrać wszystkie wartości z poprzednich 250 wierszy (widok jest posortowany malejąco według TransactionID), a następnie w kolumnie MovAvg zapisać wynik średniej z tych wartości. Szukam zebrać dane w zakresie rekordów.

+2

Zobacz w części "PARTYCJA PRZEZ" i "ROW_NUMBER" – Mihai

+0

Dzięki. Czy masz jakąś sugestię, jak to zrobić? – RunW

+0

@RunW czy ten sam identyfikator transakcji powtarza się z inną wartością, czy jest tam znacznik czasu lub kolumna tożsamości oprócz identyfikatora transakcji? – radar

Odpowiedz

20

Funkcje okna w SQL 2008 są raczej ograniczone w porównaniu do późniejszych wersji i jeśli dobrze pamiętam, można tylko partycjonować i nie można użyć żadnego wiersza/limitu ramki zakresu, ale myślę, że to może być to, czego chcesz:

;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 

Należy zauważyć, że w każdym rzędzie zastosowano skorelowane pod-zapytanie, a wydajność może być niewielka.

Z późniejszych wersjach mogłeś używanych funkcji rama okienna i zrobić coś takiego:

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

Dziękuję bardzo. Z pewnością jest to sposób, aby to zrobić, ale jak mówisz, wydajność jest dość zła. Doceniam Twoją pomoc. – RunW

+0

Czy jest to skuteczniejszy sposób w SQL 2008, który nie uruchamia skorelowane pod-kwerendy w każdym wierszu? Próbowałem znaleźć rozwiązanie, które skróciłoby mi czas wykonania, ale już niedługo. – mitchimus

+0

@mitchimus Może być, ale nie jestem tego świadomy - nie myślałem o tym zbyt wiele, ponieważ są lepsze opcje z późniejszymi wersjami serwera. – jpw

5

użyć Common Table Expression (CTE) włączenie rownum dla każdej transakcji, a następnie przystąpić do CTE przeciwko sobie od liczby rzędów tak możesz uzyskać poprzednie wartości, aby obliczyć średnią z.

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