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

时间:2009-05-25 19:32:27

标签: php mysql arrays

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

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

如何生成此查询字符串以用于MySQL?

18 个答案:

答案 0 :(得分:304)

  

请注意!此答案包含严重的SQL injection漏洞。不要使用此处提供的代码示例,也不要确保清除任何外部输入。

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

答案 1 :(得分:290)

使用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]:为简洁起见,我省略了一些错误检查。您需要检查每个数据库方法的常见错误(或将数据库驱动程序设置为抛出异常)。

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

答案 2 :(得分:55)

整数:

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

的字符串:

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

答案 3 :(得分:27)

假设你事先正确消毒了你的输入......

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

然后只需调整您的查询:

SELECT *
FROM galleries
WHERE id IN ( $matches ) 

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

答案 4 :(得分:10)

使用:

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

简单的for each循环可以正常工作。

Flavius/AvatarKava's way更好,但要确保没有数组值包含逗号。

答案 5 :(得分:8)

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

答案 6 :(得分:8)

作为Flavius Stef's answer,您可以使用intval()来确保所有id都是int值:

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

答案 7 :(得分:6)

我们应该处理 SQL injection 漏洞和空条件。我将如下处理两者。

对于纯数字数组,请对每个元素使用相应的类型转换viz 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;
  

提示:如果要在空数组中显示所有记录(无过滤)而不是隐藏所有行,只需将 0 替换为 1 三元组的虚假部分。

答案 8 :(得分:5)

更安全。

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

答案 9 :(得分:5)

如果我们正确地过滤输入数组,我们可以使用这个“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})";

答案 10 :(得分:4)

您可以拥有表格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或其他编程语言。

答案 11 :(得分:4)

中校。 Shrapnel的PHP SafeMySQL库在其参数化查询中提供了类型提示的占位符,并包含了几个方便的占位符来处理数组。 ?a占位符将数组扩展为以逗号分隔的转义字符串列表*。

例如:

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

*请注意,由于MySQL执行自动类型强制,因此SafeMySQL会将上面的ID转换为字符串并不重要 - 您仍然可以获得正确的结果。

答案 12 :(得分:3)

更多例子:

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

答案 13 :(得分:2)

除了使用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;
   }

答案 14 :(得分:2)

因为原始问题与数字数组有关,而且我使用的是字符串数组,所以我无法使给定的例子有效。

我发现需要将每个字符串封装在单引号中以使用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语句中没有单引号。

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

答案 15 :(得分:1)

下面是我使用的方法,使用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);

答案 16 :(得分:1)

没有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选择

答案 17 :(得分:-2)

防止SQL注入的基本方法是:

  • 使用预准备语句和参数化查询
  • 转义不安全变量
  • 中的特殊字符

使用预准备语句和参数化查询查询被认为是更好的做法,但如果您选择转义字符方法,那么您可以尝试下面的示例。

您可以使用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 . ");";

生成的$ sql var将是:

SELECT * FROM gallery WHERE id IN ('1', '2', '5');
  

注意:如mysql_real_escape_string中所述,its documentation here在PHP 5.5.0中已弃用,并且已在PHP 7.0.0中删除。相反,应该使用MySQLi或PDO_MySQL扩展。另请参阅MySQL:选择API指南和相关的常见问题解答以获取更多信息。该功能的替代方案包括:

     
      
  • mysqli_real_escape_string()

  •   
  • PDO ::引号()

  •