挿入・更新する値が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 ; |
プレースホルダで「= ?」と「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 ); |