使用WHERE子句将数组传递给查询

给定一个id数组$galleries = array(1,2,5),我想有一个SQL查询,在它的WHERE子句中使用数组的值,如:

SELECT *
FROM galleries
WHERE id = /* values of array $galleries... eg. (1 || 2 || 5) */

我如何生成这个查询字符串与MySQL使用?

657755 次浏览

使用:

select id from galleries where id in (1, 2, 5);

一个简单的for each循环就可以工作。

弗拉菲乌/ AvatarKava更好,但要确保数组值中没有包含逗号。

小心!此答案包含严重的SQL注入漏洞。不要使用这里给出的代码示例,除非确保任何外部输入都被清除。

$ids = join("','",$galleries);
$sql = "SELECT * FROM galleries WHERE id IN ('$ids')";

假设你事先正确地清理了你的输入……

$matches = implode(',', $galleries);

然后调整你的查询:

SELECT *
FROM galleries
WHERE id IN ( $matches )

根据数据集适当地引用值。

因为最初的问题涉及到一个数字数组,而我使用的是一个字符串数组,我无法使给定的例子工作。

我发现每个字符串都需要封装在单引号中才能使用IN()函数。

这是我的解决方案

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 (\')中包装每个数组变量,然后内爆数组。

注意:$status在SQL语句中没有单引号。

可能有更好的方式添加引号,但这是可行的。

你可能有表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

这样,您就可以管理一个简单的n2m数据库关系,而不需要额外的表,并且只使用SQL,而不需要使用PHP或其他编程语言。

整数:

$query = "SELECT * FROM `$table` WHERE `$column` IN(".implode(',',$array).")";

字符串:

$query = "SELECT * FROM `$table` WHERE `$column` IN('".implode("','",$array)."')";

使用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,...)

我们可以从数组中构建一个表达式放在()中。注意,括号内必须至少有一个值,否则MySQL将返回错误;这相当于确保我们的输入数组至少有一个值。为了防止SQL注入攻击,首先为每个输入项生成?以创建参数化查询。这里我假设包含id的数组名为$ids:

$in = join(',', array_fill(0, count($ids), '?'));


$select = <<<SQL
SELECT *
FROM galleries
WHERE id IN ($in);
SQL;

给定一个包含三个元素的输入数组,$select将如下所示:

SELECT *
FROM galleries
WHERE id IN (?, ?, ?)

再次注意,输入数组中的每一项都有?。然后,我们将使用PDO或MySQLi来准备和执行上面提到的查询。

使用带有字符串的IN()操作符

由于绑定了参数,所以很容易在字符串和整数之间进行更改。对于PDO,不需要更改;对于MySQLi,如果你需要检查字符串,将str_repeat('i',更改为str_repeat('s',

为了简洁起见,我省略了一些错误检查。您需要检查每个数据库方法的常见错误(或设置您的DB驱动程序抛出异常)

<一口> [2]:要求PHP 5.6或更高版本。为简洁起见,我再次省略了一些错误检查

Flavius Stef的回答一样,你可以使用intval()来确保所有的id都是int值:

$ids = join(',', array_map('intval', $galleries));
$sql = "SELECT * FROM galleries WHERE id IN ($ids)";

更安全。

$galleries = array(1,2,5);
array_walk($galleries , 'intval');
$ids = implode(',', $galleries);
$sql = "SELECT * FROM galleries WHERE id IN ($ids)";

除了使用IN查询,您还有两个选择,因为在IN查询中存在SQL注入漏洞的风险。你可以使用循环来获得你想要的确切数据,或者你可以使用带有大小写的查询

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;
}

如果正确筛选输入数组,我们可以使用这个"WHERE id IN"子句。就像这样:

$galleries = array();


foreach ($_REQUEST['gallery_id'] as $key => $val) {
$galleries[$key] = filter_var($val, FILTER_SANITIZE_NUMBER_INT);
}

如下例:enter image description here

$galleryIds = implode(',', $galleries);

也就是说,现在你应该安全地使用$query = "SELECT * FROM galleries WHERE id IN ({$galleryIds})";

对于带有转义函数的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);

我们应该注意SQL注入漏洞和空的条件。我将按以下方式处理这两个问题。

对于纯数值数组,在每个元素上使用适当的类型转换,即intvalfloatvaldoubleval。对于字符串类型mysqli_real_escape_string(),如果你愿意,也可以应用于数值。MySQL允许数字和日期变量作为字符串

为了在传递到查询之前适当地转义值,创建一个类似于以下的函数:

function escape($string)
{
// Assuming $db is a link identifier returned by mysqli_connect() or mysqli_init()
return mysqli_real_escape_string($db, $string);
}

这样的函数很可能在您的应用程序中已经可用,或者您已经创建了一个。

净化字符串数组,如下所示:

$values = array_map('escape', $gallaries);

数值数组可以使用intvalfloatvaldoubleval进行消毒:

$values = array_map('intval', $gallaries);

最后构建查询条件

$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;

提示:如果你想在一个空数组的情况下显示所有记录(不过滤),而不是隐藏所有行,只需将三元的false部分中的0替换为1

Col. Shrapnel的PHP SafeMySQL库在其参数化查询中提供了类型提示占位符,并包括两个用于处理数组的方便占位符。?a占位符将数组展开为逗号分隔的转义字符串列表*。

例如:

$someArray = [1, 2, 5];
$galleries = $db->getAll("SELECT * FROM galleries WHERE id IN (?a)", $someArray);

*请注意,由于MySQL执行自动类型强制,SafeMySQL将上面的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();

下面是我使用的方法,对其他数据使用带有命名占位符的PDO。为了克服SQL注入,我过滤了数组,只接受整数的值,并拒绝所有其他的值。

$owner_id = 123;
$galleries = array(1,2,5,'abc');


$good_galleries = array_filter($chapter_arr, 'is_numeric');


$sql = "SELECT * FROM galleries WHERE owner=:OWNER_ID AND id IN ($good_galleries)";
$stmt = $dbh->prepare($sql);
$stmt->execute(array(
"OWNER_ID" => $owner_id,
));


$data = $stmt->fetchAll(PDO::FETCH_ASSOC);

无PDO的安全方法:

$ids = array_filter(array_unique(array_map('intval', (array)$ids)));


if ($ids) {
$query = 'SELECT * FROM `galleries` WHERE `id` IN ('.implode(',', $ids).');';
}
  • (array)$ids$ids变量强制转换为数组
  • array_map将所有数组值转换为整数
  • array_unique删除重复值
  • array_filter删除零值
  • implode将所有值连接到IN选择中