为何`date_trunc('day', current_date)+interval...`会导致PostgreSQL查询挂起?
PostgreSQL查询使用当天结束时间作为范围上限时无限挂起的原因分析
核心问题:子查询返回行数的巨大差异
两个range_values子查询的本质区别在于返回的结果行数:
- 第一个子查询使用
max(lasttime)聚合函数,会对people表做聚合计算,最终仅返回1行结果。 - 第二个子查询没有任何聚合操作,只是从
people表中查询两个与表行无关的常量值(date_trunc('month', current_date)和当天结束时间都是固定值),这会导致子查询返回与people表总行数完全一致的结果集。
如果people表数据量较大(比如几十万甚至百万行),第二个子查询会生成海量重复的minval和maxval记录,后续查询基于这个超大结果集进行关联、过滤时,会产生指数级的计算量,直接导致查询无限挂起。
验证方式
单独运行第二个子查询,查看返回行数:
select date_trunc('month', current_date) as minval, (date_trunc('day', current_date) + interval '1 day' - interval '1 second') as maxval from people;
结果行数会等于people表的总行数,所有行的内容完全相同。
解决办法
将第二个子查询修改为仅返回单行结果,以下几种方式均可:
- 添加聚合函数(利用聚合函数将多行合并为单行):
range_values as ( select date_trunc('month', current_date) as minval, max(date_trunc('day', current_date) + interval '1 day' - interval '1 second') as maxval from people )
- 使用单行数据源(避免从大表查询常量值):
range_values as ( select date_trunc('month', current_date) as minval, (date_trunc('day', current_date) + interval '1 day' - interval '1 second') as maxval from (select 1) as t )
- 添加
limit 1限制行数:
range_values as ( select date_trunc('month', current_date) as minval, (date_trunc('day', current_date) + interval '1 day' - interval '1 second') as maxval from people limit 1 )
补充说明
另外,end_of_day的范围比max(lasttime)大,若后续查询基于该范围过滤数据,可能会扫描更多索引或数据块,但最根本的性能瓶颈还是子查询返回的行数过多。
内容的提问来源于stack exchange,提问作者3ch01c
相关产品推荐
相关产品推荐

