在 ORDER BY 子句中使用比较运算符的结果

Dra*_*neg 3 mysql order-by

有一个表 x:

select * from x;

+------+------+
| a    | b    |
+------+------+
| aaa  | 999  |
| bbb  | 888  |
| ccc  | 777  |
+------+------+
Run Code Online (Sandbox Code Playgroud)

以下查询有什么作用?

select * from x order by a < b;
select * from x order by a = b;
select * from x order by a <> b;
select * from x order by a <> b, b;
Run Code Online (Sandbox Code Playgroud)

这个运营商的名字是什么?我在 MySQL 文档中没有找到关于这个主题的任何内容。

Aki*_*ina 6

这是常见的比较运算符。

执行比较时,会产生一些布尔结果。它可能是 TRUE、FALSE 或 NULL。

这 3 个值相关为

NULL < FALSE < TRUE
Run Code Online (Sandbox Code Playgroud)

该关系用于排序。

例如:

select * from x order by a<b;
Run Code Online (Sandbox Code Playgroud)

如果a和/或b为 NULL,则排序表达式的结果(比较结果)为 NULL,此类记录将位于输出的开头。

如果a小于则b排序表达式的结果为 TRUE,并且此类记录将位于输出的末尾。

a大于或等于的记录b将在中间。


Vér*_*ace 5

这些查询都不应该工作 - 它们是可憎的。编写可以查询 a 是否INTEGER小于、等于或大于 a 的SQLCHAR(n)/VARCHAR(n)/TEXT/<any_character_field>完全没有意义的

显示了"REAL"数据库服务器(在本例中为 PostgreSQL)如何处理此类恐怖事件(显示here了其中一个查询的示例输出):

错误:运算符不存在:字符 < 整数第 1 行:选择 * from x order by a<b;
^ 提示:没有运算符匹配给定的名称和参数类型。您可能需要添加显式类型转换。

这些查询在dbfiddle.uk除 MariaDB 之外的所有其他服务器上也会失败(赞美!),这显然对与 MySQL 的 bug-for-bug 兼容有既得利益。这并不奇怪,因为这两个服务器的主要贡献者有相当大的重叠——Monty Widenius是 MariaDB 的 CTO,并且是 MySQL 的创始人,而 MariaDB 被吹捧为 MySQL 的替代品。

MySQL 行为的解释:

MySQL 的 InnoDB 引擎有一种生成 IMPLICIT PRIMARY KEY 的机制,即使 dba/dev 没有声明一个!

here,我们有(来自 MySQL 文档的引用 - 在帖子中链接到):

如果表没有 PRIMARY KEY 或合适的 UNIQUE 索引,InnoDB 在包含行 ID 值的合成列上内部生成一个名为 GEN_CLUST_INDEX 的隐藏聚集索引。行按 InnoDB 分配给此类表中行的 ID 排序。行 ID 是一个 6 字节的字段,随着插入新行而单调增加。因此,按行 ID 排序的行在物理上是按插入顺序排列的。

不幸的是,您无法访问此伪列。没有可访问的隐藏字段 - 不像 Oracle 的服务器那样说,它有大量这样的字段,可以SELECT(参见 参考资料here)。

在 InnoDB 中,如果您不提供 PRIMARY KEY(和...),则隐藏的 BIGINT 将用作 PK。但你不能得到它。

上一个链接中提到的文档说它是一个 6 字节INTEGER而不是一个 BIGINT(但是它8 Bytes - 64 bit与本讨论无关)。

编辑:

另一个证据是here来自的帖子Bill Karwin。他要么是一个MySQL的社区经理,你可以从他的个人资料中看到,是在计算器上一个相当大的打者与> 40万点。他说:

如果没有明确的 ORDER BY,当前版本的 InnoDB 会按照它读取的索引的顺序返回行。哪个索引不同,但它总是从某个索引读取。甚至从“表”中读取也是一个索引——它是主键索引。

正如上面的评论一样,不能保证在 InnoDB 的下一个版本中这将保持不变。您应该将其视为巧合的行为,它没有记录,并且 MySQL 的制造商不承诺不会更改它。

同样在那篇文章中,他举例说明了 MySQL 将如何处理不同的SELECTs 和INDEXes - 值得阅读整个线程!

因此,基本上,如果! 中没有其他字段,MySQL 似乎会按隐式PK(按INSERT顺序)排序 几个简单的测试[ , ]演示了这一点(仅来自第一个测试的结果,如果感兴趣,请参考另一个):KEYSELECT12

CREATE TABLE a (b VARCHAR(10), c INT);
INSERT INTO a VALUES ('aaa', 999), ('bbb', 888), ('ccc', 777);
Run Code Online (Sandbox Code Playgroud)

CREATE TABLE x (y VARCHAR(10), z INT);
INSERT INTO x VALUES ('ccc', 777), ('bbb', 888), ('aaa', 999);
Run Code Online (Sandbox Code Playgroud)

运行:

SELECT * FROM a; 
Run Code Online (Sandbox Code Playgroud)

结果:

b     c
aaa 999
bbb 888
ccc 777
Run Code Online (Sandbox Code Playgroud)

可能是对第一个字段的简单 ASCII 排序。但是,现在将其与:

SELECT * FROM x;
Run Code Online (Sandbox Code Playgroud)

结果:

y     z
ccc 777
bbb 888
aaa 999
Run Code Online (Sandbox Code Playgroud)

但这不是第一个字段的 ASCII 排序。没有 PK,因此它使用(implicit) clustered index PK (*)按插入顺序排序的 !

(*) 这是 MySQL 执行 PK 的方式 - 并非所有服务器都这样做 - 我无法想到另一个默认情况下执行此操作的系统。MySQL 中的所有 PK 都是集群的。

我已经重写了您的查询(请参阅测试 1),以便您可以更好地了解发生了什么!

select a, b, a = b
from x 
order by a = b; 
Run Code Online (Sandbox Code Playgroud)

结果:

  a  b  a = b
aaa 999     0
bbb 888     0
ccc 777     0
Run Code Online (Sandbox Code Playgroud)

在这里,表达式a = b总是假的,因此它按隐藏的 PK 排序,这取决于 INSERT 顺序!

进而:

select a, b, a <> b 
from x 
order by a <> b, b;  
Run Code Online (Sandbox Code Playgroud)

结果:

  a   b   a <> b
ccc 777        1
bbb 888        1
aaa 999        1
Run Code Online (Sandbox Code Playgroud)

在第二种情况下,a != b总是为真,因此它按explicitly specifiedORDER BY 中的第二个(这次)字段排序,即 ( b)!无需依赖隐式PK!

你应该能够从那里找出其余的!

正如上面提到的比尔Karwin指出,这是一个未记录的功能,并应被依赖并可以去除/随时修改。

数据库社区普遍同意,如果你想要一个给定的顺序,那么你应该在ORDER BY子句中指定它,而不是依赖可能在未来版本中改变的未记录的功能!

ps 欢迎来到论坛!还有一个 +1 来提醒我 MySQL 是多么糟糕!