九月 2, 2025
摘要:在本教程中,你将了解在执行计划中可能会看到的各种扫描 “节点” 或操作。
目录
Function Scan
示例:
explain analyze select * from generate_series(1,10) i;
QUERY PLAN
---------------------------------------------------------------------------------------------------------------------
Function Scan on generate_series i (cost=0.00..10.00 rows=1000 width=4) (actual time=0.012..0.013 rows=10 loops=1)
Total runtime: 0.034 ms
(2 rows)
通常情况下,函数扫描非常简单,本无需额外说明,但出于完整性的考虑,因此这里还是对其进行一下简要介绍。
函数扫描(Function Scan)是一个极为简单的执行节点:它的作用是执行一个返回结果集(recordset)的函数。也就是说,它不会执行lower()
这类普通函数,只会执行那些(至少潜在地)能返回多行或多列数据的函数。当函数返回数据行后,这些数据会传递给执行计划树中函数扫描节点的上层节点;若函数扫描本身就是顶层节点,则数据会直接返回给客户端。
函数扫描可能包含的唯一额外逻辑,是对返回的数据行进行过滤,如下例所示:
explain analyze select * from generate_series(1,10) i where i < 3;
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------
Function Scan on generate_series i (cost=0.00..12.50 rows=333 width=4) (actual time=0.012..0.014 rows=2 loops=1)
Filter: (i < 3)
Rows Removed by Filter: 8
Total runtime: 0.030 ms
(4 rows)
Values Scan
与之前介绍的 Result 节点类似,值扫描(Values Scan)用于返回查询中直接定义的简单数据,但不同的是,它依托VALUES()
语法,能够返回完整的结果集。
可能你还不知道,通过VALUES
语法,无需关联任何表,就能直接查询包含多行多列的数据,示例如下:
select * from ( values (1, 'redrock'), (2, 'postgres'), (3, 'postgresql') ) as t (a,b);
a | b
---+------------
1 | redrock
2 | postgres
3 | postgresql
(3 rows)
该查询的执行计划如下:
QUERY PLAN
--------------------------------------------------------------
Values Scan on "*VALUES*" (cost=0.00..0.04 rows=3 width=36)
(1 row)
值扫描最常见的用途是配合INSERT
语句使用,但也有其他应用场景,例如自定义排序。
CTE Scan
CTE 扫描与前文中提到的 “物化”(Materialize)操作类似:它会先执行查询中的某一部分(即 CTE 部分),并将执行结果存储起来,以便查询的其他部分(一个或多个)重复使用。
示例:
explain analyze with x as (select relname, relkind from pg_class)
select relkind, count(*), (select count(*) from x)
from x
group by relkind;
1. QUERY PLAN
2.-----------------------------------------------------------------------------------------------------------------
3. HashAggregate (cost=24.80..26.80 rows=200 width=1) (actual time=0.466..0.468 rows=6 loops=1)
4. CTE x
5. -> Seq Scan on pg_class (cost=0.00..10.92 rows=292 width=65) (actual time=0.009..0.127 rows=295 loops=1)
6. InitPlan 2 (returns $1)
7. -> Aggregate (cost=6.57..6.58 rows=1 width=0) (actual time=0.085..0.085 rows=1 loops=1)
8. -> CTE Scan on x x_1 (cost=0.00..5.84 rows=292 width=0) (actual time=0.000..0.055 rows=295 loops=1)
9. -> CTE Scan on x (cost=0.00..5.84 rows=292 width=1) (actual time=0.012..0.277 rows=295 loops=1)
10. Total runtime: 0.524 ms
11.(8 rows)
请注意,第 5 行显示pg_class
表仅被扫描了一次,但扫描结果被存储在 CTE “x” 中,随后被两次读取:一次是在聚合操作内部(第 8 行),另一次是在哈希聚合操作中(第 9 行)。
CTE 扫描与物化操作的区别是什么?简单来说,核心区别在于 CTE 是用户显式定义的,而物化操作是 PostgreSQL 优化器在判断 “使用物化能提升效率” 时,自动选择的辅助操作。
一个非常重要的特点是:CTE 会严格按照用户定义的逻辑执行。因此,在某些情况下,可借助 CTE 规避查询优化器原本可能做出的不够理想的优化决策。