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 左側にもう一度。しかし、まだ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パラメータ形式を使用します 目的の結果を直接かつ優れたパフォーマンスで生成するために、上記のクエリを最初から行う必要はありません。検討してください:



    1. 月をテキストに変換しながら並べ替える

    2. MySQLを使用して複数の列でGROUPBYすることは可能ですか?

    3. 日付を事前定義された形式と比較するplsql

    4. mysqlINNODBAUTO_INCREMENTテーブル値の設定で変数を使用する