sql >> Database teknologi >  >> RDS >> PostgreSQL

Samling af de seneste tilknyttede rekorder om ugen

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 indeksupdates . 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.

SQL Fiddle.

Relateret:



  1. Hvordan man selv opdaterer PHP+MySQL CMS?

  2. vælg max- og min-værdier for hver x-mængde af rows-postgresql

  3. Hvad er forskellene mellem INSERT og UPDATE i MySQL?

  4. hvilken database der skal bruges til selvstændige applikationer Winforms