今回はPostgreSQLのCASEを使い、条件によって返す値を変えます。取得したデータの意味を整理します。
所要時間は15分ほどです。
それでは、さっそく始めていきましょう!
用語と到達目標
CASEは、条件を上から順に確認し、最初にTRUEとなった条件に対応する値を返すSQLの式です。TRUEは、条件が成り立つことを表します。
CASE
WHEN 条件 THEN 条件を満たした場合の値
ELSE それ以外の場合の値
END
| 記述 | 役割 |
|---|---|
| WHEN | 判定する条件を書く |
| THEN | その条件がTRUEの場合に返す値を書く |
| ELSE | どのWHENもTRUEにならなかった場合の値を書く |
| END | CASE式を終える |
今回の目標は、NULLかどうかと日付の範囲を使って、表示する文字列を分けられることです。
今回使うデータ
取得元はmobility.poisです。1行が1施設を表します。
| 列名 | 意味 |
|---|---|
| poi_id | 施設を識別するID |
| poi_name | 施設名 |
| closed_date | 閉業日を表す日付型の列 |
演習では、施設IDが1・39・41の3施設を使いました。
演習①:日付の有無で表示を分ける
次のSQLを実行しました。
SELECT
poi_id,
poi_name,
closed_date,
CASE
WHEN closed_date IS NULL THEN '日付なし'
ELSE '日付あり'
END AS closed_date_status
FROM mobility.pois
WHERE poi_id IN (1, 39, 41)
ORDER BY poi_id;
実行結果は次の3行でした。
| poi_id | poi_name | closed_date | closed_date_status |
|---|---|---|---|
| 1 | Tokyo_station_001 | NULL(画面では空欄) | 日付なし |
| 39 | Omiya_hotel_039 | 2020-02-24 | 日付あり |
| 41 | Tokyo_station_041 | 2023-09-26 | 日付あり |
closed_date IS NULLは、閉業日がNULLならTRUEになります。
ID1ではこの条件がTRUEなので、THENの「日付なし」が返ります。ID39・41ではNULLではないため、ELSEの「日付あり」が返ります。
「閉業日がNULLの施設では、THENとELSEのどちらの値が返るか」という確認には、「THEN」と回答しました。
表示する文字列の意味を限定する
ここでの「日付なし」「日付あり」は、列の値がNULLかどうかを表しています。営業中か、すでに閉業したかを判定するものではありません。
また、closed_date_statusはSELECTの結果に付けた列名です。テーブルに新しい列を追加する操作ではなく、保存された閉業日も変更していません。
演習②:WHENを増やして3種類に分ける
次に、NULLではない日付を、2023年1月1日より前か、それ以降かに分けました。
SELECT
poi_id,
closed_date,
CASE
WHEN closed_date IS NULL THEN '日付なし'
WHEN closed_date < DATE '2023-01-01' THEN '2023年より前'
ELSE '2023年以降'
END AS closed_date_group
FROM mobility.pois
WHERE poi_id IN (1, 39, 41)
ORDER BY poi_id;
実行結果は次のとおりでした。
| poi_id | closed_date | closed_date_group |
|---|---|---|
| 1 | NULL(画面では空欄) | 日付なし |
| 39 | 2020-02-24 | 2023年より前 |
| 41 | 2023-09-26 | 2023年以降 |
それぞれの行で、次のように判定されます。
- ID1:最初のWHENがTRUEなので、「日付なし」
- ID39:最初のWHENがFALSE、2番目がTRUEなので、「2023年より前」
- ID41:どちらのWHENもFALSEなので、ELSEの「2023年以降」
CASEは、最初にTRUEになったWHENに対応する値を返します。
境界の日付を確認する
確認問題では、閉業日がちょうど2023年1月1日の場合を考え、「2023年以降」と回答しました。
2番目の条件は次の記述です。
closed_date < DATE '2023-01-01'
<は、指定した日付より前であることを判定します。同じ日付は含みません。
そのため2023年1月1日は、このWHENを満たさず、ELSEへ進みます。
この境界日の確認は、仮定した日付への回答です。画像で実際に取得した3施設に、2023年1月1日の行があったという意味ではありません。
NULLを先に分けた理由
今回のCASEでは、最初のWHENでNULLの行を分けています。
そのため、ELSEまで進むのは、日付が入力されていて、かつ2023年1月1日より前ではない行です。「2023年以降」というラベルが、その条件に対応します。
NULLを判定する最初のWHENを省くと、NULLと日付の比較はUNKNOWNとなり、日付のWHENは選ばれません。そのままELSEの「2023年以降」が返ると、日付不明の行を期間分類に混ぜてしまいます。
これは条件の意味を確認する説明であり、省略したSQLを追加実行したわけではありません。
CASEでTHENの値が返るのは、そのWHENがTRUEの場合です。FALSEやUNKNOWNの場合は、次のWHENやELSEへ進みます。
合格条件と学習記録
| 確認項目 | 確認できた内容 |
|---|---|
| WHEN・THEN・ELSEを使う | 日付の有無を3施設に表示 |
| NULLの場合の分岐を説明する | THENの値が返ると回答 |
| 複数のWHENを使う | 日付なし・2023年より前・2023年以降に分類 |
| 境界の日付を判定する | 2023年1月1日は2023年以降と回答 |
まとめ
CASEは上から順に条件を確認し、最初にTRUEになったWHENの値を返します。
今回のようにNULLを先に分けてから日付の範囲を判定すると、日付のない行と期間に属する行を区別できます。
表示用の分類名は、実際の条件に合わせて付けます。今回のSELECTは取得結果を作る操作であり、保存済みのデータを更新するものではありません。