かもメモ

自分の落ちた落とし穴に何度も落ちる人のメモ帳

PHP PDO 複数条件(WHERE IN)にハマる

PHPのPDOで複数のidのデータをまとめて取ってくるとかで WHERE IN を使おうとしてハマったのでメモ。

配列のまま渡しても取得できない。

<?php
$ids = [1, 2, 3];

$pdo = new PDO();
$sql = "SELECT * FROM {$table} WHERE id IN (:ids)";

$stmt = $pdo->prepare($sql);
$stmt->bindValue(':ids', $ids, PDO::PARAM_INT);
$stmt->execute();
$res = $stmt->fetchAll();

var_dump($res); // => DBに該当するデータがあっても取得できない
}

配列を文字列化してみる。

WHERE id in (1, 2, 3)

という形式になれば良いはずなので、idの配列をimplode()関数でコンマ区切りに文字列連結してみました。

<?php
$ids = [1, 2, 3];
// implode で '1, 2, 3' という文字列にしてしまう
ids_str = implode(',', $ids);

$pdo = new PDO();
$sql = "SELECT * FROM {$table} WHERE id IN (:ids)";

$stmt = $pdo->prepare($sql);
$stmt->bindValue(':ids', $ids_str, PDO::PARAM_INT); // $ids_str が Stringなのにエラーにならない
$stmt->execute();
$res = $stmt->fetchAll(); // => array(1) id = 1 のデータだけが取得される

なぜか、配列の先頭の値のものだけがSELECTされるようです。
配列の先頭の値に該当するデータが無かったらarray(0) { }が返ってきます。
どうやら配列の先頭の値だけが評価されるみたいです。

note. '(1, 2, 3)' という文字列をプレースホルダにはできない

<?php
$ids = [1, 2, 3];
// '(1, 2, 3)' という文字列にしてしまう
ids_str = '(' . implode(',', $ids) . ')';

$pdo = new PDO();
$sql = "SELECT * FROM {$table} WHERE id IN :ids";

$stmt = $pdo->prepare($sql); // => Fatal error

SQLプレースホルダが (?, ?, ?) になるようにすればOK

WHERE id in (?, ?, ?)

というSQLを作成てし、$stmt->execute()に プレースホルダに該当する値の配列を渡せば、意図した通りにデータを取ってくることができました。

<?php
$ids = [1, 2, 3];

// IN 句に入る値を作成
$inClause = substr(str_repeat(',?', count($ids)), 1); // '?,?,?'

$sql = "SELECT * FROM {$table} WHERE id IN ({$inClause})";
$stmt = $pdo->prepare($sql);
// プレースホルダが ? の時 execute() に配列で渡すことが出来る。
$stmt->execute( $ids );
$res = $stmt->fetchAll(); // => id が 1 or 2 or 3 のデータが取得できる

ただ、execute()に配列を渡す方法は、値が全てPDO::PARAM_STRとして扱われてしまうようなので、注意が必要かもです。

PDOStatement::execute
public bool PDOStatement::execute ([ array $input_parameters ] )
input_parameters

  • 実行される SQL 文の中のバインドパラメータと同数の要素からなる、 値の配列。すべての値は PDO::PARAM_STR として扱われます。
  • ひとつのパラメータに対して複数の値をバインドすることはできません。 例えば、IN() 句の中のひとつのパラメータに対して 2 つの値をバインドすることはできません。
  • 指定した数よりも多い値をバインドすることはできません。 input_parameters のキーが PDO::prepare() で指定した SQL にある数より多い場合は、 ステートメントが失敗してエラーが発生します。

PHP: PDOStatement::execute - Manual

 


[参考]