如何用SQL实现分组内移除两组最高值?
如何用SQL按季度分组并移除每组中最高的两个MOB值?
当然可以实现!从你的示例来看,需求是按季度分组,每个季度里要删掉MOB值最高的两个等级对应的所有记录(比如2016Q1删MOB=26和25的所有行,2016Q2删MOB=19和18的所有行)。下面给你一套通用的解决方案,大部分主流数据库(MySQL 8+、PostgreSQL、SQL Server、Oracle等)都支持:
核心思路
先给每个季度内的MOB值按降序做排名,相同的MOB值分配相同的排名,然后只保留排名大于2的记录——这样就自动去掉了每个季度里最高的两个MOB组。
具体SQL代码
用CTE(公共表表达式)的写法(推荐,可读性强)
WITH ranked_mob AS ( SELECT Quarter, MOB, Amount, -- 按季度分组,MOB降序排名,相同MOB排名一致 DENSE_RANK() OVER (PARTITION BY Quarter ORDER BY MOB DESC) AS mob_rank FROM your_table_name -- 替换成你的实际表名 ) SELECT Quarter, MOB, Amount FROM ranked_mob WHERE mob_rank > 2;
兼容老版本数据库的子查询写法
如果你的数据库不支持CTE(比如MySQL 5.x及以前),可以改用子查询:
SELECT Quarter, MOB, Amount FROM ( SELECT Quarter, MOB, Amount, DENSE_RANK() OVER (PARTITION BY Quarter ORDER BY MOB DESC) AS mob_rank FROM your_table_name -- 替换成你的实际表名 ) AS subquery WHERE mob_rank > 2;
为什么用DENSE_RANK()而不是RANK()/ROW_NUMBER()?
DENSE_RANK()会给相同的MOB值分配相同的排名,比如2016Q2里两个MOB=19的记录都会得到mob_rank=1,两个MOB=18的记录得到mob_rank=2,这样筛选mob_rank>2就会把这两个MOB组的所有记录都排除,完全符合你的需求。- 如果用
ROW_NUMBER(),会给每一行单独编号,那可能只会删掉每个季度的前两行,而不是整个最高的两个MOB组,这不符合你的示例要求。 RANK()和DENSE_RANK()的区别是,当有相同排名时,RANK()会跳过后续的排名号(比如两个1之后直接是3),但在这里用RANK()也能得到正确结果,不过DENSE_RANK()更直观。
验证示例数据
用你的测试数据跑这个SQL,结果会是:
Quarter MOB Amount
2016Q1 24 8520
2016Q1 23 15200
2016Q1 22 3000
2016Q2 17 9530
2016Q2 17 4000
2016Q2 16 8900
2016Q2 16 4550
完全符合你要移除每组最高两个MOB值记录的要求。
内容的提问来源于stack exchange,提问作者Jeremy Wik
相关产品推荐
相关产品推荐

