为何PostgreSQL该查询仅返回单条结果?
测试场景说明
- 测试表
tenk1共10000行,unique1和unique2列各自是0-9999的唯一值(每行的unique1和unique2唯一,不同行间两列值可能重复)
1. 初始查询仅返回一行的原因
初始查询语句:
select (select max((select i.unique2 from tenk1 i where i.unique1 = o.unique1))) from tenk1 o;
这里的问题在于最内层子查询被当作标量子查询处理,外层的max()被优化器误判为不依赖外部表o的上下文。优化器认为整个max()子查询可以一次性计算出全局最大值,因此只会执行一次,返回全局最大的9999,最终整个查询只输出一行,而非遍历tenk1 o的10000行。
2. 显式关联改写后报错的原因
改写后的查询:
select (select max((select i.unique2 from tenk1 i join tenk1 o on i.unique1 = o.unique1)))
这里的显式join让内层子查询直接生成10000行结果(i.unique1与o.unique1一一匹配)。虽然max()能处理多行,但整个外层子查询作为select的表达式,没有关联任何外部表,PostgreSQL会直接执行内层join生成全表结果,此时内层子查询返回10000行,触发规则报错:
ERROR: more than one row returned by a subquery used as an expression
这完全符合PostgreSQL的基础规则:作为表达式的子查询必须返回单行。
3. 移除max()后能返回10000行的核心原因
查询语句:
select (select (select i.unique2 from tenk1 i where i.unique1 = o.unique1)) from tenk1 o;
关键在于内层子查询关联了外部表o的o.unique1列,PostgreSQL将其判定为关联子查询(correlated subquery),会为外部表o的每一行单独执行一次内层子查询。
由于tenk1的unique1是唯一值,针对外部表的每一行,内层子查询只会返回单行结果(每个unique1对应唯一的i行),因此作为表达式使用时不会触发多行错误,最终遍历10000行输出对应结果。
4. 移除外部表o后再次报错的原因
查询语句:
select (select (select i.unique2 from tenk1 i))
此时内层子查询没有关联任何外部上下文,会直接返回tenk1的全部10000行unique2值,违反了“作为表达式的子查询必须返回单行”的规则,因此触发报错,这是符合规则的正常行为。
总结
PostgreSQL对关联外部表的子查询会做特殊处理:当子查询依赖外部表的列时,会被视为每行独立执行的关联子查询,只要每一次执行返回单行,就不会触发多行错误;而无关联的子查询会被一次性执行,若返回多行则直接报错。
初始查询的异常是优化器误判了max()子查询的依赖关系,导致只执行一次返回全局最大值,而非逐行执行。
内容的提问来源于stack exchange,提问作者Mason Wheeler

