私は考えます これがあなたが探しているものです:
Postgres13以降
WITH cte AS ( -- MATERIALIZED
SELECT app_id, min(review_date) AS earliest_review, count(*)::int AS total_ct
FROM reviews
GROUP BY 1
)
SELECT *
FROM (
SELECT generate_series(min(review_date)
, max(review_date)
, '1 day')::date
FROM reviews
) d(review_window_start)
LEFT JOIN LATERAL (
SELECT total_ct, array_agg(app_id) AS apps
FROM (
SELECT app_id, total_ct
FROM cte c
WHERE c.earliest_review >= d.review_window_start
ORDER BY total_ct DESC
FETCH FIRST 1 ROWS WITH TIES -- new & hot
) sub
GROUP BY 1
) a ON true;
WITH TIES
少し安くなります。 Postgres 13(現在はベータ版)で追加されました。参照:
Postgres12以前
WITH cte AS ( -- MATERIALIZED
SELECT app_id, min(review_date) AS earliest_review, count(*)::int AS total_ct
FROM reviews
GROUP BY 1
)
SELECT *
FROM (
SELECT generate_series(min(review_date)
, max(review_date)
, '1 day')::date
FROM reviews
) d(review_window_start)
LEFT JOIN LATERAL (
SELECT total_ct, array_agg(app_id) AS apps
FROM (
SELECT total_ct, app_id
, rank() OVER (ORDER BY total_ct DESC) AS rnk
FROM cte c
WHERE c.earliest_review >= d.review_window_start
) sub
WHERE rnk = 1
GROUP BY 1
) a ON true;
db <> fiddle こちら
上記と同じですが、WITH TIES
はありません 。
テーブルapps
を含める必要はありません まったく。テーブルのreviews
必要なすべての情報があります。
CTE cte
アプリごとの最も早いレビューと現在の合計数を計算します。 CTEは、繰り返しの計算を回避します。かなり役立つはずです。
これは常にPostgres12の前に具体化され、メインクエリで何度も使用されるため、Postgres12で自動的に具体化される必要があります。それ以外の場合は、キーワードMATERIALIZED
を追加できます。
Postgres12以降でそれを強制します。参照:
最適化されたgenerate_series()
callは、最初のレビューから最新のレビューまでの一連の日を生成します。参照:
最後に、LEFT JOIN LATERAL
あなたはすでに発見しました。ただし、複数のアプリが結びつく可能性がある ほとんどのレビューについては、0〜n個のアプリである可能性があるすべての勝者を取得します。クエリはすべての毎日の勝者を配列に集約するため、review_window_start
ごとに1つの結果行を取得します 。または、タイブレーカーを定義して、最大で1つを取得します 勝者。参照: