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

Jak uwzględnić brakujące dane dla wielu grupowań w określonym przedziale czasu?

W oparciu o pewne założenia (niejednoznaczności w pytaniu) proponuję:

SELECT upper(trim(t.full_name)) AS teacher
     , m.study_month
     , r.room_code              AS room
     , count(s.room_id)         AS study_count

FROM   teachers t
CROSS  JOIN generate_series(date_trunc('month', now() - interval '12 month')  -- 12!
                          , date_trunc('month', now())
                          , interval '1 month') m(study_month)
CROSS  JOIN rooms r
LEFT   JOIN (                                                  -- parentheses!
          studies s
   JOIN   teacher_contacts tc ON tc.id = s.teacher_contact_id  -- INNER JOIN!
   ) ON tc.teacher_id = t.id
    AND s.study_dt >= m.study_month
    AND s.study_dt <  m.study_month + interval '1 month'      -- sargable!
    AND s.room_id = r.id
GROUP  BY t.id, m.study_month, r.id  -- id is PK of respective tables
ORDER  BY t.id, m.study_month, r.id;

Główne punkty

  • Zbuduj siatkę wszystkich pożądanych kombinacji za pomocą CROSS JOIN . A potem LEFT JOIN do istniejących wierszy. Powiązane:

  • W twoim przypadku jest to łączenie kilku tabel, więc używam nawiasów w FROM lista do LEFT JOIN do wyniku z INNER JOIN w nawiasach. Byłoby nieprawidłowe do LEFT JOIN do każdej tabeli osobno, ponieważ uwzględniasz trafienia w częściowych dopasowaniach i uzyskujesz potencjalnie nieprawidłowe liczby.

  • Zakładając uczciwość referencyjną i pracując bezpośrednio z kolumnami PK, nie musimy uwzględniać rooms i teachers po lewej stronie po raz drugi. Ale nadal mamy połączenie dwóch tabel (studies i teacher_contacts ). Rola teacher_contacts jest dla mnie niejasne. Normalnie spodziewałbym się związku między studies i teachers bezpośrednio. Może być jeszcze bardziej uproszczony ...

  • Musimy policzyć niezerową kolumnę po lewej stronie, aby uzyskać żądane liczby. Podobnie jak count(s.room_id)

  • Aby zachować szybkość w przypadku dużych tabel, upewnij się, że predykaty są możliwe do sargowania . I dodaj pasujące indeksy .

  • Kolumna teacher jest mało (niezawodnie) wyjątkowy. Działaj z unikalnym identyfikatorem, najlepiej PK (także szybciej i prościej). Nadal używam teacher aby wynik pasował do pożądanego rezultatu. Rozsądne może być dołączenie unikalnego identyfikatora, ponieważ nazwy mogą być duplikatami.

  • Chcesz:

    Zacznij więc od date_trunc('month', now() - interval '12 month' (nie 13). To już zaokrągla początek i robi to, co chcesz - dokładniej niż oryginalne zapytanie.

Ponieważ wspomniałeś o niskiej wydajności, zależnej od rzeczywistych definicji tabel i dystrybucji danych, prawdopodobnie szybciej będzie agregować najpierw, a później dołączyć , jak w tej powiązanej odpowiedzi:

SELECT upper(trim(t.full_name)) AS teacher
     , m.mon                    AS study_month
     , r.room_code              AS room
     , COALESCE(s.ct, 0)        AS study_count

FROM   teachers t
CROSS  JOIN generate_series(date_trunc('month', now() - interval '12 month')  -- 12!
                          , date_trunc('month', now())
                          , interval '1 month') mon
CROSS  JOIN rooms r
LEFT   JOIN (                                                  -- parentheses!
   SELECT tc.teacher_id, date_trunc('month', s.study_dt) AS mon, s.room_id, count(*) AS ct
   FROM   studies s
   JOIN   teacher_contacts tc ON s.teacher_contact_id = tc.id
   WHERE  s.study_dt >= date_trunc('month', now() - interval '12 month')  -- sargable
   GROUP  BY 1, 2, 3
   ) s ON s.teacher_id = t.id
      AND s.mon = m.mon
      AND s.room_id = r.id
ORDER  BY 1, 2, 3;

O Twojej uwadze końcowej:

Są szanse, możesz użyj dwuparametrowej formy crosstab() aby uzyskać pożądany wynik bezpośrednio i z doskonałą wydajnością, a powyższe zapytanie nie jest potrzebne na początku. Rozważ:



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Stopniowa migracja z SQL Server do PostgreSQL

  2. Węzeł korporacyjny PostgreSQL na Corda zgłasza błędy relacji

  3. Plan wykonania funkcji w PostgreSQL

  4. dostosuj pager w psql

  5. Kompromisy we wdrożeniach w trybie gorącej gotowości