Eu acho é isso que você procura:
Postgres 13 ou mais recente
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
deixa um pouco mais barato. Adicionado no Postgres 13 (atualmente beta). Ver:Postgres 12 ou mais antigo
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 aqui
Igual ao anterior, mas sem
WITH TIES
. Não precisamos envolver a tabela
apps
de forma alguma. A tabela reviews
tem todas as informações que precisamos. O CTE
cte
calcula a primeira revisão e a contagem total atual por aplicativo. O CTE evita cálculos repetidos. Deve ajudar bastante.É sempre materializado antes do Postgres 12, e deve ser materializado automaticamente no Postgres 12, pois é usado muitas vezes na consulta principal. Caso contrário, você pode adicionar a palavra-chave
MATERIALIZED
no Postgres 12 ou posterior para forçá-lo. Ver:O
generate_series()
otimizado chamada produz a série de dias da primeira à última revisão. Ver:- Gerando hora série entre duas datas no PostgreSQL
- Junte-se a uma consulta de contagem em um generate_series no postgres e também recupere valores nulos como "0"
Finalmente, o
LEFT JOIN LATERAL
você já descobriu. Mas como vários aplicativos podem vincular para o maior número de avaliações, recupere todos os vencedores, que podem ser 0 - n apps. A consulta agrega todos os vencedores diários em uma matriz, de modo que obtemos uma única linha de resultado por review_window_start
. Como alternativa, defina o(s) desempate(s) para obter no máximo um vencedora. Ver: