我可以将数组绑定到 PDO 查询中的 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();
?>
并让 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();
?>
哪个确实可以完成这项工作,但只是想知道我是否缺少一个内置的解决方案?
I'm curious to know if it's possible to bind an array of values to a placeholder using PDO. The use case here is attempting to pass an array of values for use with an IN()
condition.
I'd like to be able to do something like this:
<?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();
?>
And have PDO bind and quote all the values in the array.
At the moment I'm doing:
<?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();
?>
Which certainly does the job, but just wondering if there's a built in solution I'm missing?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(23)
您必须手动构建占位符列表,为每个数组成员添加一个占位符。
鉴于
$inQuery
不接受任何输入并且完全由常量值(?,
部分)构造,因此在查询中添加此类变量是安全的。如果查询中还有其他占位符,您可以使用 array_merge() 函数将所有变量连接到一个数组中,以数组的形式添加其他变量,按照它们出现的顺序您的查询:
如果您使用命名占位符,代码会稍微复杂一些,因为您必须创建命名占位符的序列,例如
:id0,:id1,:id2
。 所以代码是:幸运的是,对于命名占位符,我们不必遵循严格的顺序,因此我们可以按任何顺序合并数组。
You'll have to construct the list of placeholders manually, adding a placeholder for each array member.
Given
$inQuery
doesn't take any input and fully constructed from constant values (?,
parts), it's safe to add such a variable in the query.In case there are other placeholders in the query, you could use
array_merge()
function to join all the variables into a single array, adding your other variables in the form of arrays, in the order they appear in your query:In case you are using named placeholders, the code would be a little more complex, as you have to create a sequence of the named placeholders, e.g.
:id0,:id1,:id2
. So the code would be:Luckily, for the named placeholders we don't have to follow the strict order, so we can merge our arrays in any order.
对于快速的事情:
For something quick:
使用
IN
语句有那么重要吗? 尝试使用FIND_IN_SET
操作。例如,PDO 中有一个这样的查询
然后你只需要绑定一个值数组,用逗号进行内爆,就像这个一样,
就完成了。
UPD:正如一些人在对此答案的评论中指出的那样,有一些问题应该明确说明。
FIND_IN_SET
不使用表中的索引,并且尚未实现 - 请参阅这条记录在 MYSQL bug tracker 中。 感谢@BillKarwin 的通知。implode
之后不可能以正确的方式解析此类字符串。 感谢@VaL 的注释。总之,如果您不严重依赖索引并且不使用带逗号的字符串进行搜索,我的解决方案将比上面列出的解决方案更容易、更简单、更快。
Is it so important to use
IN
statement? Try to useFIND_IN_SET
op.For example, there is a query in PDO like that
Then you only need to bind an array of values, imploded with comma, like this one
and it's done.
UPD: As some people pointed out in comments to this answer, there are some issues which should be stated explciitly.
FIND_IN_SET
doesn't use index in a table, and it is still not implemented yet - see this record in the MYSQL bug tracker. Thanks to @BillKarwin for the notice.implode
since you use comma symbol as a separator. Thanks to @VaL for the note.In fine, if you are not heavily dependent on indexes and do not use strings with comma for search, my solution will be much easier, simpler, and faster than solutions listed above.
由于我做了很多动态查询,所以这是我制作的一个超级简单的辅助函数。
像这样使用它:
返回一个字符串
:id1,:id2,:id3
并更新运行查询时所需的$bindArray
绑定。 简单的!Since I do a lot of dynamic queries, this is a super simple helper function I made.
Use it like this:
Returns a string
:id1,:id2,:id3
and also updates your$bindArray
of bindings that you will need when it's time to run your query. Easy!对于 postgres 来说,非常干净的方法是使用 postgres 数组(“{}”):
very clean way for postgres is using the postgres-array ("{}"):
EvilRygy 的解决方案对我不起作用。 在 Postgres 中,您可以采取另一种解决方法:
Solution from EvilRygy didn't worked for me. In Postgres you can do another workaround:
这是我的解决方案:
注意 array_values 的使用。 这可以解决关键的排序问题。
我正在合并 ids 数组,然后删除重复的项目。 我有这样的想法:
那是失败的。
Here is my solution:
Note the use of array_values. This can fix key ordering issues.
I was merging arrays of ids and then removing duplicate items. I had something like:
And that was failing.
查看PDO:预定义常量,没有您可以使用的 PDO::PARAM_ARRAY需要如 PDOStatement->bindParam 上列出的那样
所以我认为这是不可能实现的。
Looking at PDO :Predefined Constants there is no PDO::PARAM_ARRAY which you would need as is listed on PDOStatement->bindParam
So I don't think it is achievable.
我扩展了 PDO 来执行类似于 stefs 建议的操作,从长远来看,这对我来说更容易:
您可以像这样使用它:
I extended PDO to do something similar to what stefs suggests, and it was easier for me in the long run:
You can use it like this:
当你有其他参数时,你可以这样做:
When you have other parameter, you may do like this:
对我来说,更性感的解决方案是构造一个动态关联数组& 用它
For me the sexier solution is to construct a dynamic associative array & use it
我遇到了一个独特的问题,在将即将弃用的 MySQL 驱动程序转换为 PDO 驱动程序时,我必须创建一个可以从同一参数数组动态构建普通参数和 IN 的函数。 所以我很快就构建了这个:
它尚未经过测试,但逻辑似乎已经存在。
经过一些测试,我发现:
I had a unique problem where, while converting the soon-to-be deprecated MySQL driver to the PDO driver I had to make a function which could build, dynamically, both normal parameters and INs from the same parameter array. So I quickly built this:
It is still untested however the logic seems to be there.
After some testing, I found out:
对 Schnalle 代码进行一些编辑
A little editing about the code of Schnalle
如果列只能包含整数,您可能可以在不使用占位符的情况下执行此操作,只需将 id 直接放入查询中即可。 您只需将数组的所有值转换为整数即可。 像这样:
这不应该容易受到任何 SQL 注入的攻击。
If the column can only contain integers, you could probably do this without placeholders and just put the ids in the query directly. You just have to cast all the values of the array to integers. Like this:
This shouldn't be vulnerable to any SQL injection.
你使用什么数据库? 在 PostgreSQL 中我喜欢使用 ANY(数组)。 因此,要重用您的示例:
不幸的是,这是非常不可移植的。
在其他数据库上,您需要像其他人提到的那样创造自己的魔法。 当然,您需要将该逻辑放入类/函数中,以使其在整个程序中可重用。 请查看 PHP.NET 上
mysql_query
页面上的评论,了解有关该主题和此场景示例的更多想法。What database are you using? In PostgreSQL I like using ANY(array). So to reuse your example:
Unfortunately this is pretty non-portable.
On other databases you'll need to make up your own magic as others have been mentioning. You'll want to put that logic into a class/function to make it reusable throughout your program of course. Take a look at the comments on
mysql_query
page on PHP.NET for some more thoughts on the subject and examples of this scenario.据我所知,不可能将数组绑定到 PDO 语句中。
但存在两种常见的解决方案:
使用位置占位符 (?,?,?,?) 或命名占位符 (:id1, :id2, :id3)
$whereIn = implode(',', array_fill(0, count($ids), '?'));
之前引用数组
$whereIn = array_map(array($db, 'quote'), $ids);
这两种选择都很好而且安全。
我更喜欢第二个,因为它更短,并且如果需要的话我可以 var_dump 参数。
使用占位符,您必须绑定值,最终您的 SQL 代码将是相同的。
最后对我来说最重要的是避免错误“绑定变量的数量与标记的数量不匹配”
原则,它是使用位置占位符的一个很好的例子,只是因为它对传入参数有内部控制。
As I know there is no any possibility to bind an array into PDO statement.
But exists 2 common solutions:
Use Positional Placeholders (?,?,?,?) or Named Placeholders (:id1, :id2, :id3)
$whereIn = implode(',', array_fill(0, count($ids), '?'));
Quote array earlier
$whereIn = array_map(array($db, 'quote'), $ids);
Both options are good and safe.
I prefer second one because it's shorter and I can var_dump parameters if I need it.
Using placeholders you must bind values and in the end your SQL code will be the same.
And the last and important for me is avoiding error "number of bound variables does not match number of tokens"
Doctrine it's great example of using positional placeholders, only because it has internal control over incoming parameters.
不可能在 PDO 中使用类似的数组。
您需要为每个值构建一个带有参数(或使用?)的字符串,例如:
:an_array_0, :an_array_1, :an_array_2, :an_array_3, :an_array_4, :an_array_5
是一个示例:
下面 如果你想继续使用
bindParam
,你可以这样做:如果你想使用
?
占位符,你可以这样做:如果你不知道是否 < code>$ids 为空,您应该对其进行测试并相应地处理这种情况(返回空数组,或返回 Null 对象,或引发异常,...)。
It's not possible to use an array like that in PDO.
You need to build a string with a parameter (or use ?) for each value, for instance:
:an_array_0, :an_array_1, :an_array_2, :an_array_3, :an_array_4, :an_array_5
Here's an example:
If you want to keep using
bindParam
, you may do this instead:If you want to use
?
placeholders, you may do it like this:If you don't know if
$ids
is empty, you should test it and handle that case accordingly (return an empty array, or return a Null Object, or throw an exception, ...).在经历了同样的问题之后,我采用了一个更简单的解决方案(尽管仍然不如
PDO::PARAM_ARRAY
那么优雅):给定数组
$ids = array(2, 4 , 32)
:...等等因此
,如果您使用混合值数组,则在分配类型参数之前您将需要更多代码来测试您的值:
但我还没有测试过这个。
After going through the same problem, i went to a simpler solution (although still not as elegant as an
PDO::PARAM_ARRAY
would be) :given the array
$ids = array(2, 4, 32)
:... and so on
So if you are using a mixed values array, you will need more code to test your values before assigning the type param:
But i have not tested this one.
对于 MySQL 和 PDO,我们可以使用 JSON 数组和 JSON_CONTAINS() (https://dev.mysql.com/doc/refman/8.0/en/json-search-functions.html#function_json-contains) 进行搜索 https://dev.mysql.com/doc/refman/8.0/en/json-table-functions.html#function_json-
还可以使用 JSON_TABLE() (https://dev.mysql.com/doc/refman/8.0/en/json-table-functions.html#function_json-table)用于更复杂的情况和 JSON 数据探索:
With MySQL and PDO we can use a JSON array and
JSON_CONTAINS()
(https://dev.mysql.com/doc/refman/8.0/en/json-search-functions.html#function_json-contains) to search in.Whe can also use
JSON_TABLE()
(https://dev.mysql.com/doc/refman/8.0/en/json-table-functions.html#function_json-table) for more complex cases and JSON data exploration :这是我的解决方案,基于 alan_mm的回答。 我还扩展了 PDO 类:
以下是上述代码的示例用法:
Here is my solution, based on alan_mm's answer. I have also extended the PDO class:
Here is a sample usage for the above code:
你首先设置“?”的数量 在查询中然后通过“for”发送参数
像这样 :
you first set number of "?" in query and then by a "for" send parameters
like this :
我更进一步,让答案更接近使用占位符绑定参数的原始问题。
这个答案必须对要在查询中使用的数组进行两次循环。 但它确实解决了使用其他列占位符进行更具选择性查询的问题。
I took it a bit further to get the answer closer to the original question of using placeholders to bind the params.
This answer will have to make two loops through the array to be used in the query. But it does solve the issue of having other column placeholders for more selective queries.
您可以将其转换为:
In this:
并使用此数组执行它:
因此:
最终代码:
You could convert this:
In this:
And execute it with this array:
Thus:
Final code: