背景
レコードにセットされている値によって、出力や処理を変化させたい場合があります。select文のcase式が典型例です。ですがselectしたレコードが存在しない場合があります。selectしたレコードの値をチェックしようとしているのに照会結果がない場合、期待しない結果になります。
やりたいこと
レコードがない場合でも必ずレコードを返すことにより値の真偽を確認したい。
※IBM Db2 V11.5 Windowsで確認していますが他のDBMSでもできるはずです。
環境
テーブルは3つです。
CFG_CD CFG_TEXT CFG_DATE
------ -------------------- ----------
1 誕生日を表示 2018-09-30
2 メールアドレスを表示 2018-09-30
USR_ID CFG_CD CFG_BOOLEAN
----------- ------ -----------
1 1 1
1 2 0
2 1 1
2 2 0
3 1 0
3 2 1
USR_ID USR_NAME USR_BIRTH USR_MAIL
----------- ---------------- ---------- --------------------
1 東京 太郎 1999-05-07 taro@hogehoge.hoge
2 愛知 花子 2001-09-15 hanako@hogehoge.hoge
3 大阪 一郎 1987-12-01 ichiro@hogehoge.hoge
SQL
例ではct.cfg_booleanに対してcase式を記述したselect文です。東京太郎さんの設定情報を取得します。
SELECT um.usr_name, cm.cfg_cd, cm.cfg_text, ct.cfg_boolean,
CASE ct.cfg_boolean
WHEN true THEN '表示'
ELSE '非表示'
END AS display
FROM cfg_tbl ct, cfg_mst cm, usr_mst um
WHERE ct.cfg_cd = cm.cfg_cd
AND ct.usr_id = um.usr_id
AND ct.usr_id = 1
ORDER BY cm.cfg_cd ;
実行結果は以下になります。
C:\_work>db2 -tvf select_cfg_tbl_1.sql
SELECT um.usr_name, cm.cfg_cd, cm.cfg_text, ct.cfg_boolean, CASE ct.cfg_boolean WHEN true THEN '表示' ELSE '非表示' END AS display FROM cfg_tbl ct, cfg_mst cm, usr_mst um WHERE ct.cfg_cd = cm.cfg_cd AND ct.usr_id = um.usr_id AND ct.usr_id = 1 ORDER BY cm.cfg_cd
USR_NAME CFG_CD CFG_TEXT CFG_BOOLEAN DISPLAY
---------------- ------ -------------------- ----------- -------
東京 太郎 1 誕生日を表示 1 表示
東京 太郎 2 メールアドレスを表示 0 非表示
2 レコードが選択されました。
ct.cfg_booleanがture(1)なら'表示'、そうでなければ、'非表示'を出力します。
問題点
アプリケーションの機能追加の結果、マスターテーブルにレコードを追加することはよくあることです。例は設定マスターに「自己紹介を表示」という3つ目のレコードを追加しています。
CFG_CD CFG_TEXT CFG_DATE
------ -------------------- ----------
1 誕生日を表示 2018-09-30
2 メールアドレスを表示 2018-09-30
3 自己紹介を表示 2020-12-25 <-- 新しく追加したレコード
先ほどのSQLを再度実行します。
C:\_work>db2 -tvf select_cfg_tbl_1.sql
SELECT um.usr_name, cm.cfg_cd, cm.cfg_text, ct.cfg_boolean, CASE ct.cfg_boolean WHEN true THEN '表示' ELSE '非表示' END AS display FROM cfg_tbl ct, cfg_mst cm, usr_mst um WHERE ct.cfg_cd = cm.cfg_cd AND ct.usr_id = um.usr_id AND ct.usr_id = 1 ORDER BY cm.cfg_cd
USR_NAME CFG_CD CFG_TEXT CFG_BOOLEAN DISPLAY
---------------- ------ -------------------- ----------- -------
東京 太郎 1 誕生日を表示 1 表示
東京 太郎 2 メールアドレスを表示 0 非表示
2 レコードが選択されました。
結果は変わりません。データである設定情報(CFG_TBL)にCFG_CDが3のレコードがないため、テーブルを連結すると「自己紹介を表示」という3つ目のレコードは表示されません。 ですが、私の期待する実行結果は3つ目の設定マスターのレコードとcase式で記述したDISPLAY列の値が返ってきて欲しいです。
USR_NAME CFG_CD CFG_TEXT CFG_BOOLEAN DISPLAY
---------------- ------ -------------------- ----------- -------
東京 太郎 1 誕生日を表示 1 表示
東京 太郎 2 メールアドレスを表示 0 非表示
- 3 自己紹介を表示 - 非表示 <-- レコードがないなら非表示を出力
対策
Left Joinします。まずは、設定マスター(CFG_MST)を元に設定情報(CFG_TBL)とLeft Joinします。
SELECT ct.usr_id, cm.cfg_cd, cm.cfg_text, ct.cfg_boolean,
CASE ct.cfg_boolean
WHEN true THEN '表示'
ELSE '非表示'
END AS display
FROM cfg_mst cm
LEFT JOIN (SELECT *
FROM cfg_tbl
WHERE usr_id = 1) ct
ON cm.cfg_cd = ct.cfg_cd
ORDER BY cm.cfg_cd ;
実行結果は以下になります。設定情報(CFG_TBL)のレコードがない行の列はNULL(-) になります。
C:\_work>db2 -tvf select_cfg_tbl_1_LeftJoin.sql
SELECT ct.usr_id, cm.cfg_cd, cm.cfg_text, ct.cfg_boolean, CASE ct.cfg_boolean WHEN true THEN '表示' ELSE '非表示
' END AS display FROM cfg_mst cm LEFT JOIN (SELECT * FROM cfg_tbl WHERE usr_id = 1) ct ON cm.cfg_cd = ct.cfg_cd ORDER BY cm.cfg_cd
USR_ID CFG_CD CFG_TEXT CFG_BOOLEAN DISPLAY
----------- ------ -------------------- ----------- -------
1 1 誕生日を表示 1 表示
1 2 メールアドレスを表示 0 非表示
- 3 自己紹介を表示 - 非表示
3 レコードが選択されました。
CFG_BOOLEAN列もNULLなのでcase式はtrue(1)以外になり、DISPLAY列は非表示が出力されます。
さらに
さらにユーザー(USR_MST)も連結します。
SELECT ct.usr_name, cm.cfg_cd, cm.cfg_text, ct.cfg_boolean,
CASE ct.cfg_boolean
WHEN true THEN '表示'
ELSE '非表示'
END AS display
FROM cfg_mst cm
LEFT JOIN (SELECT *
FROM cfg_tbl t1, usr_mst t2
WHERE t1.usr_id = t2.usr_id
AND t1.usr_id = 1) ct
ON cm.cfg_cd = ct.cfg_cd
ORDER BY cm.cfg_cd ;
実行結果は以下になります。
C:\_work>db2 -tvf select_cfg_tbl_1_LeftJoin_mod.sql
SELECT ct.usr_name, cm.cfg_cd, cm.cfg_text, ct.cfg_boolean, CASE ct.cfg_boolean WHEN true THEN '表示' ELSE '非表示' END AS display FROM cfg_mst cm LEFT JOIN (SELECT * FROM cfg_tbl t1, usr_mst t2 WHERE t1.usr_id = t2.usr_id AND t1.usr_id = 1) ct ON cm.cfg_cd = ct.cfg_cd ORDER BY cm.cfg_cd
USR_NAME CFG_CD CFG_TEXT CFG_BOOLEAN DISPLAY
---------------- ------ -------------------- ----------- -------
東京 太郎 1 誕生日を表示 1 表示
東京 太郎 2 メールアドレスを表示 0 非表示
- 3 自己紹介を表示 - 非表示
3 レコードが選択されました。
期待した結果が返ってきました。設定マスター(CFG_MST)の行はすべて出力させ、他のテーブルでレコードがない場合はNULLをセットしたレコードで出力しています。DISPLAY列は非表示が出力されます。
まとめ
- レコードが存在しない場合は列にNULLをセットしたレコードを返す
- Left Joinでもとになるテーブルを決める