在Mysql里,一般情况下,对一个排序后的查询进行分组,其排序会失效,只会根据数据库默认的排序方式(按插入顺序)得到查询结果,然后进行分组。
举个例子:
假设有一张用户登录记录表userLoginLog
,里面记录了用户每次登录的时间。
名称 | 字段 |
用户id |
|
用户名 |
|
登录时间 |
|
现在想从这张表里查询出每个用户的第一次登录时间。
实现的方法有多种,这里不一一赘述。
这里只说一种方式:
- 先按时间倒叙
- 将1的结果根据用户id分组,这样按时间倒叙的第一条记录就会作为分组的记录。
select t.id, t.name, t.loginDate
from (
select id, name, loginDate
from userLoginLog
order by loginDate ASC
) t
group by t.id
很简单直接的逻辑,但是现实并不总是如愿。
在实际查询的时候,分组展示的那条数据并不是排序后的第一条数据,而是数据库默认排序(即按插入顺序排序)的第一条数据。
这时候,只要在子查询里面加入limit
,限制查询数量,就可以在分组的时候使用到我们自定义的排序了。
select t.id, t.name, t.loginDate
from (
select id, name, loginDate
from userLoginLog
order by loginDate ASC
limit 9999999 --加入限制
) t
group by t.id
究其原因,是mysql在执行sql语句的时候,会先调用自身的 查询优化器
先对查询语句进行优化,再执行。在我们原来的子查询里面,查询优化器
就对其进行了优化,丢弃了排序关键字,使用了默认排序方式,以提高查询效率。
而后面加入了limit
关键字,限制了查询长度后, 查询优化器
辨识到子查询加入了更多的限制,在保留limit
的同时,也保留了排序的要求。
注:
这里借用一张图,来解释mysql对语句的执行过程:
顺带复习一下MySQL的关键字执行顺序:
- from
- on
- join
- where
- group by
- 聚合函数,如:avg,sum…
- having
- select
- distinct
- order by
- limit
按照上面的顺序,可以发现一个规律,就是sql语句的执行顺序,是按照从大到小的范围来执行,层层筛选,最后得到所要的数据的。
- (1~3)获取范围:收集查询所需的所有表格。
- (4~7)第一步筛选:对表格内容做各种条件限制,得到符合查询要求的数据集合。
- (8)第二步筛选,出初步结果:在所得数据集合之上,进一步挑选所需字段,缩小查询范围。
- (9~10)优化内容:对最后查询出来的数据集合和字段,进行内容上的优化
- (11)按量输出:按要求输出所需数据量