首页 文章

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

提问于
浏览
277

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

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

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

18 回答

  • 5

    对于具有转义功能的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);
    
  • 10

    Using 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);
    

    Using 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或更高版本 . 为了简洁起见,我省略了一些错误检查 .

  • 8

    整型:

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

    字符串:

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

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

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

    当心!此答案包含严重的SQL注入漏洞 . 请勿使用此处提供的代码示例,而不确保清除任何外部输入 .

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

    使用:

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

    一个简单的 for each 循环将起作用 .

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

  • 288

    除了使用IN查询之外,您还有两个选项可以执行此操作,因为在IN查询中存在SQL注入漏洞的风险 . 您可以使用 looping 获取所需的确切数据,也可以使用 OR case查询

    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;
       }
    
  • 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,如其文档中所述,在PHP 5.5.0中已弃用,并已在PHP 7.0.0中删除 . 相反,应该使用MySQLi或PDO_MySQL扩展 . 另请参阅MySQL:选择API指南和相关的常见问题解答以获取更多信息 . 此函数的替代方法包括:mysqli_real_escape_string()PDO :: quote()

  • 2

    更安全 .

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

    我们应该处理SQL injection漏洞和空洞的情况 . 我将如下处理两者 .

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

    or

    $where  = count($values) ? "`id` IN ('" . implode("', '", $values) . "')" : 0;
    

    由于数组有时也可能为空,如 $galleries = array(); ,因此我们应注意 IN () 不允许空列表 . 也可以使用 OR ,但问题仍然存在 . 所以上面的检查, count($values) ,是为了确保相同 .

    并将其添加到最终查询:

    $query  = 'SELECT * FROM `galleries` WHERE ' . $where;
    

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

  • 0

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

    例如:

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

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

  • 0

    你可能有表 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或其他编程语言 .

  • 282

    更多例子:

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

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

  • 6

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

  • 4

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

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

    然后只需调整您的查询:

    SELECT *
    FROM galleries
    WHERE id IN ( $matches )
    

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

  • 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语句中没有单引号 .

    可能有一种更好的方法来添加引号,但这有效 .

  • -2

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

相关问题