给定一个ids数组$galleries = array(1,2,5),我想有一个SQL查询,在它的WHERE子句中使用数组的值,如:
SELECT *
FROM galleries
WHERE id = /* values of array $galleries... eg. (1 || 2 || 5) */
我如何生成这个查询字符串与MySQL使用?
给定一个ids数组$galleries = array(1,2,5),我想有一个SQL查询,在它的WHERE子句中使用数组的值,如:
SELECT *
FROM galleries
WHERE id = /* values of array $galleries... eg. (1 || 2 || 5) */
我如何生成这个查询字符串与MySQL使用?
当前回答
无PDO的安全方法:
$ids = array_filter(array_unique(array_map('intval', (array)$ids)));
if ($ids) {
$query = 'SELECT * FROM `galleries` WHERE `id` IN ('.implode(',', $ids).');';
}
将$ids变量强制转换为数组 将所有数组值转换为整数 移除重复的值 删除零值 内爆连接所有值到IN选择
其他回答
使用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'。
[1]:为了简洁,我省略了一些错误检查。您需要检查每个数据库方法的常见错误(或设置您的DB驱动程序抛出异常)。
[2]:要求PHP 5.6或更高版本。为了简洁起见,我再次省略了一些错误检查。
下面是我使用的方法,对其他数据使用带有命名占位符的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);
再举一个例子:
$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();
如果正确筛选输入数组,我们可以使用这个"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注入漏洞和空条件。我将按以下方式处理这两个问题。
对于纯数值数组,在每个元素上使用适当的类型转换,即intval或floatval或doubleval。对于字符串类型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);
数值数组可以使用intval或floatval或doubleval来进行清理:
$values = array_map('intval', $gallaries);
最后构建查询条件
$where = count($values) ? "`id` = '" . implode("' OR `id` = '", $values) . "'" : 0;
or
$where = count($values) ? "`id` IN ('" . implode("', '", $values) . "')" : 0;
因为数组有时也可以是空的,比如$galleries = array();因此,我们应该注意IN()不允许空列表。也可以用OR代替,但问题仍然存在。因此,上面的检查count($values)是为了确保相同。
并将其添加到最后的查询:
$query = 'SELECT * FROM `galleries` WHERE ' . $where;
提示:如果你想在一个空数组的情况下显示所有记录(不过滤),而不是隐藏所有行,只需在三元的false部分将0替换为1。