Je pense voici ce que vous recherchez :
Postgres 13 ou version ultérieure
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
le rend un peu moins cher. Ajouté dans Postgres 13 (actuellement bêta). Voir :
Postgres 12 ou version antérieure
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<>violon ici
Comme ci-dessus, mais sans WITH TIES
.
Nous n'avons pas besoin d'impliquer la table apps
du tout. La table reviews
a toutes les informations dont nous avons besoin.
Le CTE cte
calcule la première révision et le nombre total actuel par application. Le CTE évite les calculs répétés. Cela devrait aider un peu.
Il est toujours matérialisé avant Postgres 12 et devrait être matérialisé automatiquement dans Postgres 12 car il est utilisé plusieurs fois dans la requête principale. Sinon, vous pouvez ajouter le mot clé MATERIALIZED
dans Postgres 12 ou version ultérieure pour le forcer. Voir :
Le generate_series()
optimisé call produit la série de jours de la première à la dernière revue. Voir :
- Temps de génération série entre deux dates dans PostgreSQL
- Rejoindre une requête de comptage sur un generate_series dans postgres et également récupérer les valeurs Null comme "0"
Enfin, le LEFT JOIN LATERAL
vous avez déjà découvert. Mais comme plusieurs applications peuvent être liées pour le plus grand nombre d'avis, récupérez tous les gagnants, qui peuvent être de 0 à n applications. La requête regroupe tous les gagnants quotidiens dans un tableau, nous obtenons donc une seule ligne de résultat par review_window_start
. Vous pouvez également définir des conditions de départage pour en obtenir au plus un gagnant. Voir :