SQL语句逻辑执行过程和相关语法详解(5)

MariaDB [test]> select distinct class from Student order by class;
+--------+
| class  |
+--------+
| C      |
| C#    |
| C++    |
| Java  |
| Python |
+--------+

现在假设order by的排序列能使用sid进行排序。那么期待的结果将是根据如下数据进行返回的:

select distinct class from Student order by sid;
+------+--------+
| sid  | class  |
+------+--------+
|    7 | C      |
+---------------+
|    4 | C#    |
+---------------+
|    8 | C++    |
+---------------+
|    1 |        |
|    3 | Java  |
|    6 |        |
|    9 |        |
+---------------+
|    2 |        |
|    5 | Python |
+------+--------+

这样的结构已经违反了关系型数据库的范式要求。因此,sql server和oracle会直接对该语句报错。

但是MySQL/mariadb就允许在order by中使用非select_list列进行排序。它们是如何"偷奸耍滑"的呢?还是上面违反关系模型范式的数据结构,MySQL和mariadb会从Java和Python对应的sid中挑选第一行(order by已经对其排序,因此不是随机数据),然后和Java、Python分别组成一行,得到如下虚拟表:

+------+--------+
| sid  | class  |
+------+--------+
|    7 | C      |
+---------------+
|    4 | C#    |
+---------------+
|    8 | C++    |
+---------------+
|    1 | Java  |
+---------------+
|    2 | Python |
+------+--------+

然后将此虚拟表中非select_list中的列都去掉,得到最终结果。真的是最终结果吗?

MariaDB [test]> select distinct class from Student order by sid;
+--------+
| class  |
+--------+
| Java  |
| Python |
| C#    |
| C      |
| C++    |
+--------+

虽然返回的结果内容上和前面分析的一致,但是顺序却不一致,影响因素就是"order by sid"。

其实认真观察结果,很容易就发现它们是根据sid排序后再对class去重得到的结果。也就是说,ORDER BY子句比DISTINCT子句先执行了。稍稍分析一下,这里先以sid排序,得到如下虚拟结果:

+--------+------+
| class  | sid  |
+--------+------+
| Java  |    1 |
| Python |    2 |
| Java  |    3 |
| C#    |    4 |
| Python |    5 |
| Java  |    6 |
| C      |    7 |
| C++    |    8 |
| Java  |    9 |
+--------+------+

再对class去重,得到如下虚拟结果:

+------+--------+
| sid  | class  |
+---------------+
|    1 |        |
|    3 | Java  |
|    6 |        |
|    9 |        |
+------+--------+
|    2 |        |
|    5 | Python |
+------+--------+
|    4 | C#    |
+---------------+
|    7 | C      |
+---------------+
|    8 | C++    |
+---------------+

最后去掉非select_list中的列sid,得到最终结果。

1.9 关于标准SQL的GROUP BY

如果让我给SQL语句的逻辑执行顺序划分为两段式,我会将"三八线"划在GROUP BY这里。因为在GROUP BY之前甚至完全没有GROUP BY子句的语句部分,操作的对象都是表中的每行数据,也就是说操作的上下文环境是表的数据行。而在GROUP BY之后,操作的对象是组而不再是行,也就是说操作的上下文将从表中的数据行变成组。

直白一点说,GROUP BY之前,关系引擎的目光集中在数据行的细节上,GROUP BY之后,关系引擎的目光则集中在组上。至于每个分组中的行,对关系引擎来说是透明的,它不在乎组中行这种细节性的东西是否存在,而且按照关系模型的要求,也不应该认为它们存在。注意,这里说的是标准SQL,而MySQL和mariadb又"偷奸耍滑"去了。

举个例子就很容易理解GROUP BY前后侧重点的变化过程。

以下是Student表的内容。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/90c3b57f9b6508b170ad70d479052a49.html