Jeg tror dette er hvad du leder efter:
Postgres 13 eller nyere
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
gør det lidt billigere. Tilføjet i Postgres 13 (i øjeblikket beta). Se:
Postgres 12 eller ældre
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 her
Samme som ovenfor, men uden WITH TIES
.
Vi behøver ikke at involvere tabellen apps
overhovedet. Tabellen reviews
har alle de oplysninger, vi har brug for.
CTE cte
beregner den tidligste anmeldelse og det aktuelle samlede antal pr. app. CTE undgår gentagen beregning. Det burde hjælpe en del.
Det er altid materialiseret før Postgres 12, og bør materialiseres automatisk i Postgres 12, da det bruges mange gange i hovedforespørgslen. Ellers kan du tilføje søgeordet MATERIALIZED
i Postgres 12 eller senere for at tvinge det. Se:
Den optimerede generate_series()
call producerer rækken af dage fra tidligst til seneste anmeldelse. Se:
- Genereringstid serie mellem to datoer i PostgreSQL
- Deltag i en optællingsforespørgsel på en generate_series i postgres og hent også Null-værdier som "0"
Til sidst, LEFT JOIN LATERAL
du allerede har opdaget. Men da flere apps kan knyttes for de fleste anmeldelser, hent alle vindere, som kan være 0 - n apps. Forespørgslen samler alle daglige vindere i en matrix, så vi får en enkelt resultatrække pr. review_window_start
. Alternativt kan du definere tiebreaker for at få højst én vinder. Se: