SQL分组取前三(包含并列)问题
撸SQL的人经常会碰到这种问题,分组取前几。这个问题也常常作为大厂的面试题之一。
最常见的解决方法是使用x>(count(*))这种,也是大厂面试的标准答案之一。我们来推演一下:
先看下表结构,我们来取每班前3名、前2名和第1名:
标准解答是这样的:
select * from ADU_TEST T
where 3>(select count(*) from ADU_TEST where CLASS=T.CLASS and SCORE>T.SCORE)
结果的确是我们想要的。
我们拆分来理解一下这条SQL。查询ADU_TEST表中的数据,条件是每个班的前三名。也就是同一个班中(CLASS=T.CLASS),比其分数高的(SCORE>T.SCORE),且小于3人的。括号中是一条自连接查询
运行原理:首先匹配一班,从1表中SCORE=10,依次跟2表中SCORE=10,20,30,40,50,60,70,80比较,结果=7不<3,舍;
再到1表中SCORE=20,依次跟2表中SCORE=10,20,30,40,50,60,70,80比较,结果=6不<3,舍; 。
。
。
再到1表中SCORE=60,依次跟2表中SCORE=10,20,30,40,50,60,70,80比较,结果=2<3,留;
再到1表中SCORE=70,依次跟2表中SCORE=10,20,30,40,50,60,70,80比较,结果=1<3,留;
再到1表中SCORE=80,依次跟2表中SCORE=10,20,30,40,50,60,70,80比较,结果=0<3,留;
依次匹配二班、三班。
OK,语句也理解了,也背过了,大功告成。
实际并没什么luan用。
我写SQL的这些年,还从未在生产环境下用过这条语句。原因很简单,他的原理就是IF...FOR...IF...FOR。实际工作中几乎不存在这么一张表,直接存储我们想要的这种数据结构。而是在一个复杂的子查询中再进行分组排列取前几。而基于这条语句的运行原理我们可以看出,他的效率是极其低下的。
以下为真实案例:
真实的需求是在这条子查询基础上,根据C_VIP_ID分组,找出前三,我们用刚才的方法试一下:
很明显,这个很多大厂作为标准答案的方法,在实际业务上并不是很实用。