SQL实现单字段去重并取同组另一字段首条记录的查询方法
问题场景
现有包含id1、id2、other三个字段的数据表,样例数据如下:
| id1 | id2 | other |
|---|---|---|
| 1 | a | p |
| 1 | b | q |
| 2 | c | q |
| 2 | d | q |
| 2 | e | q |
需求为实现类似SELECT DISTINCT id1的去重效果,按id1分组后仅保留每个分组下第一次出现的id2取值,过滤其余冗余记录,期望输出结果如下:
| id1 | id2 |
|---|---|
| 1 | a |
| 2 | c |
实现方法
首先需要明确:SQL中的表是无序集合,不存在天然的"先后顺序",要准确定义"第一次出现"的记录,必须有可标识记录插入/排列顺序的字段(比如自增主键、创建时间字段,下文示例中用seq代指该类字段,值越小代表记录出现越早)。
通用写法(支持所有现代数据库)
所有支持SQL:2003标准窗口函数的数据库(MySQL 8.0+、PostgreSQL、SQL Server、Oracle等),可以用ROW_NUMBER()窗口函数实现,逻辑清晰且性能稳定:
SELECT id1, id2 FROM ( SELECT id1, id2, ROW_NUMBER() OVER (PARTITION BY id1 ORDER BY seq ASC) AS row_rank FROM 你的表名 ) tmp WHERE row_rank = 1;
老版本兼容写法(MySQL 5.x等不支持窗口函数的环境)
可以用NOT EXISTS子查询关联实现,效果和窗口函数一致:
SELECT t1.id1, t1.id2 FROM 你的表名 t1 WHERE NOT EXISTS ( SELECT 1 FROM 你的表名 t2 WHERE t2.id1 = t1.id1 AND t2.seq < t1.seq );
特殊场景简化写法
如果你的业务场景中,每个id1分组下需要保留的id2刚好是该分组内最小的取值(和本次样例数据特征匹配),也可以直接用聚合函数简化,但该写法不具备通用性,仅当排序规则和id2本身的排序规则一致时可使用:
SELECT id1, MIN(id2) AS id2 FROM 你的表名 GROUP BY id1;
注意:如果没有明确的排序字段就直接写分组取首行的逻辑,数据库返回的
id2取值是不确定的,可能随执行计划、数据量变化发生改变,无法稳定得到预期结果。
内容的提问来源于stack exchange,提问作者james pow
相关产品推荐
相关产品推荐

