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

因此一般会给另一个建议,为了确保数据一定是符合预期的,在order by中应该再加一列(最好具有唯一性)作为决胜属性,例如对age排序后再按照sid排序,这样就能保证返回结果不是随机的。

select * from Student order by age,sid;
select * from Student order by age,sid limit 9; 

1.7 关于DISTINCT和GROUP BY

DISTINCT子句用于消除select_list列的重复行,这很容易理解。大多数情况下,DISTINCT子句在功能上都可以认为等价于group by子句。有些DISTINCT不适合做的操作,可以在GROUP BY中来完成。

例如下面两个SQL语句是等价的:

select distinct class,age from Student;
select class,age from Student group by class,age;

正因为等价,很多时候对DISTINCT的优化行为总是和GROUP BY的优化行为一致。以下是sql server上对上述两条语句的执行计划:

select distinct class,age from Student;
  |--Sort(DISTINCT ORDER BY:([test].[dbo].[Student].[class] ASC, [test].[dbo].[Student].[age] ASC))
      |--Table Scan(OBJECT:([test].[dbo].[Student]))
 select class,age from Student group by class,age;
  |--Sort(DISTINCT ORDER BY:([test].[dbo].[Student].[class] ASC, [test].[dbo].[Student].[age] ASC))
      |--Table Scan(OBJECT:([test].[dbo].[Student]))

从结果中看到,执行DISTINCT去除重复行时,默认就带有了排序过程。实际上,DISTINCT几乎总是会将数据复制到内存中的一张临时表中进行,该临时表的结构和前面得到的虚拟表字段结构几乎一致,但却多了一个唯一性索引列用来做重复消除。

但如果DISTINCT结合GROUP BY子句呢?其实不建议这么做。这里也不讨论这种问题。

1.8 关于DISTINCT和ORDER BY

既然DISTINCT默认就带了排序行为,那此时再指定ORDER BY会如何?例如下面的语句:

select distinct class,age from Student ORDER BY age desc;

在SQL Server中的执行计划如下:

select distinct class,age from Student ORDER BY age desc;
  |--Sort(DISTINCT ORDER BY:([test].[dbo].[Student].[age] DESC, [test].[dbo].[Student].[class] ASC))
      |--Table Scan(OBJECT:([test].[dbo].[Student]))

其实和前面没什么区别,无非是先对order by列进行排序而已。但是从这里能看出,DISTINCT和ORDER BY字句其实没有严格的逻辑执行先后顺序,甚至ORDER BY指定的排序列还优先于DISTINCT的排序行为。

但是,DISTINCT和ORDER BY结合时,order by的排序列是有要求的:排序列必须是select_list中的列(distinct很多时候都可以看作group by)。例如select distinct a,b from t order by c;是错误的。但MySQL和mariadb又在这里进行了扩展,它们的排序列允许非select_list中的列。

先说标准SQL为何不允许使用非select_list中的列,这归根结底还是关系型数据库的范式问题。假如DISTINCT消除了部分列的重复值,最终将只返回一条重复记录,而如果使用非select_list的列排序,将要求返回一条重复记录的同时还要返回每个重复值对应的多条记录以便排序,而在要求范式的关系表中是无法整合这样的结果。

例如表中数据如下:

MariaDB [test]> select sid,age,class from Student order by class;
+------+------+--------+
| sid  | age  | class  |
+------+------+--------+
|    7 |  22 | C      |
|    4 |  20 | C#    |
|    8 |  20 | C++    |
|    1 |  22 | Java  |
|    3 |  21 | Java  |
|    6 |  19 | Java  |
|    9 |  24 | Java  |
|    2 |  23 | Python |
|    5 |  21 | Python |
+------+------+--------+

现在对class列进行去重。

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

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