我遇到了一个MySQL语句的问题,它一直在返回null,虽然我能够弄明白,但是这个原因让我有些困惑。
这是有问题的查询的简化版本:
SELECT id FROM users WHERE id = id = 2;
错误发生是因为id =
重复,删除其中一个id =
解决了问题(因为有一个id为2的用户)。但令我有点困惑的是它如何“无声地失败”,返回0行,而不是给出错误。
我在SQL Server上测试了一个类似的查询,并得到了错误消息Incorrect syntax near '='
(类似于我对MySQL的期望)。
最初,我认为MySQL正在前两个字段之间进行比较,最后一个是比较的结果(0表示false,1表示true)。像这样:
SELECT id FROM users WHERE (id = id) = 2;
但后来我发了一些与之相矛盾的问题。让我详细说明一下。
想象一下这个表(称为“用户”):
id | username
----|----------------
0 | anonymous
1 | root
2 | john
3 | doe
如果我SELECT * FROM users WHERE id = id = 1
,我会收到所有4位用户。而SELECT * FROM users WHERE id = id = 0
,我没有得到任何。这似乎证实了比较理论。但是,如果我做这样的事情会让事情变得混乱:
SELECT * FROM users WHERE id = username = 1;
SELECT * FROM users WHERE id = username = 0;
没有任何记录与用户名具有相同的ID(它们甚至不是同一类型:int(11)
和varchar(25)
),但是对于第一个,我得到一个结果:“匿名”。而第二个,我得到所有用户,但“匿名”。为什么会这样?我发现它与id
为0有关,因为如果我将“匿名”id替换为0到4,那么我不会再用第一个查询得到它(并且它显示在第二个)。
我想这与 MySQL在与数字比较时将字符串/ varchars转换为0有关。但为什么它允许在同一条款中进行链式比较?比较它们时会遵循什么顺序?
当这样的查询有效并且实际返回(意外的?)值时,搞笑。例如:
SELECT * FROM users WHERE id = id = id = username = username = id = username = 1;
返回id为2和3的记录,但没有id为0和1的记录。为什么会这样?
tl; dr version:为什么带链式比较操作的查询有效?比较中遵循的顺序是什么?这是一个错误还是预期的行为?
答案 0 :(得分:6)
首先,感谢您提出这个有趣的问题。我喜欢玩这个。让我们开始回答:
这很明显,但我正在为每个程序员做出这个答案,即使你真的不知道SQL是如何工作的。一个SELECT-WHERE
查询,基本上是:
where_condition
。where_condition
结果为TRUE
,则会列出。{/ li>
醇>
伪代码可以是:
for every row:
current values = row values # for example: username = 'anonymous'
if (where_condition = TRUE)
selected_rows.append(this row)
除非字符串是数字('1'
,'423'
,'-42'
)或字符串以数字开头,否则每个其他字符串等于0
(或{{1 }})。 “数字字符串”等于其等效数字,“起始数字字符串”等于其初始数字。提供一些例子:
MySQL的> SELECT'a'= 0;
FALSE
+---------+
| 'a' = 0 |
+---------+
| 1 |
+---------+
1 row in set, 1 warning (0.00 sec)
。
mysql> SELECT 'john' = 0;
+------------+
| 'john' = 0 |
+------------+
| 1 |
+------------+
1 row in set, 1 warning (0.00 sec)
mysql> SELECT '123' = 123;
+-------------+
| '123' = 123 |
+-------------+
| 1 |
+-------------+
1 row in set (0.00 sec)
逐个解析链式比较,首先是括号的唯一偏好,从左侧开始直到mysql> SELECT '12a5' = 12;
+-------------+
| '12a5' = 12 |
+-------------+
| 1 |
+-------------+
1 row in set, 1 warning (0.00 sec)
或TRUE
为剩余。
因此,例如FALSE
将被跟踪如下:
1 = 1 = 0 = 0
我将追踪最后一个查询,我认为这是最复杂但最美丽的解释:
1 = 1 = 0 = 0
[1] = 0 = 0
[0] = 0
[1]
Final result: 1 -> TRUE
首先,我将向每个SELECT * FROM users WHERE id = id = id = username = username = id = username = 1;
显示每个行变量:
where_condition
id = id = id = username = username = id = username = 1
现在我将追踪每一行:
0 = 0 = 0 = 'anonymous' = 'anonymous' = 0 = 'anonymous' = 1
1 = 1 = 1 = 'root' = 'root' = 1 = 'root' = 1
2 = 2 = 3 = 'john' = 'john' = 2 = 'john' = 1
3 = 3 = 3 = 'doe' = 'doe' = 3 = 'doe' = 1
因此,0 = 0 = 0 = 'anonymous' = 'anonymous' = 0 = 'anonymous' = 1
[1] = 0 = 'anonymous' = 'anonymous' = 0 = 'anonymous' = 1
[0] = 'anonymous' = 'anonymous' = 0 = 'anonymous' = 1
[1] = 'anonymous' = 0 = 'anonymous' = 1
[0] = 0 = 'anonymous' = 1
[1] = 'anonymous' = 1
[0] = 1
[0] -> no match
1 = 1 = 1 = 'root' = 'root' = 1 = 'root' = 1
[1] = 1 = 'root' = 'root' = 1 = 'root' = 1
[1] = 'root' = 'root' = 1 = 'root' = 1
[0] = 'root' = 1 = 'root' = 1
[1] = 1 = 'root' = 1
[1] = 'root' = 1
[0] = 1
[0] -> no match
2 = 2 = 3 = 'john' = 'john' = 2 = 'john' = 1
[1] = 3 = 'john' = 'john' = 2 = 'john' = 1
[0] = 'john' = 'john' = 2 = 'john' = 1
[1] = 'john' = 2 = 'john' = 1
[0] = 2 = 'john' = 1
[0] = 'john' = 1
[1] = 1
[1] -> match
3 = 3 = 3 = 'doe' = 'doe' = 3 = 'doe' = 1
[1] = 3 = 'doe' = 'doe' = 3 = 'doe' = 1
[0] = 'doe' = 'doe' = 3 = 'doe' = 1
[1] = 'doe' = 3 = 'doe' = 1
[0] = 3 = 'doe' = 1
[0] = 'doe' = 1
[1] = 1
[1] -> match
id
和2
的行与3
匹配,这就是查询显示的行。
答案 1 :(得分:1)
我根本不知道MYSQL,但怀疑你遇到了一个副作用,即where子句的每个谓词都必须求值为true。 E.g。
Select *
from table
where 1 = 1
实际上在说
Select *
from table
where is_true(1 = 1)
我建议你尝试除1和0之外的其他值,看看你得到了什么结果。
显然(id = id)为真所以在使用0和1表示false / true时这等于1
经过一些实验后,Alvaro Montoro对此进行了补充 (int == int)= true; 但对于int> 1:(int == true)= false,(int == false)= false。
答案 2 :(得分:1)
此查询:
SELECT *
FROM users
WHERE id = username = 1;
对于" anonymous"会返回true,如果它被解析为,则返回0:
SELECT *
FROM users
WHERE id = (username = 1);
id
为0,因此它表示" false =" false"。
请注意,这些是不任何类型的链式比较。 MySQL正在将比较转换为整数(0和1),然后应用=
。因此,1 = 1 = 1
为真,但0 = 0 = 0
为假。所以,这是二元逻辑和相等比较的奇怪组合。
我的建议是不要在表达式中使用多个比较。忘记MySQL支持这一事实 - 它不是标准的一部分,大多数数据库都不会这样做。