Ik denk dit is wat je zoekt:
Postgres 13 of nieuwer
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
maakt het wat goedkoper. Toegevoegd in Postgres 13 (momenteel bèta). Zie:
Postgres 12 of ouder
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 hier
Hetzelfde als hierboven, maar zonder WITH TIES
.
We hoeven de tabel apps
niet te betrekken helemaal niet. De tabel reviews
heeft alle informatie die we nodig hebben.
De CTE cte
berekent de vroegste beoordeling en het huidige totale aantal per app. De CTE vermijdt herhaalde berekeningen. Zou aardig wat moeten helpen.
Het wordt altijd gematerialiseerd vóór Postgres 12 en zou automatisch moeten worden gematerialiseerd in Postgres 12, omdat het vaak wordt gebruikt in de hoofdquery. Anders kunt u het trefwoord MATERIALIZED
toevoegen
in Postgres 12 of later om het te forceren. Zie:
De geoptimaliseerde generate_series()
call produceert de reeks dagen van de eerste tot de laatste beoordeling. Zie:
- Tijd genereren serie tussen twee datums in PostgreSQL
- Doe mee aan een count-query op een generator_series in postgres en haal ook Null-waarden op als "0"
Ten slotte, de LEFT JOIN LATERAL
je hebt het al ontdekt. Maar aangezien meerdere apps kunnen binden voor de meeste beoordelingen haalt u alle winnaars op, dit kunnen 0 - n apps zijn. De zoekopdracht verzamelt alle dagelijkse winnaars in een array, zodat we één resultaatrij per review_window_start
krijgen. . U kunt ook tiebreaker(s) definiëren om maximaal één te krijgen winnaar. Zie: