最近刷SQL题,发现好多有点难度的题都是围绕着分组的组内排名来出题的。
这里记录一下聚合函数的一个隐藏的用法;
题目:
请你写出一个sql语句查询在2025-10-15以后,如果有一个用户下单2个以及2个以上状态为购买成功的C++课程或Java课程或Python课程,那么输出这个用户的user_id,以及满足前面条件的第一次购买成功的C++课程或Java课程或Python课程的日期first_buy_date,以及满足前面条件的第二次购买成功的C++课程或Java课程或Python课程的日期second_buy_date,以及购买成功的C++课程或Java课程或Python课程的次数cnt,并且输出结果按照user_id升序排序,以上例子查询结果如下:
本题要求获取每个分组内的第一名和第二名的数据。
而我们都知道,一般情况下,group by以后都是默认取组内第一条记录的,那么我们要获取组内的多个满足条件的记录需要怎么做呢?
具体见下面代码中的min()函数:
select user_id
,min(case when r = 1 then date end) as first_buy_date
,min(case when r = 2 then date end) as second_buy_date
,count(1) as cnt
from (
select *
,rank() over(partition by user_id order by date) as r
from order_info
where 1=1
and status = 'completed'
and date > '2025-10-15'
and product_name in ('C++', 'Python', 'Java'))t
group by user_id
having count(1) >= 2;
min(case when r = 1 then date end)这里如果不加min函数,那么操作对象就是group by取出的默认第一条记录,不符合when r=1这个条件的话就返回null,而加上聚合函数min()的话,case when操作就是面向所有当前组组内的记录了,所以一定能找出r=1的记录的。
理解了这个的话,那么我们可以知道,把min()换成其他的聚合函数也是OK的。
总结:聚合函数的处理对象是当前组组内的记录,利用这个便利,我们可以按照题目要求在聚合函数内部对组内的记录进行筛选。以后类似的求组内的记录可以考虑用这个思路。