今回はPostgreSQLの三値論理を学びます。NULLを含む条件がどのように判定されるかを確認し、取得したデータの意味を整理します。
所要時間は15分ほどです。
それでは、さっそく始めていきましょう!
用語と今回扱う対象
三値論理は、条件の判定をTRUE・FALSE・UNKNOWNの3種類で扱う仕組みです。
| 用語 | 意味 |
|---|---|
| TRUE(真) | 条件が成り立つ |
| FALSE(偽) | 条件が成り立たない |
| UNKNOWN(不明) | 条件が成り立つか判定できない |
今回扱う対象はSQLの条件式と、その判定結果です。PostgreSQLではUNKNOWNを真偽値型のNULLで表します。NULLは空文字列やゼロとは異なります。
演習で確認するのは、SELECTで表示される判定結果と、WHEREで取得される行です。統計的な推定は行いません。
WHEREで残るのはTRUEの行だけ
WHEREは、条件がTRUEになった行を取得します。FALSEの行だけでなく、UNKNOWNの行も取得しません。
| WHEREの条件の判定結果 | 取得対象になるか |
|---|---|
| TRUE | なる |
| FALSE | ならない |
| UNKNOWN | ならない |
FALSEとUNKNOWNは異なる判定結果ですが、WHEREで取得されないという点は共通しています。
演習①:3種類の判定を表示する
次のSQLを実行しました。
SELECT
1 = 1 AS true_result,
1 = 2 AS false_result,
NULL = 1 AS unknown_result;
実行画面には、順にt、f、空欄が表示されました。
| 列名 | 画面の表示 | 判定結果 |
|---|---|---|
| true_result | t | TRUE |
| false_result | f | FALSE |
| unknown_result | 空欄 | UNKNOWNを表すNULL |
1 = 1は成り立ち、1 = 2は成り立ちません。一方、NULL = 1では比較する値が不明なため、等しいとも等しくないとも判定できません。
「WHEREの条件がUNKNOWNになった行は含まれるか」という質問には、「含まれません」と回答しました。
NULLの表示を見分ける
psqlでは、次の設定でNULLの表示を変更できます。
\pset null '[NULL]'
これは表示設定であり、保存された値を変更しません。今回送った実行画像ではNULLは空欄で表示されていました。設定コマンドの実行結果は画像で確認していないため、設定済みとは扱いません。
演習②:UNKNOWNをNOTで否定する
次に、それぞれの条件にNOTを付けました。
SELECT
NOT (1 = 1) AS not_true,
NOT (1 = 2) AS not_false,
NOT (NULL = 1) AS not_unknown;
実行結果は、順にf、t、空欄でした。
| 否定前 | NOTを付けた判定結果 |
|---|---|
| TRUE | FALSE |
| FALSE | TRUE |
| UNKNOWN | UNKNOWN |
TRUEとFALSEは反転しますが、UNKNOWNはUNKNOWNのままです。値が不明である以上、等しいかどうかを否定しても判定は確定しません。
「NOT (NULL = 1)はどれになるか」という確認には、UNKNOWNの意味で回答しました。入力は「NNKNOWN」でしたが、文脈からUNKNOWNの回答として確認しました。
演習③:施設データで確認する
取得元はmobility.poisです。1行が1施設を表し、poi_idは施設ID、poi_nameは施設名、closed_dateは閉業日を表す日付の列です。
次のSQLを実行しました。
SELECT poi_id, poi_name, closed_date
FROM mobility.pois
WHERE NOT (closed_date = DATE '2023-09-26')
ORDER BY poi_id;
実行結果は次の3行でした。
| poi_id | poi_name | closed_date |
|---|---|---|
| 39 | Omiya_hotel_039 | 2020-02-24 |
| 45 | Yokohama_office_045 | 2028-05-02 |
| 46 | Kawasaki_park_046 | 2022-04-28 |
日付が入力されており、その日付が2023年9月26日と等しくない行が取得されています。この条件は過去の日付だけを指定するものではないため、2028年の日付も含まれます。
NULLの施設が含まれない理由
条件の判定を、日付の値ごとに分けて確認します。
| closed_dateの値 | 指定日と等しいか | NOTを付けた結果 | WHEREで取得 |
|---|---|---|---|
| 2023-09-26 | TRUE | FALSE | されない |
| 2020-02-24など、指定日以外の日付 | FALSE | TRUE | される |
| NULL | UNKNOWN | UNKNOWN | されない |
前の演習で確認した施設ID41の閉業日は2023年9月26日なので、今回は含まれません。また、NULLの施設も含まれません。除外される理由は同じではなく、前者はFALSE、後者はUNKNOWNになるためです。
最後の確認では、「NULLと日付の比較はUNKNOWNになり、NOTを付けてもUNKNOWNのままで、WHEREでは取得されないから」という選択肢を選びました。
NOTは条件を否定するものであり、NULLを日付に書き換える操作ではありません。NULLの行そのものを探す場合は、前回学んだIS NULLを使います。
入力途中のSQLを取り消す
演習中に、入力をいったんクリアする方法も確認しました。
入力途中のSQLを取り消すにはControl + Cを使います。通常の入力状態では、次のpsqlコマンドでも入力バッファをリセットできます。
\r
引用符を閉じ忘れた場合はControl + Cを使います。これらは入力途中のSQLを取り消す操作で、保存済みのデータを削除するものではありません。
合格条件と学習記録
| 確認項目 | 確認できた内容 |
|---|---|
| 3種類の判定結果を確認する | TRUE・FALSE・UNKNOWNのSELECTを実行 |
| WHEREの取得条件を理解する | UNKNOWNの行は含まれないと回答 |
| NOTとUNKNOWNの関係を理解する | 否定後もUNKNOWNと回答 |
| 施設データで説明する | 3行の取得結果を確認し、NULLが除外される理由を選択 |
まとめ
WHEREで取得されるのは、条件がTRUEになった行だけです。NULLを含む通常の等値比較はUNKNOWNとなり、NOTを付けてもUNKNOWNのままです。
条件を否定すればNULLの行も取得できる、と考えないことが今回の要点です。NULLかどうかを調べる条件と、日付の値を比較する条件を区別して使います。