使用WHERE子句将数组传递给查询
给定一个ID数组$galleries = array(1,2,5)
我想有一个SQL查询,在其WHERE子句中使用数组的值,如:
SELECT * FROM galleries WHERE id = /* values of array $galleries... eg. (1 || 2 || 5) */
我怎样才能生成这个查询string用于MySQL?
谨防! 这个答案包含一个严重的SQL注入漏洞。 不要使用这里介绍的代码示例,也不要确保任何外部input都已过滤。
$ids = join("','",$galleries); $sql = "SELECT * FROM galleries WHERE id IN ('$ids')";
使用PDO: [1]
$in = join(',', array_fill(0, count($ids), '?')); $select = <<<SQL SELECT * FROM galleries WHERE id IN ($in); SQL; $statement = $pdo->prepare($select); $statement->execute($ids);
使用MySQLi [2]
$in = join(',', array_fill(0, count($ids), '?')); $select = <<<SQL SELECT * FROM galleries WHERE id IN ($in); SQL; $statement = $mysqli->prepare($select); $statement->bind_param(str_repeat('i', count($ids)), ...$ids); $statement->execute(); $result = $statement->get_result();
说明:
使用SQL IN()
运算符来检查给定列表中是否存在值。
一般来说,它看起来像这样:
expr IN (value,...)
我们可以build立一个expression式放置在我们的数组()
。 请注意,括号内必须至less有一个值,否则MySQL将返回一个错误; 这相当于确保我们的input数组至less有一个值。 为了帮助防止SQL注入攻击,首先生成一个?
为每个input项目创build一个参数化查询。 这里我假设包含你的ID的数组叫做$ids
:
$in = join(',', array_fill(0, count($ids), '?')); $select = <<<SQL SELECT * FROM galleries WHERE id IN ($in); SQL;
给定三个项目的input数组$select
将如下所示:
SELECT * FROM galleries WHERE id IN (?, ?, ?)
再次注意到有一个?
为input数组中的每个项目。 然后,我们将使用PDO或MySQLi来准备和执行上面提到的查询。
使用带有string的IN()
运算符
由于绑定的参数,在string和整数之间很容易改变。 对于PDO,不需要改变; 对于MySQLi,将str_repeat('i',
更改为str_repeat('s',
如果需要检查string。
[1]:为了简洁,我省略了一些错误检查。 您需要检查每个数据库方法的常见错误(或者将您的数据库驱动程序设置为抛出exception)。
[2]:需要PHP 5.6或更高版本。 为了简洁,我再次省略了一些错误检查。
整型:
$query = "SELECT * FROM `$table` WHERE `$column` IN(".implode(',',$array).")";
string:
$query = "SELECT * FROM `$table` WHERE `$column` IN('".implode("','",$array)."')";
假设您事先已经正确地清理了您的input内容
$matches = implode(',', $galleries);
然后调整你的查询:
SELECT * FROM galleries WHERE id IN ( $matches )
引用值取决于你的数据集。
使用:
select id from galleries where id in (1, 2, 5);
一个简单for each
循环将工作。
Flavius / AvatarKava的方式更好,但要确保没有数组值包含逗号。
作为Flavius Stef的回答 ,你可以使用intval()
来确保所有的id
都是int值:
$ids = join(',', array_map('intval', $galleries)); $sql = "SELECT * FROM galleries WHERE id IN ($ids)";
对于具有转义函数的MySQLi :
$ids = array_map(function($a) use($mysqli) { return is_string($a) ? "'".$mysqli->real_escape_string($a)."'" : $a; }, $ids); $ids = join(',', $ids); $result = $mysqli->query("SELECT * FROM galleries WHERE id IN ($ids)");
对于准备好声明的PDO:
$qmarks = implode(',', array_fill(0, count($ids), '?')); $sth = $dbh->prepare("SELECT * FROM galleries WHERE id IN ($qmarks)"); $sth->execute($ids);
更安全。
$galleries = array(1,2,5); array_walk($galleries , 'intval'); $ids = implode(',', $galleries); $sql = "SELECT * FROM galleries WHERE id IN ($ids)";
如果我们正确地过滤了input数组,我们可以使用这个“WHERE ID IN”子句。 像这样的东西:
$galleries = array(); foreach ($_REQUEST['gallery_id'] as $key => $val) { $galleries[$key] = filter_var($val, FILTER_SANITIZE_NUMBER_INT); }
像下面的例子一样:
$galleryIds = implode(',', $galleries);
即现在你应该安全地使用$query = "SELECT * FROM galleries WHERE id IN ({$galleryIds})";
我们应该注意SQL注入漏洞和一个空的情况 。 我将如下处理。
对于一个纯数字数组,使用适当的types转换即每个元素的intval
或floatval
或doubleval
。 对于stringtypesmysqli_real_escape_string()
,如果您愿意,也可以应用于数字值。 MySQL允许数字以及datevariables作为string 。
要在传递给查询之前正确地转义值,请创build一个类似于以下内容的函数:
function escape($string) { // Assuming $db is a link identifier returned by mysqli_connect() or mysqli_init() return mysqli_real_escape_string($db, $string); }
这样的function很可能已经在您的应用程序中可用,或者您已经创build了一个。
清理string数组,如:
$values = array_map('escape', $gallaries);
数组数组可以使用intval
或floatval
或doubleval
进行消毒,
$values = array_map('intval', $gallaries);
最后build立查询条件
$where = count($values) ? "`id` = '" . implode("' OR `id` = '", $values) . "'" : 0;
要么
$where = count($values) ? "`id` IN ('" . implode("', '", $values) . "')" : 0;
由于数组有时也可以是空的,比如$galleries = array();
我们应该注意到IN ()
不允许有一个空的列表。 也可以使用OR
来代替,但问题仍然存在。 所以上面的检查, count($values)
,以确保相同。
并将其添加到最终查询中:
$query = 'SELECT * FROM `galleries` WHERE ' . $where;
提示 :如果你想显示所有的logging(没有过滤)的情况下,而不是隐藏所有行的数组,而不是隐藏所有行,只需在三元的假部分取代1 。
用于PHP的Shrapnel的SafeMySQL库在其参数化查询中提供了types提示的占位符,并且包含了几个用于处理数组的便利占位符。 ?a
占位符将一个数组扩展为以逗号分隔的转义string列表*。
例如:
$someArray = [1, 2, 5]; $galleries = $db->getAll("SELECT * FROM galleries WHERE id IN (?a)", $someArray);
*请注意,由于MySQL执行自动types强制转换,因此SafeMySQL将上面的id转换为string并不重要 – 您仍然可以得到正确的结果。
您可能有表texts
(T_ID (int), T_TEXT (text))
和表test
(id (int), var (varchar(255)))
insert into test values (1, '1,2,3') ;
以下将从表格文本中输出行,其中T_ID IN (1,2,3)
:
SELECT * FROM `texts` WHERE (SELECT FIND_IN_SET( T_ID, ( SELECT var FROM test WHERE id =1 ) ) AS tm) >0
这样你就可以pipe理一个简单的n2m数据库关系,不需要额外的表格,只需要使用SQL,而不需要使用PHP或其他编程语言。
因为原来的问题涉及到一个数组数组,我正在使用一个string数组我不能让给定的例子工作。
我发现每个string都需要用单引号封装在IN()
函数中。
这是我的解决scheme
foreach($status as $status_a) { $status_sql[] = '\''.$status_a.'\''; } $status = implode(',',$status_sql); $sql = mysql_query("SELECT * FROM table WHERE id IN ($status)");
正如你所看到的,第一个函数用single quotes (\')
包装每个数组variables,然后使数组爆炸。
注: $status
在SQL语句中没有单引号。
有可能是一个更好的方式来添加引号,但这个工程。
除了使用IN查询外,在IN查询中有两个选项可能会导致SQL注入漏洞。 你可以使用循环来获得你想要的确切数据,或者你可以使用OR情况下的查询
1. SELECT * FROM galleries WHERE id=1 or id=2 or id=5; 2. $ids = array(1, 2, 5); foreach ($ids as $id) { $data[] = SELECT * FROM galleries WHERE id= $id; }
更多的例子:
$galleryIds = [1, '2', 'Vitruvian Man']; $ids = array_filter($galleryIds, function($n){return (is_numeric($n));}); $ids = implode(', ', $ids); $sql = "SELECT * FROM galleries WHERE id IN ({$ids})"; // output: 'SELECT * FROM galleries WHERE id IN (1, 2)' $statement = $pdo->prepare($sql); $statement->execute();
简单地使用IN子句将起作用。
SELECT * FROM galleries WHERE id IN(1,2,3,4);
防止SQL注入的基本方法是:
- 使用预准备语句和参数化查询
- 转义不安全variables中的特殊字符
使用预处理语句和参数化查询查询被认为是更好的做法,但是如果你select转义字符方法,那么你可以尝试下面的例子。
您可以使用array_map
为$galleries
中的每个元素添加单引号来生成查询:
$galleries = array(1,2,5); $galleries_str = implode(', ', array_map(function(&$item){ return "'" .mysql_real_escape_string($item) . "'"; }, $galleries)); $sql = "SELECT * FROM gallery WHERE id IN (" . $galleries_str . ");";
生成的$ sqlvariables将是:
SELECT * FROM gallery WHERE id IN ('1', '2', '5');
注意: mysql_real_escape_string ( 如其文档中所述)已在PHP 5.5.0中弃用,并且已在PHP 7.0.0中删除。 应该使用MySQLi或PDO_MySQL扩展。 另请参阅MySQL:select一个API指南和相关的FAQ以获取更多信息。 这个function的替代包括:
mysqli_real_escape_string()
PDO ::引用()