605

PDOを使用して値の配列をプレースホルダーにバインドできるかどうか知りたいです。ここでのユースケースは、IN()条件で使用するために値の配列を渡そうとしています。

私はこのようなことをしたいと思っています:

<?php
$ids=array(1,2,3,7,8,9);
$db = new PDO(...);
$stmt = $db->prepare(
    'SELECT *
     FROM table
     WHERE id IN(:an_array)'
);
$stmt->bindParam('an_array',$ids);
$stmt->execute();
?>

そして、PDOにバインドさせ、配列内のすべての値を引用します。

現在、私は次のことを行っています。

<?php
$ids = array(1,2,3,7,8,9);
$db = new PDO(...);
foreach($ids as &$val)
    $val=$db->quote($val); //iterate through array and quote
$in = implode(',',$ids); //create comma separated list
$stmt = $db->prepare(
    'SELECT *
     FROM table
     WHERE id IN('.$in.')'
);
$stmt->execute();
?>

確かにどちらが仕事をしますが、私が欠けている組み込みのソリューションがあるかどうか疑問に思っていますか?

4

22 に答える 22

283

クエリ文字列を構築する必要があります。

<?php
$ids     = array(1, 2, 3, 7, 8, 9);
$inQuery = implode(',', array_fill(0, count($ids), '?'));

$db = new PDO(...);
$stmt = $db->prepare(
    'SELECT *
     FROM table
     WHERE id IN(' . $inQuery . ')'
);

// bindvalue is 1-indexed, so $k+1
foreach ($ids as $k => $id)
    $stmt->bindValue(($k+1), $id);

$stmt->execute();
?>

chris (コメント) と someoneisintrouble の両方が foreach-loop を提案しました ...

(...)
// bindvalue is 1-indexed, so $k+1
foreach ($ids as $k => $id)
    $stmt->bindValue(($k+1), $id);

$stmt->execute();

... 冗長になる可能性があるため、foreachループとを$stmt->execute... に置き換えることができます。

<?php 
  (...)
  $stmt->execute($ids);
于 2009-05-28T12:02:04.840 に答える
186

簡単なこと:

//$db = new PDO(...);
//$ids = array(...);

$qMarks = str_repeat('?,', count($ids) - 1) . '?';
$sth = $db->prepare("SELECT * FROM myTable WHERE id IN ($qMarks)");
$sth->execute($ids);
于 2012-05-23T15:10:49.870 に答える
40

私は多くの動的クエリを実行するので、これは私が作成した非常に単純なヘルパー関数です。

public static function bindParamArray($prefix, $values, &$bindArray)
{
    $str = "";
    foreach($values as $index => $value){
        $str .= ":".$prefix.$index.",";
        $bindArray[$prefix.$index] = $value;
    }
    return rtrim($str,",");     
}

次のように使用します。

$bindString = helper::bindParamArray("id", $_GET['ids'], $bindArray);
$userConditions .= " AND users.id IN($bindString)";

文字列を返し、クエリを実行するときに必要になるバインディングを:id1,:id2,:id3更新します。$bindArray簡単!

于 2014-03-26T14:10:24.750 に答える
20

postgres の非常にクリーンな方法は、postgres-array ("{}") を使用することです。

$ids = array(1,4,7,9,45);
$param = "{".implode(', ',$ids)."}";
$cmd = $db->prepare("SELECT * FROM table WHERE id = ANY (?)");
$result = $cmd->execute(array($param));
于 2014-04-15T13:07:04.423 に答える
18

EvilRygy の解決策はうまくいきませんでした。Postgres では、別の回避策を実行できます。


$ids = array(1,2,3,7,8,9);
$db = new PDO(...);
$stmt = $db->prepare(
    'SELECT *
     FROM table
     WHERE id = ANY (string_to_array(:an_array, ','))'
);
$stmt->bindParam(':an_array', implode(',', $ids));
$stmt->execute();
于 2010-08-18T07:14:05.313 に答える
16

これが私の解決策です:

$total_items = count($array_of_items);
$question_marks = array_fill(0, $total_items, '?');
$sql = 'SELECT * FROM foo WHERE bar IN (' . implode(',', $question_marks ). ')';

$stmt = $dbh->prepare($sql);
$stmt->execute(array_values($array_of_items));

array_values の使用に注意してください。これにより、キーの順序の問題が修正される場合があります。

ID の配列をマージしてから、重複するアイテムを削除していました。私は次のようなものを持っていました:

$ids = array(0 => 23, 1 => 47, 3 => 17);

そして、それは失敗していました。

于 2015-01-21T12:15:25.550 に答える
12

私は PDO を拡張して stefs の提案と同様のことを行いましたが、長期的にはより簡単になりました。

class Array_Capable_PDO extends PDO {
    /**
     * Both prepare a statement and bind array values to it
     * @param string $statement mysql query with colon-prefixed tokens
     * @param array $arrays associatve array with string tokens as keys and integer-indexed data arrays as values 
     * @param array $driver_options see php documention
     * @return PDOStatement with given array values already bound 
     */
    public function prepare_with_arrays($statement, array $arrays, $driver_options = array()) {

        $replace_strings = array();
        $x = 0;
        foreach($arrays as $token => $data) {
            // just for testing...
            //// tokens should be legit
            //assert('is_string($token)');
            //assert('$token !== ""');
            //// a given token shouldn't appear more than once in the query
            //assert('substr_count($statement, $token) === 1');
            //// there should be an array of values for each token
            //assert('is_array($data)');
            //// empty data arrays aren't okay, they're a SQL syntax error
            //assert('count($data) > 0');

            // replace array tokens with a list of value tokens
            $replace_string_pieces = array();
            foreach($data as $y => $value) {
                //// the data arrays have to be integer-indexed
                //assert('is_int($y)');
                $replace_string_pieces[] = ":{$x}_{$y}";
            }
            $replace_strings[] = '('.implode(', ', $replace_string_pieces).')';
            $x++;
        }
        $statement = str_replace(array_keys($arrays), $replace_strings, $statement);
        $prepared_statement = $this->prepare($statement, $driver_options);

        // bind values to the value tokens
        $x = 0;
        foreach($arrays as $token => $data) {
            foreach($data as $y => $value) {
                $prepared_statement->bindValue(":{$x}_{$y}", $value);
            }
            $x++;
        }

        return $prepared_statement;
    }
}

次のように使用できます。

$db_link = new Array_Capable_PDO($dsn, $username, $password);

$query = '
    SELECT     *
    FROM       test
    WHERE      field1 IN :array1
     OR        field2 IN :array2
     OR        field3 = :value
';

$pdo_query = $db_link->prepare_with_arrays(
    $query,
    array(
        ':array1' => array(1,2,3),
        ':array2' => array(7,8,9)
    )
);

$pdo_query->bindValue(':value', '10');

$pdo_query->execute();
于 2012-02-18T22:00:59.107 に答える
11

他のパラメーターがある場合は、次のようにすることができます。

$ids = array(1,2,3,7,8,9);
$db = new PDO(...);
$query = 'SELECT *
            FROM table
           WHERE X = :x
             AND id IN(';
$comma = '';
for($i=0; $i<count($ids); $i++){
  $query .= $comma.':p'.$i;       // :p0, :p1, ...
  $comma = ',';
}
$query .= ')';

$stmt = $db->prepare($query);
$stmt->bindValue(':x', 123);  // some value
for($i=0; $i<count($ids); $i++){
  $stmt->bindValue(':p'.$i, $ids[$i]);
}
$stmt->execute();
于 2015-03-18T19:16:52.560 に答える
11

PDO :Predefined Constantsを見ると 、 PDOStatement->bindParamにリストされているように必要な PDO::PARAM_ARRAY はありません

bool PDOStatement::bindParam ( mixed $parameter , mixed &$variable [, int $data_type [, int $length [, mixed $driver_options ]]] )

だから、達成できないと思います。

于 2009-05-28T11:48:06.943 に答える
9

また、このスレッドが古いことも認識していますが、間もなく非推奨になる mysql ドライバーを PDO ドライバーに変換するときに、通常のパラメーターと IN の両方を同じものから動的に構築できる関数を作成する必要があるという固有の問題がありました。パラメータ配列。だから私はすぐにこれを構築しました:

/**
 * mysql::pdo_query('SELECT * FROM TBL_WHOOP WHERE type_of_whoop IN :param AND siz_of_whoop = :size', array(':param' => array(1,2,3), ':size' => 3))
 *
 * @param $query
 * @param $params
 */
function pdo_query($query, $params = array()){

    if(!$query)
        trigger_error('Could not query nothing');

    // Lets get our IN fields first
    $in_fields = array();
    foreach($params as $field => $value){
        if(is_array($value)){
            for($i=0,$size=sizeof($value);$i<$size;$i++)
                $in_array[] = $field.$i;

            $query = str_replace($field, "(".implode(',', $in_array).")", $query); // Lets replace the position in the query string with the full version
            $in_fields[$field] = $value; // Lets add this field to an array for use later
            unset($params[$field]); // Lets unset so we don't bind the param later down the line
        }
    }

    $query_obj = $this->pdo_link->prepare($query);
    $query_obj->setFetchMode(PDO::FETCH_ASSOC);

    // Now lets bind normal params.
    foreach($params as $field => $value) $query_obj->bindValue($field, $value);

    // Now lets bind the IN params
    foreach($in_fields as $field => $value){
        for($i=0,$size=sizeof($value);$i<$size;$i++)
            $query_obj->bindValue($field.$i, $value[$i]); // Both the named param index and this index are based off the array index which has not changed...hopefully
    }

    $query_obj->execute();

    if($query_obj->rowCount() <= 0)
        return null;

    return $query_obj;
}

まだテストされていませんが、ロジックはそこにあるようです。

同じ立場の方の参考になれば幸いです

編集:いくつかのテストの後、私は見つけました:

  • PDO は「.」が好きではありません。彼らの名前で(あなたが私に尋ねると、それはちょっとばかげています)
  • bindParam は間違った関数で、bindValue は正しい関数です。

作業バージョンに編集されたコード。

于 2012-07-12T14:44:44.970 に答える
8

Schnalleのコードについて少し編集

<?php
$ids     = array(1, 2, 3, 7, 8, 9);
$inQuery = implode(',', array_fill(0, count($ids)-1, '?'));

$db   = new PDO(...);
$stmt = $db->prepare(
    'SELECT *
     FROM table
     WHERE id IN(' . $inQuery . ')'
);

foreach ($ids as $k => $id)
    $stmt->bindValue(($k+1), $id);

$stmt->execute();
?>

//implode(',', array_fill(0, count($ids)-1), '?')); 
//'?' this should be inside the array_fill
//$stmt->bindValue(($k+1), $in); 
// instead of $in, it should be $id
于 2010-02-18T15:18:29.380 に答える
7

どのデータベースを使用していますか? PostgreSQL では、ANY(配列) を使用するのが好きです。あなたの例を再利用するには:

<?php
$ids=array(1,2,3,7,8,9);
$db = new PDO(...);
$stmt = $db->prepare(
    'SELECT *
     FROM table
     WHERE id = ANY (:an_array)'
);
$stmt->bindParam('an_array',$ids);
$stmt->execute();
?>

残念ながら、これはかなり移植性がありません。

他のデータベースでは、他の人が言及しているように、独自の魔法を構成する必要があります. もちろん、そのロジックをクラス/関数に入れて、プログラム全体で再利用できるようにする必要があります。mysql_queryPHP.NET のページのコメントを見て、このシナリオの主題と例についてさらに考えてみてください。

于 2009-05-28T12:08:51.127 に答える
5

私が知っているように、配列を PDO ステートメントにバインドする可能性はありません。

しかし、2 つの一般的な解決策が存在します。

  1. 位置プレースホルダー (?、?、?、?) または名前付きプレースホルダー (:id1、:id2、:id3) を使用する

    $whereIn = implode(',', array_fill(0, count($ids), '?'));

  2. 配列を先に引用

    $whereIn = array_map(array($db, 'quote'), $ids);

どちらのオプションも適切で安全です。私は 2 番目の方が短く、必要に応じて var_dump パラメーターを使用できるため、2 つ目の方法を好みます。プレースホルダーを使用して値をバインドする必要があり、最終的に SQL コードは同じになります。

$sql = "SELECT * FROM table WHERE id IN ($whereIn)";

そして、私にとって最後で重要なのは、「バインドされた変数の数がトークンの数と一致しません」というエラーを回避することです

Doctrine は、入力パラメーターを内部制御できるという理由だけで、位置プレースホルダーを使用する良い例です。

于 2014-06-20T08:41:42.427 に答える
3

ここに私の解決策があります。PDO クラスも拡張しました。

class Db extends PDO
{

    /**
     * SELECT ... WHERE fieldName IN (:paramName) workaround
     *
     * @param array  $array
     * @param string $prefix
     *
     * @return string
     */
    public function CreateArrayBindParamNames(array $array, $prefix = 'id_')
    {
        $newparams = [];
        foreach ($array as $n => $val)
        {
            $newparams[] = ":".$prefix.$n;
        }
        return implode(", ", $newparams);
    }

    /**
     * Bind every array element to the proper named parameter
     *
     * @param PDOStatement $stmt
     * @param array        $array
     * @param string       $prefix
     */
    public function BindArrayParam(PDOStatement &$stmt, array $array, $prefix = 'id_')
    {
        foreach($array as $n => $val)
        {
            $val = intval($val);
            $stmt -> bindParam(":".$prefix.$n, $val, PDO::PARAM_INT);
        }
    }
}

上記のコードの使用例を次に示します。

$idList = [1, 2, 3, 4];
$stmt = $this -> db -> prepare("
  SELECT
    `Name`
  FROM
    `User`
  WHERE
    (`ID` IN (".$this -> db -> CreateArrayBindParamNames($idList)."))");
$this -> db -> BindArrayParam($stmt, $idList);
$stmt -> execute();
foreach($stmt as $row)
{
    echo $row['Name'];
}

どう考えているか教えてください

于 2013-05-30T13:18:13.100 に答える
2

MySQL と PDO では、JSON 配列とJSON_CONTAINS()( https://dev.mysql.com/doc/refman/8.0/en/json-search-functions.html#function_json-contains ) を使用して検索できます。

$ids = [123, 234, 345, 456]; // Array of users I search
$ids = json_encode($ids); // JSON conversion

$sql = <<<SQL
    SELECT ALL user_id, user_login
    FROM users
    -- Cast is mandatory beaucause JSON_CONTAINS() waits JSON doc candidate
    WHERE JSON_CONTAINS(:ids, CAST(user_id AS JSON))
    SQL;

$search = $pdo->prepare($sql);
$search->execute([':ids' => $ids]);
$users = $search->fetchAll();

より複雑なケースや JSON データの探索にはJSON_TABLE()( https://dev.mysql.com/doc/refman/8.0/en/json-table-functions.html#function_json-table )も使用できます。

$users = [
    ['id' => 123, 'bday' => ..., 'address' => ...],
    ['id' => 234, 'bday' => ..., 'address' => ...],
    ['id' => 345, 'bday' => ..., 'address' => ...],
]; // I'd like to know their login

$users = json_encode($users);

$sql = <<<SQL
    SELECT ALL user_id, user_login
    FROM users
    WHERE user_id IN (
        SELECT ALL user_id
        FROM JSON_TABLE(:users, '$[*]' COLUMNS (
            -- Data exploration...
            -- (if needed I can explore really deeply with NESTED kword)
            user_id INT PATH '$.id',
            -- I could skip these :
            user_bday DATE PATH '$.bday',
            user_address TINYTEXT PATH '$.address'
        )) AS _
    )
    SQL;

$search = $pdo->prepare($sql);
$search->execute([':users' => $users]);
...
于 2021-04-08T22:16:25.977 に答える