16

SQLで移動平均を作成するにはどうすればよいですか?

現在のテーブル:

Date             Clicks 
2012-05-01       2,230
2012-05-02       3,150
2012-05-03       5,520
2012-05-04       1,330
2012-05-05       2,260
2012-05-06       3,540
2012-05-07       2,330

必要なテーブルまたは出力:

Date             Clicks    3 day Moving Average
2012-05-01       2,230
2012-05-02       3,150
2012-05-03       5,520          4,360
2012-05-04       1,330          3,330
2012-05-05       2,260          3,120
2012-05-06       3,540          3,320
2012-05-07       2,330          3,010
4

13 に答える 13

19

これは Evergreen Joe Celko の質問です。どの DBMS プラットフォームが使用されているかは無視します。しかしいずれにせよ、Joe は 10 年以上前に標準 SQL で答えることができました。

Joe Celko SQL Puzzles and Answers の引用: 「最後の更新試行は、述語を使用して、移動平均を与えるクエリを作成できることを示唆しています。」

SELECT S1.sample_time, AVG(S2.load) AS avg_prev_hour_load
FROM Samples AS S1, Samples AS S2
WHERE S2.sample_time
BETWEEN (S1.sample_time - INTERVAL 1 HOUR)
AND S1.sample_time
GROUP BY S1.sample_time;

追加の列またはクエリ アプローチのどちらが優れていますか? UPDATE アプローチはデータベースを非正規化するため、クエリは技術的に優れています。ただし、記録されている履歴データが変更されず、移動平均の計算にコストがかかる場合は、列アプローチの使用を検討してください。

MS SQL の例:

CREATE TABLE #TestDW
( Date1 datetime,
  LoadValue Numeric(13,6)
);

INSERT INTO #TestDW VALUES('2012-06-09' , '3.540' );
INSERT INTO #TestDW VALUES('2012-06-08' , '2.260' );
INSERT INTO #TestDW VALUES('2012-06-07' , '1.330' );
INSERT INTO #TestDW VALUES('2012-06-06' , '5.520' );
INSERT INTO #TestDW VALUES('2012-06-05' , '3.150' );
INSERT INTO #TestDW VALUES('2012-06-04' , '2.230' );

SQL パズル クエリ:

SELECT S1.date1,  AVG(S2.LoadValue) AS avg_prev_3_days
FROM #TestDW AS S1, #TestDW AS S2
WHERE S2.date1
    BETWEEN DATEADD(d, -2, S1.date1 )
    AND S1.date1
GROUP BY S1.date1
order by 1;
于 2012-05-16T19:22:43.950 に答える
8

これを行う 1 つの方法は、同じテーブルに数回参加することです。

select
 (Current.Clicks 
  + isnull(P1.Clicks, 0)
  + isnull(P2.Clicks, 0)
  + isnull(P3.Clicks, 0)) / 4 as MovingAvg3
from
 MyTable as Current
 left join MyTable as P1 on P1.Date = DateAdd(day, -1, Current.Date)
 left join MyTable as P2 on P2.Date = DateAdd(day, -2, Current.Date)
 left join MyTable as P3 on P3.Date = DateAdd(day, -3, Current.Date)

ON-Clauses の DateAdd コンポーネントを調整して、移動平均を厳密に過去から現在または数日前から数日前のどちらにするかを一致させます。

  • これは、数個のデータ ポイントのみの移動平均が必要な場合にうまく機能します。
  • これは、複数のデータ ポイントを持つ移動平均の最適なソリューションではありません。
于 2012-05-16T19:00:38.753 に答える
3
select t2.date, round(sum(ct.clicks)/3) as avg_clicks
from
(select date from clickstable) as t2,
(select date, clicks from clickstable) as ct
where datediff(t2.date, ct.date) between 0 and 2
group by t2.date

はこちら

明らかに、必要に応じて間隔を変更できます。簡単に変更できるように、マジック ナンバーの代わりに count() を使用することもできますが、これも遅くなります。

于 2013-06-27T10:50:08.010 に答える
2

大規模なデータセットに適した移動平均の一般的なテンプレート

WITH moving_avg AS (
  SELECT 0 AS [lag] UNION ALL
  SELECT 1 AS [lag] UNION ALL
  SELECT 2 AS [lag] UNION ALL
  SELECT 3 AS [lag] --ETC
)
SELECT
  DATEADD(day,[lag],[date]) AS [reference_date],
  [otherkey1],[otherkey2],[otherkey3],
  AVG([value1]) AS [avg_value1],
  AVG([value2]) AS [avg_value2]
FROM [data_table]
CROSS JOIN moving_avg
GROUP BY [otherkey1],[otherkey2],[otherkey3],DATEADD(day,[lag],[date])
ORDER BY [otherkey1],[otherkey2],[otherkey3],[reference_date];

そして、加重移動平均の場合:

WITH weighted_avg AS (
  SELECT 0 AS [lag], 1.0 AS [weight] UNION ALL
  SELECT 1 AS [lag], 0.6 AS [weight] UNION ALL
  SELECT 2 AS [lag], 0.3 AS [weight] UNION ALL
  SELECT 3 AS [lag], 0.1 AS [weight] --ETC
)
SELECT
  DATEADD(day,[lag],[date]) AS [reference_date],
  [otherkey1],[otherkey2],[otherkey3],
  AVG([value1] * [weight]) / AVG([weight]) AS [wavg_value1],
  AVG([value2] * [weight]) / AVG([weight]) AS [wavg_value2]
FROM [data_table]
CROSS JOIN weighted_avg
GROUP BY [otherkey1],[otherkey2],[otherkey3],DATEADD(day,[lag],[date])
ORDER BY [otherkey1],[otherkey2],[otherkey3],[reference_date];
于 2012-12-12T00:40:05.673 に答える
2
select *
        , (select avg(c2.clicks) from #clicks_table c2 
            where c2.date between dateadd(dd, -2, c1.date) and c1.date) mov_avg
from #clicks_table c1
于 2012-12-13T12:56:33.580 に答える
1

別の結合述語を使用します。

SELECT current.date
       ,avg(periods.clicks)
FROM current left outer join current as periods
       ON current.date BETWEEN dateadd(d,-2, periods.date) AND periods.date
GROUP BY current.date HAVING COUNT(*) >= 3

having ステートメントは、少なくとも N 個の値が返されない日付を防ぎます。

于 2012-05-16T19:27:58.497 に答える
1

x が平均化される値であり、xDate が日付値であると仮定します。

SELECT avg(x) from myTable WHERE xDate BETWEEN dateadd(d, -2, xDate) と xDate

于 2012-05-16T19:04:10.703 に答える
0
--@p1 is period of moving average, @01 is offset

declare @p1 as int
declare @o1 as int
set @p1 = 5;
set @o1 = 3;

with np as(
select *, rank() over(partition by cmdty, tenor order by markdt) as r
from p_prices p1
where
1=1 
)
, x1 as (
select s1.*, avg(s2.val) as avgval from np s1
inner join np s2 
on s1.cmdty = s2.cmdty and s1.tenor = s2.tenor
and s2.r between s1.r - (@p1 - 1) - (@o1) and s1.r - (@o1)
group by s1.cmdty, s1.tenor, s1.markdt, s1.val, s1.r
)
于 2015-09-22T10:28:59.070 に答える
0

注:これは回答ではなく、 Diego Scaravaggiの回答の拡張コード サンプルです。コメント欄が足りないので回答として投稿します。移動平均の期間をパラメータ化したことに注意してください。

declare @p int = 3
declare @t table(d int, bal float)
insert into @t values
(1,94),
(2,99),
(3,76),
(4,74),
(5,48),
(6,55),
(7,90),
(8,77),
(9,16),
(10,19),
(11,66),
(12,47)

select a.d, avg(b.bal)
from
       @t a
       left join @t b on b.d between a.d-(@p-1) and a.d
group by a.d
于 2012-11-14T16:27:13.787 に答える
0

目的のために、次のような補助/次元の日付テーブルを作成したいと思います

create table date_dim(date date, date_1 date, dates_2 date, dates_3 dates ...)

whiledateはキーdate_1です。この日は、date_2この日と前日を含みます。date_3...

次に、ハイブで同等の結合を行うことができます。

次のようなビューを使用します。

select date, date               from date_dim
union all
select date, date_add(date, -1) from date_dim
union all
select date, date_add(date, -2) from date_dim
union all
select date, date_add(date, -3) from date_dim
于 2012-09-24T14:42:56.310 に答える
-1

Joe Celko の「汚い」左外部結合メソッド (Diego Scaravaggi によって上で引用されたもの) を適用して、質問されたとおりに質問に答えることができます。

declare @ClicksTable table  ([Date] date, Clicks int)
insert into @ClicksTable
    select '2012-05-01', 2230 union all
    select '2012-05-02', 3150 union all
    select '2012-05-03', 5520 union all
    select '2012-05-04', 1330 union all
    select '2012-05-05', 2260 union all
    select '2012-05-06', 3540 union all
    select '2012-05-07', 2330

このクエリ:

SELECT
    T1.[Date],
    T1.Clicks,
    -- AVG ignores NULL values so we have to explicitly NULLify
    -- the days when we don't have a full 3-day sample
    CASE WHEN count(T2.[Date]) < 3 THEN NULL
        ELSE AVG(T2.Clicks) 
    END AS [3-Day Moving Average] 
FROM @ClicksTable T1
LEFT OUTER JOIN @ClicksTable T2
    ON T2.[Date] BETWEEN DATEADD(d, -2, T1.[Date]) AND T1.[Date]
GROUP BY T1.[Date]

要求された出力を生成します。

Date             Clicks    3-Day Moving Average
2012-05-01       2,230
2012-05-02       3,150
2012-05-03       5,520          4,360
2012-05-04       1,330          3,330
2012-05-05       2,260          3,120
2012-05-06       3,540          3,320
2012-05-07       2,330          3,010
于 2016-01-09T00:33:50.607 に答える