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表的内容。