SQLアタマアカデミー

第8回 SQLにおける論理演算~なぜ真理を隠すのか~ (3)HAVING句で論理演算を行おう

この記事を読むのに必要な時間:およそ 3 分

③クライアントC からの問い合わせ

続いて,次のような条件であるとします。

  • 条件:「在庫管理」「製造」の両方のスキルを持っている,または「会計」のスキルを持っている人材

今度は「または」が条件に入ってきました。論理式で書けば次のようになります。

  • (⁠在庫管理」AND「製造」⁠OR「会計」

これも考え方はのときと同じです。ORは,ビット演算では足し算で代用しますリスト6)⁠結果は図9のようになります。

リスト6 ⁠在庫管理」「製造」の両方のスキルを持っている,または「会計」のスキルを持っている人材を抽出するクエリ


SELECT candidate_id
  FROM CandidateSkills
 GROUP BY candidate_id
HAVING ( MAX(CASE WHEN skill_code = '在庫管理' THEN 1 ELSE 0 END)
       * MAX(CASE WHEN skill_code = '製造' THEN 1 ELSE 0 END))
       + MAX(CASE WHEN skill_code = '会計' THEN 1 ELSE 0 END ) >= 1; (=でないのが重要!)

図9 リスト6の出力結果

candidate_id
------------
100
200
400
500

先ほどと同様,各スキルについて,ビットフラグの一覧表を作ってみましょう図10)⁠

図10 スキルの保有有無によるビットフラグ

candidate_id
(就業希望者ID)
在庫管理フラグ製造フラグ会計フラグ(在庫管理フラグ*製造フラグ)+会計フラグ
1001112
2001011
3000100
4001101
5000111

今回は条件に「OR」が入っているため,ビット演算に足し算が含まれます。すると,100番の人のように,結果が0/1の範囲を超えて,2以上になる場合があります。これはある意味当然の話で,いかにこの演算が真理値の論理演算を代用するものとはいえ,それは私たちユーザが勝手にそう思っているだけで,DBにとってはただの整数の四則演算だからです。そのため,この演算の結果は,⁠0か,それ以上か」という形で真偽判定をしなくてはなりません。

整数で真理値を代用した場合の結果判定のルール
  • 真:1以上
  • 偽:0

そのため,結果判定の際に等号ではなく不等号を使わざるをえないのが気持ち悪いところです(リスト6参照)⁠

このように,HAVING句で論理演算の考え方を利用すると,非常に便利で幅広い応用が可能になります。そのため,DBを扱う人には積極的に身につけてもらいたい技術なのですが,その障害になるのが,SQLとRDBへの真理値型のサポートの遅れなのです。

終わりに

今回は,いささか厄介な,しかしSQLとデータベースを扱う上で避けては通れないテーマを取り上げました。頭がこんがらがった方もいるかもしれませんので,最後にまとめましょう。

  • 標準SQLには真理値型も定義されているが,サポートしているDBは少ない
  • その理由は,SQLの真理値がunknownという第3の値まで持つ独特の体系(3値論理)であるため
  • NULLとunknownは厳密には異なるのだが,PostgreSQLはこれを混同している
  • NULLと空文字は厳密には異なるのだが,Oracleはこれを混同している
  • しかたないので,現在のところ,SQLで論理演算をしたいときは,0/1のビットフラグでビット演算をするしかない

最後に,参考資料を記します。

参考資料
ミック『達人に学ぶSQL徹底指南書』
(翔泳社,2008)

「1-3 3値論理とNULL」では,SQLにおける3値論理演算がどのように行われるか,そしてそれがいかにSQLに混乱をもたらすかという点を詳細に解説しました。本稿と併せて読むとよいでしょう。

また,なぜSQLがそもそも真理値に3つ目の値を持ち込むことになったのか,という歴史的な事情を知りたい人は「2-8 神のいない論理」を参照してください。

ミック3値論理とNULL

そもそもなぜSQLにunknownという第3の真理値が存在するのか,という理由を知りたい方は,こちらの記事を参照。NULLがSQLの演算に含まれた場合の危険性についても解説しています。

明智重蔵OracleSQL パズル 9-22

Joe Celkoの『SQLパズル 第2版』⁠翔泳社)「パズル17人材紹介会社」に対して,HAVING句で真理値演算を行う解答が解説されています。本稿を書くうえで参考にさせていただきました。

COLUMN NULLとゼロの違い

先のコラムではNULLと空文字の違いについて取り上げましたが,ここではNULLとゼロの違いについて,少し注意すべき点をお話しておきましょう。

SQLのNULLには,伝播(でんば)propagationという妙な性質があります。これは演算の中にNULLが含まれていた場合,その結果が自動的にNULLになるというものです。例を示します。

  • 3 - NULL = NULL
    1 + (2 * NULL) = NULL
    4 / NULL = NULL

これだけでも十分に気味が悪い話ですが,もっと凄いのはゼロ除算の場合です。普通,SQL内の演算でゼロ除算が発生するとエラーになります。ところが,NULLをゼロで割ってもエラーにならないのです。

  • 5 / 0 -> Erorr!
    NULL / 0 = NULL -> Errorにならず!

このように,普通の値に対して適用できる演算ルールの一切を破壊してしまうという点でも,NULLが値とはまったく異なるものだということがわかります。NULLが演算に含まれた場合,その演算そのものが不成立という扱いを受けるのです。

ちなみに,NULLが演算を破壊するのは数値の四則演算の場合だけではありません。文字列の結合演算でもやはり同様です。

文字列に空文字を結合しても結果が変化しない
  • ⁠ABC’ || ‘’ = ‘ABC
文字列にNULLを結合すると結果がNULLになる
  • ⁠ABC’ || ‘’ = NULL

空文字は,文字列におけるゼロのような役割を果たすので,これを任意の文字列に結合しても変化しません。ところがNULLを結合すると,結果がNULLになってしまうのです。

NULLは,すべてを呑み込むブラックホールなのです。

著者プロフィール

ミック

SI企業に勤務するDBエンジニア。主にデータウェアハウス業務に従事している。自身のサイト「リレーショナル・データベースの世界」でデータベースとSQLについての技術情報を公開している。『Web+DB Press』で「SQLアタマアカデミー」を連載中。

著書:『達人に学ぶ SQL徹底指南書』(翔泳社、2008)訳書:J.セルコ『SQLパズル 第2版』(翔泳社、2007)

SQLアタマアカデミー:サポートページ