Potřebujete jednu datovou položku na týden a cíl (před agregací počtů na společnost). To je prostý CROSS JOIN
mezi generate_series()
a goals
. (Možná) nákladná část je získat aktuální state
z updates
pro každého. Líbí se mi @Paul již navrhl
, LATERAL
join se zdá jako nejlepší nástroj. Udělejte to pouze pro updates
a použijte rychlejší techniku s LIMIT 1
.
A zjednodušit zpracování dat pomocí 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;
Aby to bylo rychlé potřebujete vícesloupcový index :
CREATE INDEX updates_special_idx ON updates (goal_id, created_at DESC, status);
Sestupné pořadí pro created_at
je nejlepší, ale není nezbytně nutný. Postgres dokáže skenovat indexy pozpátku téměř stejně rychle. ( Nelze však použít pro obrácené pořadí řazení více sloupců.
)
Indexujte sloupce v tom objednat. Proč?
A třetí sloupec state
je připojen pouze proto, aby umožňoval rychlé prohledávání pouze na základě indexu
na updates
. Související případ:
1 000 cílů za 9 týdnů (váš interval 2 měsíců se překrývá s alespoň 9 týdny) vyžaduje pouze 9 000 vyhledání indexu pro 2. tabulku pouze o 1 000 řádcích. U malých stolů, jako je tento, by výkon neměl být velkým problémem. Ale jakmile budete mít v každé tabulce o pár tisíc více, výkon se bude sekvenčním skenováním zhoršovat.
w_start
představuje začátek každého týdne. V důsledku toho jsou počty pro začátek týdne. můžete stále extrahujte rok a týden (nebo jakékoli jiné podrobnosti představující váš týden), pokud na tom trváte:
EXTRACT(isoyear from w_start) AS year
, EXTRACT(week from w_start) AS week
Nejlepší s ISOYEAR
, jak vysvětlil @Paul.
Související:
- Jaký je rozdíl mezi LATERAL a dílčím dotazem v PostgreSQL?
- Optimalizujte dotaz GROUP BY pro získání nejnovějšího záznamu na uživatele
- Vyberte nejdříve řádek v každé skupině GROUP BY?
- PostgreSQL:průběžný počet řádků pro dotaz „po minutách“