Аз мисля това е, което търсите:
Postgres 13 или по-нов
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 (в момента бета). Вижте:
Postgres 12 или по-стара версия
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 избягва повторното изчисление. Трябва да помогне доста.
Винаги се материализира преди Postgres 12 и трябва да се материализира автоматично в Postgres 12, тъй като се използва много пъти в основната заявка. В противен случай можете да добавите ключовата дума MATERIALIZED
в Postgres 12 или по-нова, за да го принудите. Вижте:
Оптимизираният generate_series()
повикването създава поредица от дни от най-ранния до последния преглед. Вижте:
- Генериране на време серия между две дати в PostgreSQL
- Присъединете се към заявка за преброяване на generate_series в postgres и също така извлечете Null-стойности като "0"
И накрая, LEFT JOIN LATERAL
вече си открил. Но тъй като няколко приложения могат да се обвържат за най-много отзиви, извлечете всички победители, които могат да бъдат 0 - n приложения. Заявката агрегира всички дневни победители в масив, така че получаваме един ред с резултат на review_window_start
. Като алтернатива, дефинирайте тайбрек(ове), за да получите най-много един победител. Вижте: