sql >> Databasteknik >  >> RDS >> PostgreSQL

Hur inkluderar man saknad data för flera grupperingar inom tidsperioden?

Baserat på några antaganden (otydligheter i frågan) föreslår jag:

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;

Huvudpunkter

  • Bygg ett rutnät av alla önskade kombinationer med CROSS JOIN . Och sedan LEFT JOIN till befintliga rader. Relaterat:

  • I ditt fall är det en sammanfogning av flera tabeller, så jag använder parenteser i FROM lista till LEFT JOIN till resultatet av INNER JOIN inom parentes. Det skulle vara felaktigt till LEFT JOIN till varje bord separat, eftersom du skulle inkludera träffar på partiella matchningar och få potentiellt felaktiga räkningar.

  • Förutsatt referensintegritet och när vi arbetar med PK-kolumner direkt, behöver vi inte inkludera rooms och teachers på vänster sida en andra gång. Men vi har fortfarande en sammanfogning av två tabeller (studies och teacher_contacts ). Rollen för teacher_contacts är oklart för mig. Normalt skulle jag förvänta mig ett förhållande mellan studies och teachers direkt. Kan förenklas ytterligare ...

  • Vi måste räkna en kolumn som inte är noll på vänster sida för att få önskat antal. Som count(s.room_id)

  • För att hålla detta snabbt för stora bord, se till att dina predikat är sargable . Och lägg till matchande index .

  • Kolumnen teachers är knappast (pålitligt) unik. Arbeta med ett unikt ID, helst PK (snabbare och enklare också). Jag använder fortfarande teachers för att resultatet ska matcha ditt önskade resultat. Det kan vara klokt att inkludera ett unikt ID, eftersom namn kan vara dubbletter.

  • Du vill:

    Så börja med date_trunc('month', now() - interval '12 month' (inte 13). Det avrundar redan början och gör vad du vill - mer exakt än din ursprungliga fråga.

Eftersom du nämnde långsam prestanda, beroende på faktiska tabelldefinitioner och datadistribution, är det förmodligen snabbare att aggregera först och gå med senare , som i det här relaterade svaret:

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;

Om din avslutande kommentar:

Chansen är stor att du kan använd tvåparametersformen crosstab() för att producera ditt önskade resultat direkt och med utmärkt prestanda och ovanstående fråga behövs inte till att börja med. Tänk på:



  1. SQL Server motsvarande MySQL enum datatyp?

  2. Använda databasscheman i SQL Server

  3. T-SQL:söker efter e-postformat

  4. gå igenom flerdimensionell array i php och gör mysql insert (stock data)