And*_*dru 549 php arrays pdo prepared-statement where-in
我很想知道是否可以使用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();
?>
Run Code Online (Sandbox Code Playgroud)
并让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();
?>
Run Code Online (Sandbox Code Playgroud)
这当然能完成这项工作,但只是想知道是否有一个内置的解决方案我缺席了?
ste*_*efs 252
我认为soulmerge是对的.你必须构造查询字符串.
<?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();
?>
Run Code Online (Sandbox Code Playgroud)
修复:丹,你是对的.修复了代码(虽然没有测试)
编辑:克里斯(评论)和somebodyisintrouble建议foreach循环...
(...)
// bindvalue is 1-indexed, so $k+1
foreach ($ids as $k => $id)
$stmt->bindValue(($k+1), $id);
$stmt->execute();
Run Code Online (Sandbox Code Playgroud)
...可能是多余的,所以foreach循环和$stmt->execute可以被...替换
<?php
(...)
$stmt->execute($ids);
?>
Run Code Online (Sandbox Code Playgroud)
(再次,我没有测试它)
Don*_*ghn 168
对于快速的事情:
//$db = new PDO(...);
//$ids = array(...);
$qMarks = str_repeat('?,', count($ids) - 1) . '?';
$sth = $db->prepare("SELECT * FROM myTable WHERE id IN ($qMarks)");
$sth->execute($ids);
Run Code Online (Sandbox Code Playgroud)
Dmi*_*gov 46
使用IN声明是否如此重要?尝试使用FIND_IN_SETop.
例如,PDO中有一个查询
SELECT * FROM table WHERE FIND_IN_SET(id, :array)
Run Code Online (Sandbox Code Playgroud)
然后你只需要绑定一个值数组,用逗号进行内爆,就像这个一样
$ids_string = implode(',', $array_of_smth); // WITHOUT WHITESPACES BEFORE AND AFTER THE COMMA
$stmt->bindParam('array', $ids_string);
Run Code Online (Sandbox Code Playgroud)
它已经完成了.
UPD:正如一些人在对这个答案的评论中指出的那样,有一些问题应该明确说明.
FIND_IN_SET不使用表中的索引,它仍然没有实现 - 请参阅MYSQL错误跟踪器中的此记录.感谢@BillKarwin的通知.implode因为您使用逗号符号作为分隔符,所以无法以正确的方式解析此类字符串.感谢@VaL的笔记.好的,如果您没有严重依赖索引并且不使用带逗号的字符串进行搜索,那么我的解决方案将比上面列出的解决方案更容易,更简单,更快捷.
pro*_*mer 32
由于我做了很多动态查询,这是我做的一个超级简单的辅助函数.
public static function bindParamArray($prefix, $values, &$bindArray)
{
$str = "";
foreach($values as $index => $value){
$str .= ":".$prefix.$index.",";
$bindArray[$prefix.$index] = $value;
}
return rtrim($str,",");
}
Run Code Online (Sandbox Code Playgroud)
像这样使用它:
$bindString = helper::bindParamArray("id", $_GET['ids'], $bindArray);
$userConditions .= " AND users.id IN($bindString)";
Run Code Online (Sandbox Code Playgroud)
返回一个字符串,:id1,:id2,:id3并且还会更新您$bindArray在运行查询时需要的绑定.简单!
Ser*_*kin 17
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();
Run Code Online (Sandbox Code Playgroud)
ESC*_*BAR 17
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));
Run Code Online (Sandbox Code Playgroud)
Chr*_*ris 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;
}
}
Run Code Online (Sandbox Code Playgroud)
你可以像这样使用它:
$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();
Run Code Online (Sandbox Code Playgroud)
Pro*_*ock 12
这是我的解决方案:
$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));
Run Code Online (Sandbox Code Playgroud)
注意array_values的使用.这可以解决密钥排序问题.
我正在合并ID数组,然后删除重复的项目.我有类似的东西:
$ids = array(0 => 23, 1 => 47, 3 => 17);
Run Code Online (Sandbox Code Playgroud)
那是失败的.
Dan*_*zil 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();
Run Code Online (Sandbox Code Playgroud)
Rou*_*dre 11
对我来说,更性感的解决方案是构建一个动态关联数组并使用它
// 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);
Run Code Online (Sandbox Code Playgroud)
小智 10
查看 PDO:Predefined Constants没有PDO :: PARAM_ARRAY,您需要在PDOStatement-> bindParam上列出
bool PDOStatement :: bindParam(mixed $ parameter,mixed&$ variable [,int $ data_type [,int $ length [,mixed $ driver_options]]])
所以我不认为这是可以实现的.
我也意识到这个线程已经老了,但是我有一个独特的问题,在将即将被弃用的mysql驱动程序转换为PDO驱动程序时,我必须创建一个可以动态构建普通params和INs的函数.参数数组.所以我很快建立了这个:
/**
* 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;
}
Run Code Online (Sandbox Code Playgroud)
它仍未经过测试,但逻辑似乎存在.
希望它能帮助处于同一位置的人,
编辑:经过一些测试我发现:
代码编辑为工作版本.
小智 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
Run Code Online (Sandbox Code Playgroud)
你用的是什么数据库?在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();
?>
Run Code Online (Sandbox Code Playgroud)
不幸的是,这非常不便携.
在其他数据库中,你需要像其他人提到的那样构建自己的魔法.您需要将该逻辑放入类/函数中,以使其在整个程序中可重用.请查看mysql_queryPHP.NET页面上的注释,以获取有关该主题的更多想法以及此方案的示例.
如果该列只能包含整数,您可能可以在没有占位符的情况下执行此操作,只需将 id 直接放入查询中即可。您只需将数组的所有值转换为整数。像这样:
$listOfIds = implode(',',array_map('intval', $ids));
$stmt = $db->prepare(
"SELECT *
FROM table
WHERE id IN($listOfIds)"
);
$stmt->execute();
Run Code Online (Sandbox Code Playgroud)
这不应该容易受到任何 SQL 注入的影响。
据我所知,不可能将数组绑定到 PDO 语句中。
但存在两种常见的解决方案:
使用位置占位符 (?,?,?,?) 或命名占位符 (:id1, :id2, :id3)
$whereIn = implode(',', array_fill(0, count($ids), '?'));
之前的报价数组
$whereIn = array_map(array($db, 'quote'), $ids);
这两种选择都是好的和安全的。我更喜欢第二个,因为它更短,如果需要,我可以使用 var_dump 参数。使用占位符,您必须绑定值,最终您的 SQL 代码将是相同的。
$sql = "SELECT * FROM table WHERE id IN ($whereIn)";
Run Code Online (Sandbox Code Playgroud)
对我来说最后也是重要的是避免错误“绑定变量的数量与令牌数量不匹配”
Doctrine 它是使用位置占位符的一个很好的例子,只是因为它对传入的参数有内部控制。
不可能在 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);
}
Run Code Online (Sandbox Code Playgroud)
如果您想继续使用bindParam,您可以这样做:
foreach ($ids as $index => $id) {
$stmt->bindParam("an_array_$index", $ids[$id]);
}
Run Code Online (Sandbox Code Playgroud)
如果你想使用?占位符,你可以这样做:
<?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);
Run Code Online (Sandbox Code Playgroud)
如果您不知道是否$ids为空,则应该对其进行测试并相应地处理这种情况(返回空数组,或返回 Null 对象,或引发异常,...)。
| 归档时间: |
|
| 查看次数: |
196578 次 |
| 最近记录: |