sql >> Databáze >  >> RDS >> PostgreSQL

Jak zahrnout chybějící data pro více seskupení v časovém rozpětí?

Na základě některých předpokladů (nejednoznačností v otázce) navrhuji:

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;

Hlavní body

  • Vytvořte mřížku všech požadovaných kombinací pomocí CROSS JOIN . A pak LEFT JOIN do stávajících řádků. Související:

  • Ve vašem případě se jedná o spojení několika tabulek, proto používám závorky v FROM seznam LEFT JOIN k výsledku z INNER JOIN v závorkách.Bylo by to nesprávné do LEFT JOIN ke každé tabulce zvlášť, protože byste zahrnuli výsledky dílčích zápasů a získali potenciálně nesprávné počty.

  • Za předpokladu referenční integrity a pracujeme přímo se sloupci PK, nemusíme zahrnovat rooms a teachers na levé straně podruhé. Ale stále máme spojení dvou tabulek (studies a teacher_contacts ). Role teacher_contacts je mi nejasný. Normálně bych očekával vztah mezi studies a teachers přímo. Mohlo by být dále zjednodušeno ...

  • Potřebujeme spočítat nenulový sloupec na levé straně, abychom získali požadované počty. Jako count(s.room_id)

  • Abyste to u velkých stolů udrželi rychle, ujistěte se, že vaše predikáty jsou sargable . A přidejte odpovídající indexy .

  • Sloupec teacher je stěží (spolehlivě) unikátní. Pracujte s jedinečným ID, nejlépe PK (rovněž rychlejší a jednodušší). Stále používám teacher aby výstup odpovídal požadovanému výsledku. Může být rozumné zahrnout jedinečné ID, protože jména mohou být duplicitní.

  • Chcete:

    Začněte tedy s date_trunc('month', now() - interval '12 month' (ne 13). To už začátek zaokrouhluje dolů a dělá to, co chcete – přesněji než váš původní dotaz.

Vzhledem k tomu, že jste zmínil pomalý výkon v závislosti na skutečných definicích tabulek a distribuci dat, je pravděpodobně rychlejší nejprve agregovat a později se připojit , jako v této související odpovědi:

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 vaší závěrečné poznámce:

Je pravděpodobné, že můžete použijte dvouparametrovou formu crosstab() k dosažení požadovaného výsledku přímo a s vynikajícím výkonem a výše uvedený dotaz není pro začátek potřeba. Zvažte:



  1. Jak mohu zjistit, zda je v Codeigniter úspěšný dotaz na vytvoření, aktualizaci nebo odstranění

  2. Selhání aplikace Qt při použití ovladače ODBC (macOS)

  3. Jak importovat výpis Heroku PG do místního počítače

  4. Získejte typ vrácených dat v dotazu MySQL