U hebt één gegevensitem per week en doel nodig (voordat u de tellingen per bedrijf aggregeert). Dat is een eenvoudige CROSS JOIN
tussen generate_series()
en goals
. Het (mogelijk) dure deel is om de huidige state
te krijgen van updates
voor elk. Vind ik leuk @Paul heeft al voorgesteld
, een LATERAL
join lijkt de beste tool. Doe het alleen voor updates
, en gebruik een snellere techniek met LIMIT 1
.
En vereenvoudig datumafhandeling met 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;
Om dit snel te maken je hebt een index met meerdere kolommen . nodig :
CREATE INDEX updates_special_idx ON updates (goal_id, created_at DESC, status);
Aflopende volgorde voor created_at
is het beste, maar niet strikt noodzakelijk. Postgres kan indexen bijna net zo snel achteruit scannen. ( Niet van toepassing op omgekeerde sorteervolgorde van meerdere kolommen.
)
Indexkolommen in dat bestellen. Waarom?
En de derde kolom state
wordt alleen toegevoegd om snelle alleen-index scans
toe te staan op updates
. Verwant geval:
1k-doelen voor 9 weken (uw interval van 2 maanden overlapt met ten minste 9 weken) vereisen alleen 9k index-look-ups voor de 2e tabel van slechts 1k rijen. Voor kleine tabellen als deze zou de prestatie niet zo'n probleem moeten zijn. Maar zodra je er een paar duizend meer in elke tabel hebt, zullen de prestaties verslechteren bij opeenvolgende scans.
w_start
staat voor het begin van elke week. De tellingen zijn dus voor het begin van de week. Je kunt haal nog steeds het jaar en de week uit (of andere details die uw week vertegenwoordigen), als u erop staat:
EXTRACT(isoyear from w_start) AS year
, EXTRACT(week from w_start) AS week
Het beste met ISOYEAR
, zoals @Paul heeft uitgelegd.
Gerelateerd:
- Wat is het verschil tussen LATERAL en een subquery in PostgreSQL?
- Optimaliseer de GROUP BY-query om het laatste record per gebruiker op te halen
- Selecteer eerst rij in elke GROUP BY-groep?
- PostgreSQL:lopende telling van rijen voor een zoekopdracht 'per minuut'