MySQL中的链式比较得到了意想不到的结果

Alv*_*oro 4 mysql sql database

我有一个问题,一直在返回null的MySQL语句,虽然我能够弄清楚,但是原因让我有些困惑.

这是有问题的查询的简化版本:

SELECT id FROM users WHERE id = id = 2;
Run Code Online (Sandbox Code Playgroud)

发生错误是因为id =重复,删除其中一个id =解决了问题(因为有一个用户的id为2).但令我有点困惑的是它如何"无声地失败",返回0行,而不是给出错误.

我在SQL Server上测试了类似的查询,并得到了错误消息Incorrect syntax near '='(类似于我对MySQL的预期).

最初,我认为MySQL 在前两个字段之间进行了比较,最后一个是比较的结果(0表示false,1表示true).像这样的东西:

SELECT id FROM users WHERE (id = id) = 2;
Run Code Online (Sandbox Code Playgroud)

但后来我提出了一些与之相矛盾的问题.让我详细说明一下.

想象一下这个表(称为"用户"):

id  | username
----|----------------
0   | anonymous
1   | root
2   | john
3   | doe
Run Code Online (Sandbox Code Playgroud)

如果我这样做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;
Run Code Online (Sandbox Code Playgroud)

没有任何记录具有与用户名相同的ID(它们甚至不是相同的类型:int(11)varchar(25)分别),但是对于第一个记录,我得到一个结果:"匿名".而第二个,我得到所有用户,但"匿名".为什么会这样?我发现它与id0有关,因为如果我将"匿名"id替换为0到4,那么我不会再用第一个查询得到它(它会在第二个查询中出现).

我想这与MySQL在与数字进行比较时将字符串/ varchars转换为0有关.但为什么它允许在同一条款中进行链式比较?比较它们时会遵循什么顺序?

当像这样的查询有效时,事情变得有趣,并且实际上返回(意外?)值.例如:

SELECT * FROM users WHERE id = id = id = username = username = id = username = 1;
Run Code Online (Sandbox Code Playgroud)

返回id为2和3的记录,但没有id为0和1的记录.为什么会这样?


tl; dr版本:为什么带链式比较操作的查询有效?比较中遵循的顺序是什么?这是一个错误还是预期的行为?

  • SQLFiddle与数据库和最后一个查询:http://sqlfiddle.com/#!9/4f2ab/4

bar*_*ity 6

首先,感谢您提出这个有趣的问题.我喜欢玩这个.让我们开始回答:

了解其工作原理的先决条件

了解SELECT-WHERE查询

这很明显,但我正在为每个程序员做出这个答案,即使你真的不知道SQL是如何工作的.一个SELECT-WHERE查询,基本上:

  1. 循环受查询影响的每一行和
  2. where_condition使用该特定行的值来计算.
  3. 如果where_condition结果出现TRUE,则会列出.

伪代码可以是:

for every row:
    current values = row values # for example: username = 'anonymous'
    if (where_condition = TRUE)
        selected_rows.append(this row)
Run Code Online (Sandbox Code Playgroud)

几乎每个字符串都等于0(或FALSE)

除非字符串是数字('1',)或字符串以数字开头'423','-42'否则每个其他字符串等于0(或FALSE)."数字字符串"等于其等效数字,"起始数字字符串"等于其初始数字.提供一些例子:mysql> SELECT'a'= 0;

+---------+
| 'a' = 0 |
+---------+
|       1 |
+---------+
1 row in set, 1 warning (0.00 sec)
Run Code Online (Sandbox Code Playgroud)

.

mysql> SELECT 'john' = 0;
+------------+
| 'john' = 0 |
+------------+
|          1 |
+------------+
1 row in set, 1 warning (0.00 sec)
Run Code Online (Sandbox Code Playgroud)

.

mysql> SELECT '123' = 123;
+-------------+
| '123' = 123 |
+-------------+
|           1 |
+-------------+
1 row in set (0.00 sec)
Run Code Online (Sandbox Code Playgroud)

.

mysql> SELECT '12a5' = 12;
+-------------+
| '12a5' = 12 |
+-------------+
|           1 |
+-------------+
1 row in set, 1 warning (0.00 sec)
Run Code Online (Sandbox Code Playgroud)

WHERE_condition像数学运算一样被解决

链式比较逐一解决,首先是括号的唯一偏好,从左边开始直到a TRUE或者FALSE剩下的.

因此,例如1 = 1 = 0 = 0将追溯如下:

1 = 1 = 0 = 0
 [1]  = 0 = 0
     [0]  = 0
         [1]
Final result: 1 -> TRUE
Run Code Online (Sandbox Code Playgroud)

这个怎么运作

我将追踪最后一个查询,我认为这是最复杂但最美丽的解释:

SELECT * FROM users WHERE id = id = id = username = username = id = username = 1;
Run Code Online (Sandbox Code Playgroud)

首先,我将展示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
Run Code Online (Sandbox Code Playgroud)

现在我将追踪每一行:

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
Run Code Online (Sandbox Code Playgroud)

因此,带有id 23匹配的行,where_condition这就是查询显示的行.

  • 它们将分别转换为 12、13 和 14。现在猜猜 `'16e3'` 将被转换成什么。 (2认同)