4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

[PHP]SQLのIN節に配列をセットする

Last updated at Posted at 2020-12-10

PHPでSQLのIN節で配列を指定したい時に詰まった事メモ。

NG : IN節に配列でパラメータを渡す

$id = 1;
$num = [1,2,3,4,5,...];

$param = array(
    "id" => $id,
    "num" => $num,
);

$pdo = new PDO();
$sql = "SELECT * FROM hoge_table WHERE id=:id AND num IN (:num)"; // :numでエラーが起きる
$stmt = $pdo->prepare($sql);
$stmt->execute($param);

これだとこういうエラーが出る。

Notice: Array to string conversion in 

プレースホルダーに配列は使えないらしい。

OK : IN節にパラメータを一個一個指定する

一旦プレースホルダを一個一個指定して確認してみる。問題はなさそう。

$param = array(
    "id" => $id,
    "num1" => $num1,
    "num2" => $num2,
    "num3" => $num3,
);

$pdo = new PDO();
$sql = "SELECT * FROM hoge_table WHERE id=:id AND num IN (:num1, :num2, :num3);"; //一個一個指定する
$stmt = $pdo->prepare($sql);
$stmt->execute($param);

でもこれだと配列の個数が可変の時に困る。

なので修正。

NG : IN節にカンマ区切りの文字列のパラメータとして渡す

$param = array(
    "id" => $id,
    "num" => implode(",",$num), // numをパラメータとして渡す
);

$pdo = new PDO();
$sql = "SELECT * FROM hoge_table WHERE id=:id AND num IN (:num)";
$stmt = $pdo->prepare($sql);
$stmt->execute($params);

これだとなぜかINに当てはまる最初の行だけ取得する。

(天の声:(:num)のカンマ区切りが数値型にパースされちゃうとか、そうゆう感じじゃないかな・・・)
(ありえそう・・・)

なので修正。

NG : カンマ区切りの文字列としてSQL文に入れ込む

$param = array(
    "id" => $id,
);

$pdo = new PDO();
$sql = "SELECT * FROM hoge_table WHERE id=:id AND num IN (".implode(",",$num).");"; //numをSQL文に入れ込んでしまう
$stmt = $pdo->prepare($sql);
$stmt->execute($param);

結果的にはうまくいく。
だけどパラメータをSQLに直接入れてるので、ユーザが入力できる値だったりするとSQLインジェクションの恐れがある。

OK : 疑問符のプレースホルダ使ってがんばる

以下のように解決できるみたい。詳しくは記事先へ。
PDOのwhereでIN句を使う - Qiita

リファレンスを確認

PDOじゃないけどmysqliに気になる記述があった。
PHP: mysqli_stmt::prepare - Manual

Turns out you can't directly use a prepared statement for a query that has a placeholder in an IN() clause.
There are ways around that (such as constructing a string that consists of n question marks separated by commas, then using that set of placeholders in the IN() clause), but you can't just say IN (?).
This is a MySQL restriction rather than a PHP restriction, but it's not really documented in the MySQL docs either, so I figured it was worth mentioning here.

「IN()節の中にプレースホルダを持つクエリに対して、準備された文を直接使用することはできないことがわかりました。
これを回避する方法はありますが (例えば、カンマで区切られた n 個のクエスチョンマークからなる文字列を作成し、そのプレースホルダのセットを IN() 節で使用するなど)、単に IN (?) と言うことはできません。
これはPHPの制限というよりはMySQLの制限ですが、MySQLのドキュメントにも書かれていないので、ここで言及する価値があると思いました。」

SQLの仕様らしい。

おしまい。

4
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?