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

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

评论

可能链接的问题:stackoverflow.com/questions/6617620/…stackoverflow.com/questions/7538927/…stackoverflow.com/questions/5295714/array-in-sql-querystackoverflow.com/questions/8205816/…stackoverflow.com/问题/ 7298216 /…

@trante这是最古老的(2009)。

是否有类似问题的解决方案,例如SELECT * FROM TABLE WHERE NAME LIKE('ABC%'或'ABD%'OR ..)

#1 楼


注意!该答案包含一个严重的SQL注入漏洞。不要使用此处提供的代码示例,而请确保未清除所有外部输入。



评论


标识符仍然是带引号的列表,因此例如以“ WHERE id IN('1,2,3,4')”形式出现。您需要分别为每个标识符加上引号,否则请将括号内的引号引起来。

–抢夺
09年5月25日在20:05

我只是添加警告:在此语句之前,应验证$ galleries的输入!预处理语句无法处理数组AFAIK,因此,如果您习惯于绑定变量,则可以在此处轻松地进行SQL注入。

– leemes
2012年3月27日上午11:07

对于像我这样使用PHP的新手,有人可以解释一下,也可以向我指向要解释的资源,这为什么易于注入,以及应该如何正确地进行预防?如果ID列表是在下一个查询运行之前立即从查询生成的,那仍然很危险吗?

– ministe2003
2014年9月9日下午13:52

@ ministe2003想象一下$ galleries是否具有以下值:array('1);从用户中选择密码; / *')。如果您不清除它,查询将显示为SELECT * FROM Galleries ID IN(1);选择来自用户的密码; / *)。将表和列名更改为数据库中的名称,然后尝试查询,并检查结果。您会找到一个密码列表,而不是图库列表。根据数据的输出方式或脚本对一系列意外数据的处理方式,可能会将其输出到公共视图中。

–克里斯·贝克(Chris Baker)
2014年10月2日15:19

对于所提问题,这是一个完全有效和安全的答案。谁抱怨这并不安全-我如何通过问题中提供的$ galleries设置此特定代码的交易,然后使用上述“ sql注入漏洞”来利用它。如果不能,请给我200美元。那个怎么样?

–zerkms
15年4月15日在21:36

#2 楼

使用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:输入数组中的每个项目都有一个$select。然后,我们将使用PDO或MySQLi如上所述准备和执行查询。对于PDO,无需更改;对于MySQLi,如果需要检查字符串,请将?更改为IN()
[1]:为简洁起见,我省略了一些错误检查。您需要检查每种数据库方法的常见错误(或将数据库驱动程序设置为引发异常)。
[2]:需要PHP 5.6或更高版本。同样,为了简洁起见,我省略了一些错误检查。

评论


... $ ids有什么作用?我收到“语法错误,意外的'。'”。

–马塞尔
2014年9月18日下午0:43

我看到他们,我正在使用MySQLi,并且我有php 5.6

–马塞尔
2014-09-18 15:52

如果您指的是$ statement-> bind_param(str_repeat('i',count($ ids)),... $ ids);然后...将ID从数组扩展为多个参数。如果您指的是expr IN(值,...),则意味着可以有更多的值,例如WHERE id IN(1、3、4)。至少需要有一个。

–莱维·莫里森(Levi Morrison)
16年4月26日在19:27

我很困惑<<<是什么,但是找到了参考:php.net/manual/en/…

–赞加人
16年6月16日在4:23

另外,这里是...的参考:wiki.php.net/rfc/argument_unpacking

–赞加人
16年6月16日在4:35

#3 楼

整数:

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


字符串:

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


评论


为什么是“ \”?请告诉我

– zloctb
2013年9月20日上午11:42

#4 楼

假设您事先正确清理了输入内容...

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


然后只需调整您的查询即可:

SELECT *
FROM galleries
WHERE id IN ( $matches ) 


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

评论


我尝试了您的建议,但它只是获取了第一个键值。我知道这没有意义,但是如果我使用user542568示例进行操作,那么该死的事情就可以了。

–塞缪尔·拉姆赞(Samuel Ramzan)
4月25日18:16

#5 楼

使用:

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


一个简单的for each循环将起作用。包含逗号。

#6 楼

作为Flavius Stef的答案,您可以使用intval()来确保所有id均为int值:

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


#7 楼

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


评论


这很好,简短,可以避免代码插入漏洞! +1

–斯蒂芬·里希特(Stephan Richter)
17年11月8日在9:20

MySQLi也准备了语句。不要逃避您的输入,这可能仍然容易受到SQL注入的攻击。

–达尔曼
2月25日在22:54

#8 楼

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

对于纯数字数组,请在每个元素上使用适当的类型转换,即intvalfloatvaldoubleval。对于字符串类型mysqli_real_escape_string(),如果需要,也可以将其应用于数字值。 MySQL允许数字和日期变体作为字符串使用。 br />这样的功能很可能已经在您的应用程序中为您提供了,或者您已经创建了一个功能。

可以使用intvalfloatvaldoubleval代替数字数组来对数字数组进行清理: >
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);


由于有时数组也可以为空,例如$galleries = array();,因此我们应该注意不允许有一个空列表。也可以改用IN (),但问题仍然存在。因此,上述检查OR可以确保相同。

并将其添加到最终查询中:

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



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

评论


为了使我的解决方案成为一线(和丑陋的),以防万一有人需要:$ query ='SELECT * FROM Galleries WHERE'。 (count($ gallaries)?“ id IN('”。implode(“','”,array_map('escape',$ gallaries))。“')”:0);

–伊扎尔·阿兹米(Izhar Aazmi)
15年4月17日在13:38

#9 楼

更安全。

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


#10 楼

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

例如:

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


*请注意,由于MySQL执行自动类型强制,SafeMySQL将上面的id转换为字符串没关系-您仍然会得到正确的结果。

#11 楼

如果我们正确过滤输入数组,则可以使用此“ WHERE id IN”子句。像这样的例子:

$galleries = array();

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


像下面的例子:现在您应该安全使用$query = "SELECT * FROM galleries WHERE id IN ({$galleryIds})";

评论


@ levi-morrison发布了很多更好的解决方案。

– Supratim Roy
15年4月18日在7:32

#12 楼

您可能具有表texts和表(T_ID (int), T_TEXT (text)) test

(id (int), var (varchar(255)))中,以下内容将从表文本输出行,其中insert into test values (1, '1,2,3') ;:您可以管理简单的n2m数据库关系,而无需额外的表,而仅使用SQL,而无需使用PHP或其他某种编程语言。

#13 楼

更多示例:

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


#14 楼

不使用PDO的安全方法:

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

if ($ids) {
    $query = 'SELECT * FROM `galleries` WHERE `id` IN ('.implode(',', $ids).');';
}
 




(array)$ids Cast $ids变量到数组

array_map将所有数组值转换为整数

array_unique删除重复值

array_filter删除零值

implode将所有值连接到IN选择


#15 楼

除了使用IN查询外,您还有两种选择,因为在IN查询中存在SQL注入漏洞的风险。您可以使用循环获取所需的确切数据,也可以将查询与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;
   }


#16 楼

因为原始问题与数字数组有关,并且我正在使用字符串数组,所以无法使给定的示例起作用。

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

评论


或者$ filter =“'”。 implode(“','”,$ status)。 “'”;

–亚历杭德罗·萨拉曼卡·马祖埃洛(Alejandro Salamanca Mazuelo)
15年4月13日在20:06



这是易受攻击的。

– Mark Amery
15年5月17日在18:30

字符串转义在哪里?例如'字符串内? SQL注入易受攻击。使用PDO :: quote或mysqli_real_escape_string。

– 18C
17年12月21日在13:41

#17 楼

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


#18 楼

防止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指南和相关的FAQ,以获取更多信息。此功能的替代方法包括:


mysqli_real_escape_string()
PDO :: quote()



评论


与其他答案相比,这不仅没有添加任何新内容,而且尽管存在接受了多年的关于SQL注入的警告,但它容易受到注入的攻击。

– Mark Amery
15年5月17日在18:37