使用SQL(Hive/Impala语法)删除重复行
解决基于指定字段去重并保留特定行的问题
这问题我平时处理数据时碰到过好多次,SELECT DISTINCT确实搞不定这种只按部分列去重的场景——它会把所有列的组合都算进去,所以哪怕只有一列不同,都会被当成唯一行。针对你要保留2018-02-07重复条目中第二条的需求,最靠谱的方法是用窗口函数ROW_NUMBER(),下面给你具体实现:
方法一:使用CTE结合窗口函数(推荐)
假设你的原查询简化后是这样的:
SELECT event_dates, case_ids, other_column FROM your_table -- 这里是原查询的其他过滤/关联条件
我们可以把原查询包装成一个CTE,给每个(event_dates, case_ids)组合的行按你需要的顺序编号,最后筛选出编号为2的行:
WITH ranked_rows AS ( SELECT event_dates, case_ids, other_column, -- 按能区分"第二条"的列排序,比如主键id、创建时间等 ROW_NUMBER() OVER ( PARTITION BY event_dates, case_ids ORDER BY id ASC -- 替换成你实际用来确定行顺序的列 ) AS row_num FROM your_table -- 原查询的所有条件都放这里 ) SELECT event_dates, case_ids, other_column FROM ranked_rows WHERE row_num = 2;
关键逻辑解释:
PARTITION BY event_dates, case_ids:把所有event_dates和case_ids完全相同的行归为一组ORDER BY id ASC:决定组内行的排序顺序,这里按id升序的话,组内的第二行就是row_num=2的那条;如果你要保留的是最新插入的行,可以改成ORDER BY create_time DESC,然后取row_num=1也能达到类似效果ROW_NUMBER():给每组内的行从1开始依次编号,最后筛选编号为2的行就实现了只保留每组的第二条
方法二:兼容老版本SQL的子查询写法
如果你的数据库不支持CTE(比如MySQL 5.x及以前),可以把上面的逻辑改成子查询形式:
SELECT event_dates, case_ids, other_column FROM ( SELECT event_dates, case_ids, other_column, ROW_NUMBER() OVER ( PARTITION BY event_dates, case_ids ORDER BY id ASC ) AS row_num FROM your_table -- 原查询的所有条件 ) AS sub_query WHERE row_num = 2;
注意事项:
一定要保证ORDER BY后面的列能稳定区分行,比如用主键、唯一时间戳这类不会重复的字段,不然每次执行查询可能得到不同的结果,没法稳定保留你想要的那条行。
内容的提问来源于stack exchange,提问作者Peter Petocz
相关产品推荐
相关产品推荐

