Имате нужда от един елемент от данни на седмица и цел (преди обобщаване на броя за компания). Това е обикновен CROSS JOIN
между generate_series()
и goals
. (Възможно) скъпата част е да получите текущото state
от updates
за всеки. Като @Paul вече е предложен
, LATERAL
join изглежда като най-добрият инструмент. Правете го само за updates
, но използвайте по-бърза техника с LIMIT 1
.
И опростете обработката на дати с date_trunc()
.
SELECT w_start
, g.company_id
, count(*) FILTER (WHERE u.status = 'green') AS green_count
, count(*) FILTER (WHERE u.status = 'amber') AS amber_count
, count(*) FILTER (WHERE u.status = 'red') AS red_count
FROM generate_series(date_trunc('week', NOW() - interval '2 months')
, date_trunc('week', NOW())
, interval '1 week') w_start
CROSS JOIN goals g
LEFT JOIN LATERAL (
SELECT status
FROM updates
WHERE goal_id = g.id
AND created_at < w_start
ORDER BY created_at DESC
LIMIT 1
) u ON true
GROUP BY w_start, g.company_id
ORDER BY w_start, g.company_id;
За да направите товабързо имате нужда от многоколонен индекс :
CREATE INDEX updates_special_idx ON updates (goal_id, created_at DESC, status);
Низходящ ред за created_at
е най-добре, но не е строго необходимо. Postgres може да сканира индекси назад почти точно толкова бързо. ( Но не е приложимо за обърнат ред на сортиране на множество колони.
)
Индексирайте колони в това поръчка. Защо?
И третата колона status
се добавя само за да позволи бързо сканиране само за индекс
на updates
. Свързан случай:
1k цели за 9 седмици (вашият интервал от 2 месеца се припокрива с поне 9 седмици) изискват само 9k индексни справки за втората таблица от само 1k реда. За малки маси като тази производителността не би трябвало да е голям проблем. Но след като имате няколко хиляди повече във всяка таблица, производителността ще се влоши при последователни сканирания.
w_start
представлява началото на всяка седмица. Следователно преброяването е за началото на седмицата. Вие можете все още извличайте година и седмица (или всякакви други подробности, представляващи вашата седмица), ако настоявате:
EXTRACT(isoyear from w_start) AS year
, EXTRACT(week from w_start) AS week
Най-добро с ISOYEAR
, както @Paul обясни.
Свързани:
- Каква е разликата между LATERAL и подзаявка в PostgreSQL?
- Оптимизирайте заявката GROUP BY, за да извлечете последния запис на потребител
- Изберете първо ред във всяка група GROUP BY?
- PostgreSQL:текущо преброяване на редове за заявка "по минути"