今更ながら、こちらのページにて3値論理を勉強しました。
式の評価結果がTRUEでもFALSEでもない、という考え方に最初は戸惑いましたが、慣れてみると結構使えそうな感触。
イントロ
SQLでは、あるカラムがNULLであるか否かを調べるのに
xxx = NULL
ではなく
xxx IS NULL
と書きます。これまで僕は、この式の意味を深く考えずに
「NULLの場合は、"="は使えない」と機械的に考えるだけでした。
ところが、3値演算のことが理解できるようになると、"IS NULL"の本当の意味が良く分かるようになりました。
要するに3値論理って何?
3値論理を理解するキモは、「わからない(NULL/UNKNOWN)」の意味を難しく考えないことです。
以下のように、普通の言葉で置き換えるとわかりやすいでしょう。
- TRUE: 正しい
- NULL(UNKNOWN): わからない
- FALSE: 正しくない
そして、「意味の重さ」は次の通りです。
正しくない >> わからない >> 正しい
言葉の重さとは、要するに
- 他の要素が何であれ、一つでも「正しくない」モノがあれば、全体としては「正しくない」。
- 他の要素が全て「正しい」モノであっても、一つでも「わからない」モノが混入してしまえば、全体としては「わからない」。
- 絶対に「正しい」と言えるのは、全ての要素が「正しい」時だけ。
ということです。
すなわち、
「正しいもの」&「わからないもの」=「わからないもの」 つまり (TRUE AND NULL) IS NULL
が真ですし、
「わからないもの」&「正しくないもの」=「正しくないもの」 つまり (NULL AND FALSE) IS FALSE
も真です。
3値論理の応用方法
最初に紹介したページにある通りですが、SQLを固定的に書くことができます。
例えばエンティティの検索画面ではユーザが任意に検索条件を入力しますが、
検索条件値はあったりなかったりするわけです。
だからSQLを動的に生成せざるを得ず、次のようなコードを書かざるを得ないことになります。
/** * 入力された検索条件に応じてSQLを組み立てて実行する */ ResultSet select(Map<String, String> conditionValues) { // SQLを組みたてる String sql = "SELECT * FROM ABC WHERE "; List<String> whereSql = new ArrayList<String>(); if (conditionValues.get("column1") != null) { whereSql.add("column1 = ?"); } if (conditionValues.get("column2") != null) { whereSql.add("column2 = ?"); } if (conditionValues.get("column3") != null) { whereSql.add("column3 = ?"); } sql += StringUtils.join(whereSql, " and "); // パラメータをセットする PreparedStatement ps = conn.prepareStatement(sql); int position = 1; if (conditionValues.get("column1") != null) { ps.setString(position, conditionValues.get("column1")); position++; } if (conditionValues.get("column2") != null) { ps.setString(position, conditionValues.get("column2")); position++; } if (conditionValues.get("column3") != null) { ps.setString(position, conditionValues.get("column3")); position++; } return ps.executeQuery(); }
ここで3値論理を導入すると、WHERE句の各条件の評価結果がTRUEまたはNULLであればよいので、
式全体が少なくとも偽でない、すなわち"IS NOT FALSE"であれば十分ということになります。
/** * 入力された検索条件に応じてSQLを実行する */ ResultSet select(Map<String, String> conditionValues) { String sql = "SELECT * FROM ABC WHERE " + "(" + "column1 = ? and" + "column2 = ? and" + "column3 = ?" + ") IS NOT FALSE"; // パラメータをセットする PreparedStatement ps = conn.prepareStatement(sql); ps.setString(1, conditionValues.get("column1")); ps.setString(2, conditionValues.get("column2")); ps.setString(3, conditionValues.get("column3")); return ps.executeQuery(); }
と書くだけで済むのです。かなりスッキリしていますね。