LoginSignup
4
3

More than 3 years have passed since last update.

DBの縦持ちを横持ちに変換するアイデアについて

Last updated at Posted at 2020-03-18

今回の題材について

今回の例では、以下のような縦持ちのテーブル構造を考える。

帳票管理テーブル

SELECT 社員, 帳票, 帳票作成日, 帳票確認日 FROM テーブル;
No 社員 帳票 帳票作成日 帳票確認日
1 Aさん 招待状 04月01日 04月07日
2 Aさん 招待状 04月08日
3 Aさん 招待状 04月10日
4 Aさん アンケート 05月01日 05月04日
5 Bさん 招待状 10月01日 10月15日
6 Bさん 招待状 10月30日 10月31日

上記のテーブルをシステムでは以下のように表示する。
SQLのみで実現した際に、どのように考えたのかをまとめる。

システムからの表示

No 社員 作成日(招待状) 確認日(招待状) 作成日(アンケート) 確認日(アンケート)
1 Aさん 04月10日 04月07日 05月01日 05月04日
2 Bさん 10月30日 10月31日
  • 社員でグルーピングする。
  • 招待状とアンケートは横持ちで表示する。
  • 各々の作成日と確認日には直近(=最大)の日付を格納する。

上記の例だと、Aさんは最新の招待状を確認していないことになる。

元ネタのテーブルレコードの説明

No.1~3

Aさんに対する招待状が作成されたことを表している。
04月01日~04月10日にかけて、計3回も帳票を作成している。これは、データが更新されたために、帳票を再作成していることを表す。

No.4

Aに対して、アンケートという、招待状とは別の帳票を作成していることを表す。

No.5,6

Bさんに対して招待状が2回作成されている。アンケートは作成されていない。

考え方

その1:求める結果はどの単位でグルーピングされているかを考える。

システムで求める結果をみると、社員単位でグルーピングされていることがわかる。
だが、素直に社員のみでグルーピングしてはいけない。それは、横持ち項目で帳票ごとに直近の日付を求める必要があるからだ。
仮に、帳票単位でグルーピングをしなかった場合、招待状とアンケートの作成日と確認日を判別することは不可能になる。

よって、社員と帳票でグルーピングをする。
グルーピングをすることで、帳票作成日と帳票確認日に対して集合関数が適用できる。

SELECT 社員, 帳票, MAX(帳票作成日), MAX(帳票確認日) FROM テーブル GROUP BY 社員, 帳票
No 社員 帳票 帳票作成日(直近) 帳票確認日(直近)
1 Aさん 招待状 04月10日 04月07日
2 Aさん アンケート 05月01日 05月04日
3 Bさん 招待状 10月30日 10月31日

その2:横持ちの項目を作成する。

横持ちで表示させる項目は「作成日(招待状)」,「確認日(招待状)」,「作成日(アンケート)」,「確認日(アンケート)」である。

SELECT 社員,
       帳票,
       '' as '作成日(招待状)',
       '' as '確認日(招待状)',
       '' as '作成日(アンケート)',
       '' as '確認日(アンケート)'
FROM テーブル 
GROUP BY 社員, 帳票

4つの受け皿には何を格納したらよいか。とりあえず、最大値を表示しておく。

SELECT 
       社員, 
       帳票,
       MAX(帳票作成日) as '作成日(招待状)',
       MAX(帳票確認日) as '確認日(招待状)',
       MAX(帳票作成日) as '作成日(アンケート)',
       MAX(帳票確認日) as '確認日(アンケート)'
FROM テーブル 
GROUP BY 社員, 帳票
No 社員 帳票 作成日(招待状) 確認日(招待状) 作成日(アンケート) 確認日(アンケート)
1 Aさん 招待状 04月10日 04月07日 04月10日 04月07日
2 Aさん アンケート 05月01日 05月04日 05月01日 05月04日
3 Bさん 招待状 10月30日 10月31日 10月30日 10月31日

帳票作成日と確認日を2回ずつ繰り返しているだけなので、招待状の日付がアンケートの項目に表示される。(アンケートも同じ。)
赤文字の表示は正しくないが、一旦保留。

