PostgreSQL
 sql >> Baza danych >  >> RDS >> PostgreSQL

Agregowanie ostatnich rekordów dołączonych tygodniowo

Potrzebujesz jednego elementu danych na tydzień i celu (przed agregacją liczby na firmę). To jest zwykłe CROSS JOIN między generate_series() i goals . (Prawdopodobnie) kosztowną częścią jest uzyskanie aktualnego state z updates dla każdego. Jak @Paul już zasugerował , LATERAL join wydaje się najlepszym narzędziem. Zrób to tylko dla updates , i użyj szybszej techniki z LIMIT 1 .

I uprość obsługę dat dzięki 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 zrobić to szybko potrzebujesz indeksu wielokolumnowego :

CREATE INDEX updates_special_idx ON updates (goal_id, created_at DESC, status);

Malejąca kolejność dla created_at jest najlepszy, ale nie bezwzględnie konieczny. Postgres może skanować indeksy do tyłu niemal dokładnie tak samo szybko. ( Nie dotyczy jednak odwróconej kolejności sortowania wielu kolumn. )

Indeksuj kolumny w tym zamówienie. Dlaczego?

I trzecia kolumna status jest dołączany tylko w celu umożliwienia szybkiego skanowania tylko do indeksu o updates . Powiązany przypadek:

1000 goli na 9 tygodni (twój interwał 2 miesięcy pokrywa się z co najmniej 9 tygodniami) wymaga jedynie sprawdzenia indeksu 9 tys. dla drugiej tabeli zawierającej tylko 1 tys. wierszy. W przypadku małych tabel, takich jak ta, wydajność nie powinna stanowić większego problemu. Ale gdy będziesz mieć kilka tysięcy więcej w każdej tabeli, wydajność pogorszy się przy skanowaniu sekwencyjnym.

w_start reprezentuje początek każdego tygodnia. W związku z tym liczenia są na początek tygodnia. możesz nadal wyodrębnij rok i tydzień (lub inne szczegóły reprezentują Twój tydzień), jeśli nalegasz:

   EXTRACT(isoyear from w_start) AS year
 , EXTRACT(week    from w_start) AS week

Najlepiej z ISOYEAR , jak wyjaśnił @Paul.

Skrzypce SQL.

Powiązane:



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. PowerShell łączy się z Postgres DB

  2. Hibernacja nie zapisuje obiektu w bazie danych?

  3. Funkcje okien i bardziej lokalna agregacja

  4. Jak połączyć się z localhost za pomocą postgres_fdw?

  5. Problemy z tworzeniem wyzwalaczy PostgreSQL