PostgreSQL单查询实现:获取全部pending状态draws及最新draw记录
问题根因
你之前的UNION语句没跑出预期结果,本质是踩了两个SQL解析的规则坑:
- 没有括号隔离独立子查询时,UNION语句末尾的
ORDER BY ... LIMIT会被引擎解析为对两个分支合并后的全量结果做排序截断,根本不会给第二个查询分支单独排序取最新记录,执行逻辑完全偏离预期。 - UNION默认会自动合并两个分支返回的完全重复行:如果最新的那条记录本身已经属于pending集合,重复行只会被保留一次。这个行为通常不影响业务结果,但在大表场景下会带来不必要的去重排序开销。
调整实现方案
最小改动修正原写法
只需要给取最新记录的分支加一层括号,把排序、截断的逻辑完全限制在子查询内部,让引擎不会把ORDER BY的作用范围错配到整个UNION结果集:
SELECT * FROM draws WHERE state = 'pending' UNION (SELECT * FROM draws ORDER BY created_at DESC LIMIT 1);
注意:两种连接方式的差异可以根据业务需要选择:
- 用
UNION:自动合并两个分支返回的完全重复行,结果集无重复,但大表下会产生去重排序的额外开销;- 用
UNION ALL:不做去重直接拼接结果,查询性能更高,但如果最新记录本身已经属于pending集合,会返回两条完全相同的行。
跨数据库兼容的窗口函数写法
部分早期版本的数据库(比如MySQL 5.x之前的版本)对UNION分支里的括号子查询支持不完善,这时候可以用窗口函数打标记的方式实现同样逻辑,完全不受SQL解析优先级的影响:
SELECT id, created_at, state FROM ( SELECT id, created_at, state, -- 逐行标记:是否属于pending状态集合 CASE WHEN state = 'pending' THEN 1 ELSE 0 END AS is_pending, -- 逐行标记:按创建时间倒序的行号,值为1即为全表最新记录 ROW_NUMBER() OVER (ORDER BY created_at DESC) AS latest_row_num FROM draws ) t WHERE is_pending = 1 OR latest_row_num = 1;
内容的提问来源于stack exchange,提问作者ThreeAccents
相关产品推荐
相关产品推荐

