PostgreSQL WHERE子查询优化:筛选含销售记录的用户目标数据
解决按Email优先选取销售记录或最早时间记录的SQL优化方案
嘿,针对你的需求,我给你整理了一个清晰高效的解决方案,刚好能利用你提到的「每个Email要么仅一条has_sales=1,要么全为0」这个前提来简化逻辑。
需求回顾
你需要从表中为每个Email筛选一条记录:
- 优先选取该Email下
has_sales=1的记录 - 如果没有销售记录,就选该Email下
timestamp最早的记录
优化后的SQL实现
我们可以用窗口函数ROW_NUMBER()来实现这个逻辑,这种写法比嵌套子查询更直观,性能也更出色:
SELECT uuid, email, timestamp, has_sales FROM ( SELECT uuid, email, timestamp, has_sales, -- 按Email分组,先按has_sales降序(1优先),再按时间升序 ROW_NUMBER() OVER ( PARTITION BY email ORDER BY has_sales DESC, timestamp ASC ) AS row_rank FROM dup_mail ) ranked_records WHERE row_rank = 1 ORDER BY email;
逻辑解释
- 分组处理:
PARTITION BY email把数据按Email分成独立的组,每个组内单独执行排序逻辑。 - 优先级排序:
ORDER BY has_sales DESC, timestamp ASC确保:- 所有
has_sales=1的记录会排在同组的最前面(降序排序下1>0) - 如果某个Email全是
has_sales=0的记录,就按timestamp升序排列,最早的记录自动排在第一位
- 所有
- 筛选目标记录:
ROW_NUMBER()会给每个组内的记录按排序结果编号,编号为1的就是我们需要的那条记录,外层查询只保留row_rank=1的行即可。
为什么比原SQL更好?
原SQL的子查询方式在数据量较大时可能会重复执行多次MIN(timestamp)的计算,而窗口函数的执行计划通常更高效,只需要一次扫描就能完成分组和排序,同时逻辑上更清晰,后续维护也更方便。
用你的示例数据测试的话,这个SQL会精准返回你预期的结果:
uuid email timestamp has_sales 3 a@gmail.com 2017-10-06 17:08:15 1 6 b@gmail.com 2016-10-02 10:28:23 1
内容的提问来源于stack exchange,提问作者Flavio de Falcao e Helena
相关产品推荐
相关产品推荐

