MySQL中的空IN子句参数列表


74

IN子句为空的情况下执行SQL查询时会发生什么?

例如:

SELECT user WHERE id IN ();

MySQL是否会按预期方式处理(即始终为false),否则,我的应用程序在IN动态构建子句时如何处理这种情况?


id列的值显然不在空集中,因为空集中没有任何内容。另外,事实证明这是不正确的语法。sqlfiddle.com/#!2/a2581/2412
Asad Saeeduddin 2012年

13
我不会在SO上问这个问题,但是在这里找到它很方便。
乔恩·赫尔卡

20
我认为这是一个真正的问题-甚至是一个有用的问题,因为这种极端情况可能会给应用程序带来问题,并且可能需要以某种方式解决。
Brilliand

1
如果您已“构建”(或确定)一个空IN子句,请不要执行查询。嘘!
LoztInSpace

Answers:


60

如果我有一个用于IN动态构建列表的应用程序,但是它最终可能为空,那么我有时要做的就是用一个不可能的值初始化列表并添加到列表中。例如,如果它是用户名列表,我将以空字符串开头,因为这不是可能的用户名。如果它是一个auto_increment ID,我将使用-1,因为实际值始终为正。

如果由于没有不可能的值而导致这不可行,则必须使用条件语句来决定是否AND column IN ($values)WHERE子句中包含表达式。


有人在这里给我们一些SQL推理吗?正如OP所说,直观的行为是始终将其评估为假。
JMess

这只是一个语法问题-他们不想允许空列表。
Barmar


12

使用有效语法对此查询的最接近近似值是:

SELECT user FROM tbl1 WHERE id IN (SELECT id FROM tbl1 WHERE FALSE);

它无条件地返回一个空结果集。括号中的子查询始终返回一个空集,并且在空集中找不到任何值,因为空集不包含任何值。


4
另一个返回空集的子查询是(select top 0 0),其优点是不需要表名。
benrwb

6
IN动态生成文字列表的情况下,这实际上没有帮助。他没有在寻找产生虚空IN子句的方法,他想知道当意外发生时如何处理它,因为给了他空值数组。
Barmar 2015年

2
或简单地SELECT user FROM tbl1 WHERE id IN (FALSE)
Nicola Pedretti

3
@NicolaPedretti假设id不是布尔值字段,这可能是一个合理的假设,但是仍然是一个假设。
Asad Saeeduddin

问题指定了MySQL,SELECT TOP N而不是MySQL。在另一方面(SELECT 0 WHERE 0)。但是,这样做IN (SELECT ...)似乎在MySQL 5.7中创建了一个依赖子查询(如图所示explain),结果查询非常慢。
安塔克

6

使用始终为假的陈述

在创建SQL之前,请检查数组大小。如果大小为0,则将where语句生成为1 = 2,如下所示:

SELECT * FROM USERS WHERE name in () 

变成

SELECT * FROM USERS WHERE 1 = 2 

对于空数组上的“ not in”,生成一个始终为true的语句,如下所示:

SELECT * FROM USERS WHERE name not in () 

变成

SELECT * FROM USERS WHERE 1 = 1

这应适用于更复杂的查询,如下所示:

SELECT * FROM USERS WHERE (name in () OR name = 'Alice') 

变成

SELECT * FROM USERS WHERE (1 = 2 OR name = 'Alice') 

4

如果对ID(1、2、3,..)使用AUTO_INCREMENT,并且数组为空,则可以添加一项[0]。所以会的

if (empty($arr)) {
   $arr[] = 0;
}

SELECT user WHERE id IN (0);

并且不会有mysql parse错误。这种情况在子查询中非常有用-当您的主查询不依赖于子查询结果时。


更好的方法-如果数组为空,则不要调用查询。

$data = null;
if (!empty($arr)) {
    $data = ... call query
}

2

这是空列表的始终为false语句的另一种形式,它保留了逻辑和查询中实际列的概念。

错误的id in ()可以改写为:

where id <> id;

同样,不正确的否定条件id not in ()可以通过自定义查询构建代码转换为:

where id = id;

这种方法比id not in (NULL)不评估为NULL更安全。

但要注意,这会从结果中过滤出where的行id is null。在某些情况下,可以将其视为功能。

对于复杂的堆叠查询构建逻辑特别有用,其中嵌套构建器不知道如何在上面使用结果子查询。


1

我假设您IN为空时仍需要运行查询;例如与LEFT JOIN ... ON ... AND IN ()

当您已经IN在应用程序层中动态构建时,我将在那里处理空白情况。

这是PHP中的一个基本示例

$condition = 'FALSE' // Could feasibly want TRUE under different circumstances
if($values){
   $inParams = **dynamically generated IN parameters from $values**;
   $condition = 'IN (' . $inParams . ')';
}

$sql = 'SELECT * FROM `user` WHERE '.$condition;

如果您不需要使用empty运行查询IN,则不需要;节省您的数据库之旅!

注意:如果还没有的话,我会构建/使用一个可以长期运行并IN正确绑定参数的函数,而不是仅仅将其原始打包为SQL。如果对原始数据使用SQL注入,这将为您提供一些保护,以防止SQL注入。


1

您不能将IN运算符留空,NULL例如,必须在其中添加一些内容。但即使在这种情况下,它也不会按预期方式工作,因为与NULL总是返回NULL(空)进行比较。一种可能的解决方案是添加子选择。

例:

SELECT user_id FROM users;

+-----------+
| user_id   |
+-----------+
|      1000 |
|      1001 |
|      1002 |
|      1003 |
|      1004 |
|      1005 |
|      1006 |
|      1007 |
|      1008 |
|      1009 |
|      1010 |
+-----------+

SELECT user_id FROM users WHERE user_id NOT IN ();
ERROR: 1064: You have an error in your SQL syntax; check the manual that 
corresponds to your MySQL server version for the right syntax to use near ')' at line 1

SELECT user_id FROM users WHERE user_id NOT IN (NULL);
Empty set (0.0003 sec)    

SELECT user_id FROM users WHERE user_id IN (1001, 1002, 1003) 
AND user_id NOT IN (SELECT user_id FROM users WHERE user_id IN (NULL));
+---------+
| user_id |
+---------+
|    1001 |
|    1002 |
|    1003 |
+---------+
3 rows in set (0.0004 sec)

您可以稍微修改(缩短)查询,但是我认为它们也会稍微慢一点。


-1

如果您在应用程序中使用该查询,并向查询动态传递对象列表,则不应调用数据库以不可能的值进行选择,而应返回一个空列表而不调用数据库查询,直。

因为在调用它之前先执行一个知道为空的查询是没有意义的。


避免琐碎的空查询可能是过早的优化。
马提亚斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.