前两天看前辈们的老代码,看到了一句神奇的SQL,生平第一次见:
select ……………… from
(select xx1,xx2,xx3,……,row_number()
over(partition by xx4,xx5 order by xx6 desc,xx7 desc) rownum
from xxxx_tbl
where xxx8='sdfdsf' ……)
where rownum =1
为防止公司说泄露源码,就只能这样表示一下意思了,这句sql的灵魂之处在于row_number() over(partition by xx4,xx5 order by xx6 desc,xx7 desc),你们见没见过我不知道,反正我以前没见过。所以一度没看懂。然后就一探究竟了,为了更加直观,我们还是拿前一篇limit重复问题里的那种表来进行举例。
案例
表结构如下:
字段 | 类型 | 注释 |
---|---|---|
id | varchar(20) | 主键 |
col1 | varchar(20) | col1 |
col2 | varchar(20) | col2 |
col3 | varchar(20) | col3 |
全表查询:
SELECT * FROM test1 ORDER BY col1 DESC;
数据为:
id | col1 | col2 | col3 |
---|---|---|---|
15 | 5 | 9 | 10 |
12 | 2 | 5 | 6 |
14 | 2 | 7 | 8 |
16 | 2 | 4 | 5 |
11 | 1 | 2 | 3 |
其中col1字段不是唯一的,第二第三第四行的col1都是2。
上关键sql:
SELECT id,col1,col2,col3,
row_number() over (PARTITION BY col1 ORDER BY id DESC) AS row_num
FROM test1 ;
返回结果:
id | col1 | col2 | col3 | row_num |
---|---|---|---|---|
11 | 1 | 2 | 3 | 1 |
16 | 2 | 4 | 5 | 1 |
14 | 2 | 7 | 8 | 2 |
12 | 2 | 5 | 6 | 3 |
15 | 5 | 9 | 10 | 1 |
我们看这个返回结果,其中row_num列即为这段神奇的sql产生的,这一列的序号怎么来的呢:他是按照col1进行分组,然后按照id列进行倒序排序,row_num为分组后组内排序的结果。
分析
看完案例中的sql和执行结果,其实我们就能猜出开头的那个sql的目的了,他是要获取分组后每组的第一个值。其实我们经常会遇到这样的案例,比如:给你一个全年级学生的分数表,我要获取每个班分数最高的前三名。如果说这个年级有多少个班是已知的,我们可以通过union一个一个子查询拼接起来,但是如果班级个数未知,那这时候如果想用一句sql就有点无奈了。
同样,如果我们想要获取的是每个班级分数最高的一个人,我们也可以通过group by加max函数再加子查询解决,但是这里不是一个。
partition by与group by
一开始其实没搞明白,同样是分组partition by和group by有什么区别。从用法上来看:
partition by
select xx1,xx2,xx3,……,row_number()
over(partition by xx4,xx5 order by xx6 desc,xx7 desc) rownum
from xxxx_tbl
group by
select xx1,max(xx2)
from xxxx_tbl group by xx1
partition by是用在返回参数中的,而group by是用在约束里的。而深层里去理解,partition by是分组后进行组内逐条分析,比如这里的row_number() over,而groupy by则是分组后进行整组的聚合分析,比如上面的max()。
扩展
既然是用于分析的,肯定有一些常用的与之配合的分析函数,比如group常和sum、min、max等组合使用。partition by除了上面的row_number外还有以下一些常用的配合:
- max:获取组内已排序的最大值
SELECT id,col1,col2,col3, MAX(col3)
over (PARTITION BY col1 ORDER BY id DESC) AS row_num FROM test1 ;
id | col1 | col2 | col3 | row_num |
---|---|---|---|---|
11 | 1 | 2 | 3 | 3 |
16 | 2 | 4 | 5 | 5 |
14 | 2 | 7 | 8 | 8 |
12 | 2 | 5 | 6 | 8 |
15 | 5 | 9 | 10 | 10 |
- rank:排名的时候用row_number不是很好,原因是如果有两行order by的id相同,那么row_number就会漏掉其中的一行,而rank则不会漏,同时rank是跳跃排名,比如有两个第二名,那第四个就是第四名,而不是第三名
SELECT id,col1,col2,col3, rank()
over (PARTITION BY col1 ORDER BY id DESC) AS row_num FROM test1 ;
id | col1 | col2 | col3 | row_num |
---|---|---|---|---|
11 | 1 | 2 | 3 | 1 |
16 | 2 | 4 | 5 | 1 |
14 | 2 | 7 | 8 | 2 |
12 | 2 | 5 | 6 | 3 |
15 | 5 | 9 | 10 | 1 |
- dense_rank:和rank一样,也是能够查出所有的记录,但是他不是跳跃排名,两个第二名之后是第三名。
SELECT id,col1,col2,col3, dense_rank() over (PARTITION BY col1 ORDER BY id DESC) AS row_num FROM test1 ;
id | col1 | col2 | col3 | row_num |
---|---|---|---|---|
11 | 1 | 2 | 3 | 1 |
16 | 2 | 4 | 5 | 1 |
14 | 2 | 7 | 8 | 2 |
12 | 2 | 5 | 6 | 3 |
15 | 5 | 9 | 10 | 1 |