如何保留同一email对应最小created_timestamp的行并删除其他行?
解决同一邮箱仅保留最早创建时间行的问题
我懂你遇到的困扰——想给每个邮箱只留创建时间最早的那一行,用GROUP BY的时候发现完全不符合需求,确实踩了个常见的SQL坑。下面给你两种实用的解决方案,适配不同的数据库场景:
示例数据
UUID email created_timestamp 1 a@g.com 2017-05-01 2 a@g.com 2018-05-01 3 a@g.com 2018-05-20 4 b@g.com 2017-04-01 5 b@g.com 2017-06-01
方法一:窗口函数(推荐,直观灵活)
窗口函数是处理这种分组排序需求的绝佳工具,大多数现代数据库(PostgreSQL、MySQL 8.0+、SQL Server等)都支持:
WITH ranked_records AS ( SELECT UUID, email, created_timestamp, -- 按邮箱分组,按创建时间升序编号,最早的行编号为1 ROW_NUMBER() OVER (PARTITION BY email ORDER BY created_timestamp ASC) AS row_rank FROM your_table ) SELECT UUID, email, created_timestamp FROM ranked_records WHERE row_rank = 1;
说明:
PARTITION BY email:把数据按邮箱分成独立的组ORDER BY created_timestamp ASC:每个组内按创建时间从小到大排序,确保最早的行排在最前ROW_NUMBER():给每个组内的行分配唯一编号,最早的行编号为1,最后筛选出编号为1的行即可
如果你的场景中,同一邮箱可能有多行相同的最早创建时间,且需要保留所有这些行,可以把ROW_NUMBER()换成RANK()或者DENSE_RANK(),这样相同时间的行都会得到编号1。
方法二:子查询关联(兼容旧版本数据库)
如果你的数据库不支持CTE或窗口函数(比如MySQL 5.7及更早版本),可以用子查询先找出每个邮箱的最小创建时间,再和原表关联匹配:
SELECT t1.UUID, t1.email, t1.created_timestamp FROM your_table t1 INNER JOIN ( -- 先找出每个邮箱对应的最早创建时间 SELECT email, MIN(created_timestamp) AS earliest_time FROM your_table GROUP BY email ) t2 ON t1.email = t2.email AND t1.created_timestamp = t2.earliest_time;
说明:
- 子查询
t2负责提取每个邮箱的最小创建时间 - 通过
INNER JOIN把原表t1和子查询结果关联,匹配邮箱和对应的最早时间,从而得到目标行 - 这种方法会保留同一邮箱所有拥有最早创建时间的行(如果存在多行时间相同的情况)
为什么之前的GROUP BY不行?
你之前尝试的GROUP BY如果按UUID分组,相当于每行单独成组,完全达不到聚合邮箱的目的;如果强行按email分组,你没办法直接拿到对应的UUID——因为UUID是唯一值,用MIN(UUID)之类的聚合函数得到的结果,不一定对应最早创建时间的那行,所以这种思路走不通。
预期输出
UUID email created_timestamp 1 a@g.com 2017-05-01 4 b@g.com 2017-04-01
内容的提问来源于stack exchange,提问作者Flavio de Falcao e Helena
相关产品推荐
相关产品推荐

