PreparedStatementでNULLを扱う
挿入・更新
挿入・更新する値がNULLであり得る場合。(決まっているわけではなく、普通の値が入ることもある)
php - Execute PDO with an array containing null values - Stack Overflow
以下のようなSQLを、PDOのプリペアドステートメントにセットしたとして、
1 2 3 |
UPDATE table SET name=:name, id_extra1=:ex1, id_extra2=:ex2 WHERE id=1; |
たとえばid_extra2に入れる値が、NULLであり得るとする。
1 2 3 4 5 6 7 8 9 10 11 |
// $pdo = 接続済みのPDOインスタンス$stmt = $pdo->prepare( 'UPDATE table SET name=:name, id_extra1=:ex1, id_extra2=:ex2 WHERE id=1;');$stmt->execute([ ':name' => 'hoge', ':ex1' => 1, ':ex2' => null, // ここ]); |
上記のコードでは、id_extra2カラムに入るのはNULLにはならない。id_extra2の型がINTなら'0'、DATEなら'0000-00-00'になってしまう。
これは、execute()の引数に[placeholder ⇒ value]の配列を与えた場合、その型は全てPDO::PARAM_STR(文字列型)として処理されてしまうため。なので、そこにPHPとしてのnullが与えられても、SQL上では空文字列が指定されたことになってしまう。
1 2 3 4 |
-- 上記の$stmtの実行時の状態(たぶんこんな感じ)UPDATE table SET name='hoge', id_extra1='1', id_extra2='' WHERE id=1; |
SQLとしてのNULLを代入するには、placeholderに値をバインドする際、型にPDO::PARAM_NULLを指定しなければならない。
しかし、execute()の引数に渡す方法では、型は指定する方法が無い。
bindValue()なら、第3引数に型を指定することができるので、それを使う。
1 2 3 4 5 6 |
// $stmt は上記のものと一緒$stmt->bindValue(':name', 'hoge');// INTに関しては、型を省略してもSQLが解釈してくれるため、不要っちゃ不要。明示するという意味はある$stmt->bindValue(':ex1', 1, PDO::PARAM_INT); $stmt->bindValue(':ex2', null, PDO::PARAM_NULL); // ←ここで指定$stmt->execute(); |
もし、execute()と似た感じで実行時に配列一発で与えたければ、擬似的な関数を使う方法がある。(ただしこれはこれで、$valueには意図しない配列が入ることは無い、などの前提が必要になるけど)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
function executeWithDataTypes(PDOStatement $stmt, array $values){ foreach ($values as $key => $value) { if (is_array($value)) { $stmt->bindValue($key, $value['value'], $value['type']); } else { $stmt->bindValue($key, $value); } } return $stmt->execute();}// $stmt は上記のものと一緒executeWithDataTypes($stmt, [ ':name' => 'hoge', ':ex1' => 1, ':ex2' => ['value' => null, 'type' => PDO::PARAM_NULL],]); |
参照
WHERE節の中で、あるカラムの値が、NULLのものを抽出する時もある場合。たとえば以下のSQLで、:idがNULLである時もあるような場合。
1 2 3 4 5 6 7 |
SELECT * FROM table WHERE id=:id;-- 以下の構文は、SQL構文エラーになる。SELECT * FROM table WHERE id=NULL;-- NULLの場合はISで比較しなければならない。SELECT * FROM table WHERE id IS NULL; |
MySQLの場合
プレースホルダで「= ?」と「IS NULL」に同時に対応する方法 - Qiita
MySQLでは、右辺が、実態のある値でも、NULLでも、同じように比較できる演算子が用意されている。楽ちん。
1 |
SELECT * FROM table WHERE id<=>:id; |
ただし、':id' placeholderへの関連づけは、挿入・更新時と同様、型を指定する必要があるため、その点は上記参照。
そういう演算子が無い場合
PHPで、SQLを生成するときに場合分けするという愚直な方法が、ちょっと条件分岐は増えるけど、一番わかりやすそう。
1 2 3 4 5 6 7 8 9 10 11 |
$sql = 'SELECT * FROM table ';$query_params = [];if ($id === null) { $sql .= 'WHERE id IS NULL;';} else { $sql .= 'WHERE id = :id'; $query_params[':id'] = $id;}$stmt = $pdo->prepare($sql);$stmt->execute($query_params); |

