私は考えます これがあなたが探しているものです:
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つを取得します 勝者。参照: