web-dev-qa-db-ja.com

SQL Serverでの7日間のローリング平均のSQLクエリ

1時間ごとの製品使用量(製品の使用回数)データの表があります–

ID (bigint)| ProductId (tinyint)| Date (int - YYYYMMDD) | Hour (tinyint)| UsageCount (int)
#|1 | 20140901 | 0 | 10
#|1 | 20140901 | 1 | 15
#|1 | 20140902 | 5 | 25
#|1 | 20140903 | 5 | 25
#|1 | 20140904 | 3 | 25
#|1 | 20140905 | 7 | 25
#|1 | 20140906 | 10 | 25
#|1 | 20140907 | 9 | 25
#|1 | 20140908 | 5 | 25
#|2 | 20140903 | 16 | 10
#|2 | 20140903 | 13 | 115

同様に、product_usageテーブルに1時間ごとに4つの異なる製品(1〜4のProductId)の使用状況データを保存しています。ご想像のとおり、夜間のETLプロセスが前日全体のデータをダンプするため、常に成長しています。製品が1日のどの時間にも使用されない場合、その時間のレコードはこの表に表示されません。同様に、製品が1日中使用されない場合、表にはその日のレコードはありません。毎日の使用状況と過去7日間の移動平均を示すレポートを生成する必要があります。

例えば:

ProductId | Date | DailyUsage | RollingAverage
1 | 20140901 | sum of usages of that day | (Sum of usages from 20140901 through 20140826) / 7
1 | 20140901 | sum of usages of that day | (Sum of usages from 20140901 through 20140826) / 7
1 | 20140902 | sum of usages of that day | (Sum of usages from 20140902 through 20140827) / 7
2 | 20140902 | sum of usages of that day | (Sum of usages from 20140902 through 20140827) / 7

など.. SQL Server 2014でインデックス付きビューを作成する予定です。これを行うための効率的なSQLクエリを考えていただけますか。

17
Andy T

試してください:

select x.*,
       avg(dailyusage) over(partition by productid order by productid, date rows between 6 preceding and current row) as rolling_avg
  from (select productid, date, sum(usagecount) as dailyusage
          from tbl
         group by productid, date) x

フィドル:

http://sqlfiddle.com/#!6/f674a7/4/

「avg(dailusage)over ....」を実際に必要なのが先週の合計である場合は、(avgではなく)sumに置き換えます。タイトルでは、平均が欲しいと言いますが、後で合計が欲しいと言います。クエリはそれ以外は同じである必要があるため、実際に使用するものを使用します。

Gordonが指摘したように、これは基本的に製品が使用された過去6日間の平均です。まったく使用されません。これを回避するには、日付テーブルと製品テーブルを使用できます。

18
Brian DeMilia

数日でデータが失われる可能性がある場合は注意する必要があります。毎日いくつかの製品のデータがあると仮定すると、このアプローチは機能します。

select p.productid, d.date, sum(usagecount),
       sum(sum(usagecount)) over (partition by p.productid order by d.date
                                  rows between 6 preceding and current row) as Sum7day
from (select distinct productid from hourly) p cross join
     (select distinct date from hourly) d left join
     hourly h
     on h.productid = p.productid and h.date = p.date
group by p.productid, d.date;
5
Gordon Linoff