いくつかの仮定(質問のあいまいさ)に基づいて、私は提案します:
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
左側にもう一度。しかし、まだ2つのテーブルの結合があります(studies
およびteacher_contacts
)。teacher_contacts
の役割 私にはわかりません。通常、私はstudies
間の関係を期待します およびteachers
直接。さらに簡略化される可能性があります... -
必要な数を取得するには、左側のnull以外の列を数える必要があります。
count(s.room_id)
のように -
大きなテーブルでこれを高速に保つには、述語が sargableであることを確認してください。 。そして、一致するインデックスを追加します 。
-
列
teacher
ほとんど(確実に)ユニークではありません。一意のID、できればPK(高速でシンプル)で操作します。私はまだteacher
を使用しています 出力が目的の結果と一致するようにします。名前は重複する可能性があるため、一意のIDを含めることをお勧めします。 -
必要なもの:
したがって、
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()
の2パラメータ形式を使用します 目的の結果を直接かつ優れたパフォーマンスで生成するために、上記のクエリを最初から行う必要はありません。検討してください: