Du skal bruge ét dataelement pr. uge og mål (før du samler tæller pr. virksomhed). Det er en almindelig CROSS JOIN
mellem generate_series()
og goals
. Den (muligvis) dyre del er at få den nuværende state
fra updates
for hver. Som @Paul allerede foreslået
, en LATERAL
join virker som det bedste værktøj. Gør det kun for updates
, dog, og brug en hurtigere teknik med LIMIT 1
.
Og forenkle datohåndtering med 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;
For at gøre dette hurtigt du har brug for et flerkolonneindeks :
CREATE INDEX updates_special_idx ON updates (goal_id, created_at DESC, status);
Faldende rækkefølge for created_at
er bedst, men ikke strengt nødvendigt. Postgres kan scanne indeks baglæns næsten nøjagtigt lige så hurtigt. ( Ikke relevant for omvendt sorteringsrækkefølge af flere kolonner.
)
Indeks kolonner i det bestille. Hvorfor?
Og den tredje kolonne status
er kun tilføjet for at tillade hurtige scanninger kun for indeks
på updates
. Relateret sag:
1.000 mål i 9 uger (dit interval på 2 måneder overlapper med mindst 9 uger) kræver kun 9.000 indeksopslag for den 2. tabel med kun 1.000 rækker. For små borde som dette burde ydeevnen ikke være det store problem. Men når først du har et par tusinde mere i hver tabel, forringes ydeevnen med sekventielle scanninger.
w_start
repræsenterer starten på hver uge. Følgelig er tæller til starten af ugen. Du kan udtræk stadig år og uge (eller andre detaljer repræsenterer din uge), hvis du insisterer:
EXTRACT(isoyear from w_start) AS year
, EXTRACT(week from w_start) AS week
Bedst med ISOYEAR
, som @Paul forklarede.
Relateret:
- Hvad er forskellen mellem LATERAL og en underforespørgsel i PostgreSQL?
- Optimer GROUP BY-forespørgsel for at hente seneste registrering pr. bruger
- Vælg først række i hver GRUPPE FOR gruppe?
- PostgreSQL:kørende antal rækker for en forespørgsel 'efter minut'