【SQL】NOT IN 句と NULL の注意点

The moment you doubt whether you can fly, you cease for ever to be able to do it.

(拙訳: 飛べるか否かを疑った瞬間に、永遠に飛べなくなってしまう)

ジェームス・マシュー・バリー「ピーター・パンとウェンディ」より

こんにちは!休みという概念がないくらい忙しい hikaru-kimi です!

弊社のような自社サービスを運営する会社で避けられないのがエラーや不具合の調査です。

調査の際は、ソースコードはもちろんのことデータベースの調査も不可欠である場合が往々にしてあります。

SQL を書く際、NOT IN 句を使って特定の値を除外したいケースはよくあります。しかし、除外対象に NULL が含まれると意図しない結果を引き起こす可能性があります。 今回は、SQL の NOT IN 句で NULL を扱う際の注意点について解説していきたいと思います。

NOT IN 句と NULL の注意点

以下のようなテーブルとデータを基に解説していきたいと思います。

CREATE TABLE books (id INTEGER, title TEXT);

INSERT INTO
  books (id, title)
VALUES
  (1, 'The Great Gatsby'),
  (2, 'For Whom the Bell Tolls'),
  (3, '1984'),
  (4, 'Pride and Prejudice'),
  (NULL, 'The Catcher in the Rye');

上記テーブルに対して「id が 2 または NULL の行を除外したい」として、以下のようなクエリを実行したとします。

SELECT
  *
FROM
  books
WHERE
  id NOT IN (2, NULL);

一見、2NULL の行だけが除外されそうに見えますが、実際のクエリ結果は空になります。

これは、SQL の三値論理の性質が原因です。

SQL の NOT IN 句は、以下のように各行の id をリスト内のすべての値と比較して、すべてに対して <>(not equal)が成立するかを判断します。

そして、対象に NULL が含まれていると次のような比較が行われます。

id <> 2
AND id <> NULL

このうち、id <> NULL は SQL 的には UNKNOWN という結果になります。SQL では条件式の一部に UNKNOWN があると全体として FALSE と見なされ、結果としてどの行も返されなくなってしまうのです。

これを回避するために、NULL を明示的に除外する条件を加える必要があります。以下のように書き換えると期待通りの結果が得られます。

SELECT
  *
FROM
  books
WHERE
  id NOT IN (2)
  AND id IS NOT NULL;
 id |        title
----+---------------------
  1 | The Great Gatsby
  3 | 1984
  4 | Pride and Prejudice
(3 rows)

このような SQL の性質も、知識として兼ね備えていたとしてもいざ本番でデータベース調査を行う際はつい失念しがちです。数々の失敗を重ね糧とすることで、知識が知恵として昇華するのではないでしょうか。

青春の蹉跌

7月に入り、近畿地方は梅雨明けを迎え、名実ともに夏がやってきました。

「夏」と聞いて、皆さんは何を連想しますか?

高く位置する太陽が燦々と降り注ぐ開放的な季節。日焼けが気になる季節。或いは、夜通しエアコンをつけなければ寝苦しい季節。

個人的な話で恐縮ですが、私はこの季節になると決まって、大阪から遠く離れた東の街で過ごした学生時代を想起します。

夏特有の気だるさが、或いは夢想とアンニュイに満ちた学生時分の夏休みを思い返させるからなのでしょうか。

学生時代を思い返すと、多くの学びと様々な思い出、そして数々の蹉跌が惹起させられます。

過去を思い返すと懐かしく感じるのは、今が寂しいからなのでしょうか。

といいつつも、全てが懐かしく感じるわけではありません。

一所懸命に取り組んでも満足のいく結果が出ないこともありました。また、いい加減に取り組んだものが思わぬ僥倖をもたらす場合もしばしばありました。周囲が羨むものでも当の本人にとり価値を感じ得ないものだってあります。一般人から見たら取るに足らないものでも、当人にとり何事にも代え難いことだってあるのです。

このように、人生というのは随分と皮肉にできているのです。

人生が思いどおりにいかなかったからと言って、後ろばかり向き、自分を責めてみても、それは詮無いことです。

あのときああすれば人生の方向が変わっていたかもしれない――そう思うことはありましょう。

しかし、それをいつまでも思い悩んでいても意味のないことです。

カズオ・イシグロ「日の名残り」より

青春とは、己の崇高な理想と冷徹な現実との差異に苛み、それでも尚それを埋めるべく懸命に絶え間ない努力を続け、そして人知れず蹉跌に打ちひしがれ続ける季節ではないでしょうか。

そしてその苦しみから解放された時、人生における青春は終わりを告げるのです。

それでは果たして、青春の後の人生を待ち受けるのは、過去を見つめ、過去に後ろ髪をひかれながら、それでも見えない力に押し戻されるかの如く慣性の法則に従い人生という川を進んでいく侘しい姿なのでしょうか。

私はそうは思いません。夏が終われば新たな季節が到来するように、青春の終焉は、新たなる使命の始まりを意味するのです。

畢竟、青春が持てる情熱を自己のために燃やす季節であるならば、その後の人生は、他者のために自らを捧げる季節と呼べるのではないでしょうか。

自分の人生を他者のために費やす。そうすることで自分が享受した青春を次世代にも経験させる。これが、高度な知能と複雑な文明を築いてきた人類が古来から限りなく繰り返してきた大自然の摂理なのであり、人類のレゾンデートルであるのだと思います。

徒に馬齢を重ね、孔子が言うところの而立の歳を迎え、つい先日所帯を持つに至りました。 こうして上記拙文にて述べた境地の一端を垣間見ることができたわけでして、私自身、万感、胸に迫るものがあります。

弊社インゲージでは、利他心を持ったエンジニアを募集しております。

興味のある方はぜひ、以下のリンクよりご応募をよろしくお願いします。