PostgreSQL
 sql >> база данни >  >> RDS >> PostgreSQL

Как да включите липсващи данни за множество групи в рамките на времевия период?

Въз основа на някои предположения (неясноти във въпроса) предлагам:

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;

Основни точки

  • Изградете мрежа от всички желани комбинации с CROSS JOIN . И след това LEFT JOIN към съществуващи редове. Свързани:

  • Във вашия случай това е обединение на няколко таблици, така че използвам скоби в FROM списък към LEFT JOIN към резултата на INNER JOIN в скобите. Би било неправилно към LEFT JOIN към всяка таблица поотделно, тъй като ще включите попадения на частични съвпадения и ще получите потенциално неправилен брой.

  • Приемане на референтна цялост и работейки директно с PK колони, не е необходимо да включваме rooms и teachers от лявата страна втори път. Но все още имаме съединяване на две таблици (studies и teacher_contacts ). Ролята на teacher_contacts не ми е ясно. Обикновено бих очаквал връзка между studies и teachers директно. Може да бъде допълнително опростено...

  • Трябва да преброим ненулева колона от лявата страна, за да получим желаните преброявания. Като count(s.room_id)

  • За да поддържате това бързо за големи таблици, уверете се, че вашите предикати са sargable . И добавете съответстващи индекси .

  • Колоната teacher едва ли е (надеждно) уникален. Работете с уникален идентификатор, за предпочитане PK (също по-бързо и по-лесно). Все още използвам teacher така че изходът да съответства на желания резултат. Може да е разумно да включите уникален идентификатор, тъй като имената могат да се дублират.

  • Искате:

    Затова започнете с date_trunc('month', now() - interval '12 month' (не 13). Това вече закръглява началото и прави това, което искате - по-точно от първоначалната ви заявка.

Тъй като споменахте бавна производителност, в зависимост от действителните дефиниции на таблици и разпространението на данни, вероятно е по-бързо да първо обобщите и да се присъедините по-късно , като в този свързан отговор:

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;

За заключителната ви бележка:

Вероятно можете използвайте формата с два параметъра на crosstab() за да произведете желания резултат директно и с отлична производителност и горната заявка не е необходима като начало. Помислете за следното:



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Използване на кеширане на pg_prewarm и pg_hibernator на contrib в PostgreSQL 9.4.

  2. Форматиране на дата (ГГ:ММ:ДД:Час) в Excel

  3. Пълно копиране на таблица на postgres с SQL

  4. Вземете стойност на полето от запис, който кара агрегатно условие да бъде вярно

  5. Оптимизиране на груповата максимална заявка