この表は、帳票別にレコードが生成されているため、今だ縦持ちである。
しかし、表示内容が正確ではないにしろ、横持ちの項目を表示することができた。
縦持ちと横持ちの混在とでも表現すべきであろうか。

その3:横持ちの表示内容を修正する。

前述したとおり、横持ち項目の表示が正しくない。
case文を使用して、招待状の日付が、招待状の列だけに表示されるようにする。(アンケートも同じ。)
帳票列も削除してよいだろう。

SELECT 
       社員, 
       --帳票,
       CASE WHEN 帳票 = '招待状'     THEN MAX(帳票作成日) END as '作成日(招待状)',
       CASE WHEN 帳票 = '招待状'     THEN MAX(帳票確認日) END as '確認日(招待状)',
       CASE WHEN 帳票 = 'アンケート' THEN MAX(帳票作成日) END as '作成日(アンケート)',
       CASE WHEN 帳票 = 'アンケート' THEN MAX(帳票確認日) END as '確認日(アンケート)'
FROM テーブル 
GROUP BY 社員, 帳票
No 社員 帳票 作成日(招待状) 確認日(招待状) 作成日(アンケート) 確認日(アンケート)
1 Aさん 招待状 04月10日 04月07日
2 Aさん アンケート 05月01日 05月04日
3 Bさん 招待状 10月30日 10月31日

あと、少しである。

その4:システムが求める形でグルーピングする。

システムが求める表にするためには、社員のみでグルーピングをする必要がある。
その1では、帳票ごとの日付を求めるため、帳票を含めてグルーピングしていた。その理由は、帳票ごとに直近の日付を求める必要があったからである。しかし、その3で、帳票ごとに表示する列を振り分けることができたた。
すなわち、その3で求めた表は社員でグルーピングしても、招待状とアンケートの日付が混同することはない。

SELECT 
       グルーピング社員と帳票.社員,
       MAX([作成日(招待状)])     as '作成日(招待状)',
       MAX([確認日(招待状)])     as '確認日(招待状)',
       MAX([作成日(アンケート)]) as '作成日(アンケート)',
       MAX([確認日(アンケート)]) as '確認日(アンケート)'
FROM (
       SELECT
              社員, 
              CASE WHEN 帳票 = '招待状'     THEN MAX(帳票作成日) END as '作成日(招待状)',
              CASE WHEN 帳票 = '招待状'     THEN MAX(帳票確認日) END as '確認日(招待状)',
              CASE WHEN 帳票 = 'アンケート' THEN MAX(帳票作成日) END as '作成日(アンケート)',
              CASE WHEN 帳票 = 'アンケート' THEN MAX(帳票確認日) END as '確認日(アンケート)'
       FROM テーブル 
       GROUP BY 社員, 帳票
     ) グルーピング社員と帳票
GROUP BY グルーピング社員と帳票.社員

結果(再掲)

No 社員 作成日(招待状) 確認日(招待状) 作成日(アンケート) 確認日(アンケート)
1 Aさん 04月10日 04月07日 05月01日 05月04日
2 Bさん 10月30日 10月31日

おまけ:ウィンドウ関数バージョン

SELECT DISTINCT
       社員,
       MAX([作成日(招待状)]) OVER(PARTITON BY 社員),
       MAX([確認日(招待状)]) OVER(PARTITON BY 社員),
       MAX([作成日(アンケート)]) OVER(PARTITON BY 社員),
       MAX([確認日(アンケート)]) OVER(PARTITON BY 社員)
FROM (
       SELECT DISTINCT
         社員,
         CASE WHEN 帳票 = '招待状'     THEN MAX(帳票作成日) OVER(PARTITON BY 社員, 帳票) END as '作成日(招待状)',
         CASE WHEN 帳票 = '招待状' THEN MAX(帳票作成日) OVER(PARTITON BY 社員, 帳票) END as '確認日(招待状)',
         CASE WHEN 帳票 = 'アンケート'  THEN MAX(帳票作成日) OVER(PARTITON BY 社員, 帳票) END as '作成日(アンケート)',
         CASE WHEN 帳票 = 'アンケート' THEN MAX(帳票作成日) OVER(PARTITON BY 社員, 帳票) END as '確認日(アンケート)'
       FROM テーブル 
     ) グルーピング社員と帳票
4
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3