一定の日付範囲と時間間隔で時系列を提供するクエリを作成し、各時間間隔の収益を表示します。
SELECT
interval_date,
coalesce(campaign_revenue,0) AS campaign_revenue,
FROM
-- generate_series helps fill the empty gaps in the following JOIN
generate_series(
$2::timestamp,
$3::timestamp,
$4) AS interval_date -- could be '1 day', '1 hour' or '1 minute'.
LEFT OUTER JOIN
-- This SELECT gets all timeseries rows that have data
(SELECT
date_trunc($4, s.created) AS interval,
SUM(s.revenue) campaign_revenue
FROM
sale_event AS s
WHERE
s.campaignid = $1 AND s.created BETWEEN $2 AND $3 AND s.event_type = 'session_closed'
GROUP BY
interval) results
ON
(results.interval = interval_date);
クエリは、sale_event
テーブルのすべての行を取得し、作成された日付をある間隔に切り捨て(created
タイムスタンプを時系列の望ましい粒度に合わせます)、この時間間隔でグループ化し、event_type
がsession_closed
である行のrevenue
列を合計します。
これは非常にうまく機能し、指定された間隔で収入を得ることができます。結果は次のようになります。
interval_date | campaign_revenue
------------------------------------
2018-08-05 | 0.0
2018-08-06 | 1.5
2018-08-07 | 0.0
2018-08-08 | 0.5
2018-08-09 | 1.0
提供される範囲が2018-08-05 - 2018-08-09
およびinterval = '1 day'
の場合。
その日までの収益の合計を結果に追加したいと思います。したがって、2018-08-05
の前に10.0
の総収益がある場合、結果は次のようになります。
interval_date | campaign_revenue | total_campaign_revenue
-----------------------------------------------------------------
2018-08-05 | 0.0 | 10.0
2018-08-06 | 1.5 | 11.5
2018-08-07 | 0.0 | 11.5
2018-08-08 | 0.5 | 12.0
2018-08-09 | 1.0 | 13.0
私がそれを正しく理解していれば、クエリの外にウィンドウ関数を追加するだけです:
SELECT interval_date, campaign_revenue
, SUM(campaign_revenue) OVER (ORDER BY interval_date)
+ (SELECT SUM(revenue)
FROM sale_event
WHERE s.campaignid = $1
AND s.created < $2
AND s.event_type = 'session_closed') as total_campaign_revenue
FROM (
SELECT interval_date
, coalesce(campaign_revenue,0) AS campaign_revenue
FROM
-- generate_series helps fill the empty gaps in the following JOIN
...
interval) results
ON (results.interval = interval_date)
);
別のオプションは、ウィンドウ関数を直接適用し、campaign_revenue
にFILTER句を使用することです
oneスキャンでは、基になるテーブルから関連するすべての行を読み取る方が高速な場合があります。
そして、同じSELECT
内の集約関数に対してウィンドウ関数を実行できます。
これを EXPLAIN (ANALYZE, TIMING OFF)
でテストして、どちらが速いかを確認します。
_SELECT interval_ts
, coalesce(revenue , 0) AS campaign_revenue
, coalesce(total_revenue, 0) AS total_campaign_revenue
FROM generate_series($2::timestamp, $3::timestamp, $4) AS interval_ts
LEFT JOIN (
SELECT date_trunc($4, created) AS interval_ts
, SUM(revenue) AS revenue
, SUM(SUM(revenue)) OVER (ORDER BY date_trunc($4, created)) AS total_running
FROM sale_event AS s
WHERE campaignid = $1
AND created <= $3 -- read all relevant rows in one scan
AND event_type = 'session_closed'
GROUP BY date_trunc($4, created)
) results USING (interval_ts);
_
JOIN
は、サブクエリの先頭の余剰行を自動的に除外します。
SUM(SUM(revenue)) OVER (ORDER BY date_trunc($4, created))
が機能するのは、 マニュアルの引用 :
デフォルトのフレーミングオプションは_
RANGE UNBOUNDED PRECEDING
_で、これは_RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
_と同じです。 _ORDER BY
_の場合、これはフレームを、パーティションの開始から現在の行の最後の_ORDER BY
_ピアまでのすべての行になるように設定します。
まさにあなたが必要とするもの。
関連:
残りの弱点:収益のないインターバルの合計がありません。それが許容できない場合は、この手法を使用して修正できます。
そう:
_SELECT interval_ts, campaign_revenue, total_revenue
, coalesce(first_value(total_revenue) OVER (PARTITION BY grp ORDER BY interval_ts), 0) AS total_campaign_revenue
FROM (
SELECT interval_ts
, coalesce(revenue, 0) AS campaign_revenue
, total_revenue
, count(total_revenue) OVER (ORDER BY interval_ts) AS grp
FROM (
SELECT interval_ts
, coalesce(revenue, 0) AS campaign_revenue
, count(total_revenue) OVER (ORDER BY interval_ts) AS grp
FROM generate_series($2::timestamp, $3::timestamp, $4) AS interval_ts
LEFT JOIN (
SELECT date_trunc($4, created) AS interval_ts
, SUM(revenue) AS revenue
, SUM(SUM(revenue)) OVER (ORDER BY date_trunc($4, created)) AS total_running
FROM sale_event AS s
WHERE campaignid = $1
AND created <= $3 -- read all relevant rows in one scan
AND event_type = 'session_closed'
GROUP BY date_trunc($4, created)
) results USING (interval_ts)
) sub1
) sub2;
_
オーバーヘッドが追加されたため、競合できるかどうかはわかりません。選択範囲が小さく、テーブルが大きい場合でも、そうなる可能性があります。
マイナーノート:
interval_ts
_の代わりに_interval_date
_を使用します。Postgresで許可されている場合でも、列のエイリアスとしてSQLキーワードinterval
を使用したくありません。
同じ列エイリアス_interval_ts
_を使用して、より短いUSING
構文を許可する-どのかかっこが必要か。これは、結合された列_interval_ts
_のoneインスタンスのみを外部クエリに公開するため、修飾されていない名前があいまいではありません。
AS
キーワードを省略しないでください(テーブルのエイリアスの場合は問題ありません)。