我可以将数组绑定到in()条件吗?

piwo6bdm  于 2021-06-23  发布在  Mysql
关注(0)|答案(21)|浏览(646)

我很想知道是否可以使用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();
?>

这当然可以,但只是想知道是否有一个内置的解决方案,我错过了?

pes8fvy9

pes8fvy91#

因为我做了很多动态查询,所以这是我做的一个超级简单的助手函数。

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 运行查询时所需的绑定。容易的!

a11xaf1n

a11xaf1n2#

首先在查询中设置“?”的数量,然后通过“for”发送如下参数:

require 'dbConnect.php';
$db=new dbConnect();
$array=[];
array_push($array,'value1');
array_push($array,'value2');
$query="SELECT * FROM sites WHERE kind IN (";

foreach ($array as $field){
    $query.="?,";
}
$query=substr($query,0,strlen($query)-1);
$query.=")";
$tbl=$db->connection->prepare($query);
for($i=1;$i<=count($array);$i++)
    $tbl->bindParam($i,$array[$i-1],PDO::PARAM_STR);
$tbl->execute();
$row=$tbl->fetchAll(PDO::FETCH_OBJ);
var_dump($row);
sg24os4d

sg24os4d3#

在经历了同样的问题之后,我找到了一个更简单的解决方案(尽管仍然不如一个简单的解决方案优雅) PDO::PARAM_ARRAY 可能是):
给定数组 $ids = array(2, 4, 32) :

$newparams = array();
foreach ($ids as $n => $val){ $newparams[] = ":id_$n"; }

try {
    $stmt = $conn->prepare("DELETE FROM $table WHERE ($table.id IN (" . implode(", ",$newparams). "))");
    foreach ($ids as $n => $val){
        $stmt->bindParam(":id_$n", intval($val), PDO::PARAM_INT);
    }
    $stmt->execute();

... 等等
因此,如果使用的是混合值数组,则在指定类型param之前,需要更多的代码来测试值:

// inside second foreach..

$valuevar = (is_float($val) ? floatval($val) : is_int($val) ? intval($val) :  is_string($val) ? strval($val) : $val );
$stmt->bindParam(":id_$n", $valuevar, (is_int($val) ? PDO::PARAM_INT :  is_string($val) ? PDO::PARAM_STR : NULL ));

但我没有测试过这个。

v09wglhw

v09wglhw4#

为了让答案更接近使用占位符绑定参数的原始问题,我做了进一步的工作。
这个答案必须在查询中使用的数组中进行两个循环。但它确实解决了在更具选择性的查询中使用其他列占位符的问题。

//builds placeholders to insert in IN()
foreach($array as $key=>$value) {
    $in_query = $in_query . ' :val_' . $key . ', ';
}

//gets rid of trailing comma and space
$in_query = substr($in_query, 0, -2);

$stmt = $db->prepare(
    "SELECT *
     WHERE id IN($in_query)";

//pind params for your placeholders.
foreach ($array as $key=>$value) {
    $stmt->bindParam(":val_" . $key, $array[$key])
}

$stmt->execute();
hfsqlsce

hfsqlsce5#

在pdo中不可能使用这样的数组。
您需要为每个值构建一个带有参数(或使用?)的字符串,例如: :an_array_0, :an_array_1, :an_array_2, :an_array_3, :an_array_4, :an_array_5 举个例子:

<?php
$ids = array(1,2,3,7,8,9);
$sqlAnArray = join(
    ', ',
    array_map(
        function($index) {
            return ":an_array_$index";
        },
        array_keys($ids)
    )
);
$db = new PDO(
    'mysql:dbname=mydb;host=localhost',
    'user',
    'passwd'
);
$stmt = $db->prepare(
    'SELECT *
     FROM table
     WHERE id IN('.$sqlAnArray.')'
);
foreach ($ids as $index => $id) {
    $stmt->bindValue("an_array_$index", $id);
}

如果你想继续使用 bindParam ,您可以这样做:

foreach ($ids as $index => $id) {
    $stmt->bindParam("an_array_$index", $ids[$id]);
}

如果你想用 ? 占位符,您可以这样做:

<?php
$ids = array(1,2,3,7,8,9);
$sqlAnArray = '?' . str_repeat(', ?', count($ids)-1);
$db = new PDO(
    'mysql:dbname=dbname;host=localhost',
    'user',
    'passwd'
);
$stmt = $db->prepare(
    'SELECT *
     FROM phone_number_lookup
     WHERE country_code IN('.$sqlAnArray.')'
);
$stmt->execute($ids);

如果你不知道 $ids 如果为空,则应该测试它并相应地处理该情况(返回空数组,或返回空对象,或引发异常,…)。

xcitsw88

xcitsw886#

当您有其他参数时,您可以这样做:

$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();
nhhxz33t

nhhxz33t7#

对我来说,更性感的解决方案是构造一个动态关联数组并使用它

// A dirty array sent by user
$dirtyArray = ['Cecile', 'Gilles', 'Andre', 'Claude'];

// we construct an associative array like this
// [ ':name_0' => 'Cecile', ... , ':name_3' => 'Claude' ]
$params = array_combine(
    array_map(
        // construct param name according to array index
        function ($v) {return ":name_{$v}";},
        // get values of users
        array_keys($dirtyArray)
    ),
    $dirtyArray
);

// construct the query like `.. WHERE name IN ( :name_1, .. , :name_3 )`
$query = "SELECT * FROM user WHERE name IN( " . implode(",", array_keys($params)) . " )";
// here we go
$stmt  = $db->prepare($query);
$stmt->execute($params);
i86rm4rw

i86rm4rw8#

使用它有那么重要吗 IN 声明?尝试使用 FIND_IN_SET 作品。
例如,在pdo中有这样一个查询

SELECT * FROM table WHERE FIND_IN_SET(id, :array)

然后您只需要绑定一个值数组,用逗号内爆,就像这个

$ids_string = implode(',', $array_of_smth); // WITHOUT WHITESPACES BEFORE AND AFTER THE COMMA
$stmt->bindParam('array', $ids_string);

一切都结束了。
upd:正如一些人在对这个答案的评论中指出的那样,有一些问题需要明确说明。 FIND_IN_SET 没有在表中使用索引,它还没有实现-请参阅mysql bug tracker中的记录。感谢@billkarwin的通知。
不能使用内部带有逗号的字符串作为搜索数组的值。用正确的方法解析这样的字符串是不可能的 implode 因为您使用逗号符号作为分隔符。感谢@val的留言。
总之,如果您不严重依赖索引,也不使用带逗号的字符串进行搜索,那么我的解决方案将比上面列出的解决方案更简单、更快速。

xxls0lw8

xxls0lw89#

快来点东西:

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

$qMarks = str_repeat('?,', count($ids) - 1) . '?';
$sth = $db->prepare("SELECT * FROM myTable WHERE id IN ($qMarks)");
$sth->execute($ids);
lxkprmvk

lxkprmvk10#

如果列只能包含整数,则可以不使用占位符,直接将id放入查询中。您只需将数组的所有值强制转换为整数。这样地:

$listOfIds = implode(',',array_map('intval', $ids));
$stmt = $db->prepare(
    "SELECT *
     FROM table
     WHERE id IN($listOfIds)"
);
$stmt->execute();

这不应该受到任何sql注入的攻击。

mqkwyuun

mqkwyuun11#

你在用什么数据库?在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_query 请访问php.net上的页面,了解有关该主题的更多想法和此场景的示例。

daupos2t

daupos2t12#

我认为Solr默是对的。您必须构造查询字符串。

<?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(评论)和somebodyisintrouble都建议foreach循环。。。

(...)
// 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);
?>

(再说一遍,我没有测试)

klr1opcd

klr1opcd13#

这是我的解决办法。我还扩展了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'];
}

让我知道你的想法

yyhrrdl8

yyhrrdl814#

postgres非常简洁的方法是使用postgres数组(“{}”):

$ids = array(1,4,7,9,45);
$param = "{".implode(', ',$ids)."}";
$cmd = $db->prepare("SELECT * FROM table WHERE id = ANY (?)");
$result = $cmd->execute(array($param));
fbcarpbf

fbcarpbf15#

邪恶的解决方案对我不起作用。在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();

相关问题