web-dev-qa-db-ja.com

SQLサーバーのOver句を使用した移動平均の計算

私のテーブルは次のようになります:

CREATE TABLE [dbo].[TimeSeries](
[LOCID] [int] NOT NULL,
[Date] DateTime,
[YEAR] int,
[MONTH] tinyint, 
[RTT] [int]
PRIMARY KEY ( [LOCID] ASC,[Date] ASC)
 )

これには、LOCIDごと、年ごとに12行(またはMONTHS)が含まれます。

次のクエリがあります。

SELECT 
    LOCID,
    [Year],
    [Date],
    AVG(cast(RTT as float)) OVER (
         PARTITION BY LOCID ORDER BY Date 
             ROWS BETWEEN CURRENT ROW AND 4 FOLLOWING ) AS avgRTT
FROM TimeSeries

データの例:

LOCID   Date    Year    Month   RTT
1   01/01/1982  1982    1   58
1   01/02/1982  1982    2   63
1   01/03/1982  1982    3   34
1   01/04/1982  1982    4   27
1   01/05/1982  1982    5   6
1   01/06/1982  1982    6   4
1   01/07/1982  1982    7   3
1   01/08/1982  1982    8   14
1   01/09/1982  1982    9   22
1   01/10/1982  1982    10  16
1   01/11/1982  1982    11  17
1   01/12/1982  1982    12  44
1   01/01/1983  1983    1   58
1   01/02/1983  1983    2   63
1   01/03/1983  1983    3   33
1   01/04/1983  1983    4   27
1   01/05/1983  1983    5   9
1   01/06/1983  1983    6   0
1   01/07/1983  1983    7   3
1   01/08/1983  1983    8   0
1   01/09/1983  1983    9   6
1   01/10/1983  1983    10  27
1   01/11/1983  1983    11  11
1   01/12/1983  1983    12  48

今、私は毎年のみ特定の月について上記のクエリを実行して、続く5つの日付/月/行の平均を計算しようとしています。ただし、MONTH値を指定するWHERE句は機能しません。これは、平均される数値に影響するためです。

何万ものLOCIDと何百万もの行に対してこれを行う必要があります。

私の質問は、「MONTH」の特定の値に対してのみ正しい移動平均を実行する方法があるかどうかです。これにより、うまくいけば、処理時間を何度も短縮できます...

望ましい出力例:

LOCID   Date    Year    Month   avgRTT
1   29952   1982    1   37.6
1   30317   1983    1   38

どんなポインタでも大歓迎です。

2
Joost vdW

移動平均を計算した後、_@dekade_に最終フィルターを適用できます。

移動平均のために処理する必要のある行の数を減らすために、[dekade] IN (@dekade, (@dekade+1)%36, (@dekade+2)%36)に以前のフィルターを適用して、最小限の行を処理しながら確実にすべての行を含めることができます。次の11行を移動平均に含める必要があります。 (_% 36_の唯一の理由は、年末に該当する_@dekade_の値を処理するためです。)

これにより、現在のテーブル構造が与えられた場合でもテーブルスキャンは行われますが、少なくともクエリプランの早い段階で行を除外できます。

_DECLARE @dekade TINYINT = 1
SELECT *
FROM (
    SELECT 
        LOCID,
        [Year],
        [Date],
        [dekade],
        AVG(cast(RTT as float)) OVER 
            (PARTITION BY LOCID ORDER BY Date
                ROWS BETWEEN CURRENT ROW AND 11 FOLLOWING) AS avgRTT
    FROM TimeSeries
    -- If you want to limit the rows that are use when computing the running average,
    -- you can make sure that only the desired @dekade plus the following two @dekades
    -- (which may be needed to get the following 11 rows) are used for each year
    WHERE [dekade] (@dekade, (@dekade+1)%36, (@dekade+2)%36)
) x
-- Filter your results be @dekade after computing the running average
WHERE x.[dekade] = @dekade
_

実際にクエリを実行できるように、いくつかのサンプルデータを投稿すると役立ちます。

1
Geoff Patterson

運が良ければ、オプティマイザが年に1回だけ移動平均を計算できるほど賢い場合は、AVGCASEに入れてみてください。

select *
from
 (
   SELECT 
       LOCID,
       [Year],
       [Date],
       [Month],
       case when Month = 1 then
          AVG(cast(RTT as float)) OVER (
               PARTITION BY LOCID ORDER BY Date 
                   ROWS BETWEEN CURRENT ROW AND 4 FOLLOWING ) 
       end AS avgRTT
   FROM TimeSeries
 ) as dt
where month = 1 -- or avgRTT is not null
1
dnoeth