剩下的七个Scan节点
T_SubqueryScanState,
T_FunctionScanState,
T_ValuesScanState,
T_CteScanState,
T_WorkTableScanState,
T_ForeignScanState,
T_CustomScanState,
8.SubqueryScan 节点
SubqueryScan节点的作用是以另一个査询计划树(子计划)为扫描对象进行元组的扫描,其扫描过程最终被转换为子计划的执行。
Postgres子查询主要包含如下几个关键字: EXISTS, IN, NOT IN, ANY/SOME, ALL,详细介绍可以看看:子查询表达式
举例子:
postgres=# explain select id from test_new where exists (select id from test_dm);
QUERY PLAN
-------------------------------------------------------------------------
Result (cost=0.02..35.52 rows=2550 width=4)
One-Time Filter: $0
InitPlan 1 (returns $0)
-> Seq Scan on test_dm (cost=0.00..22346.00 rows=1000000 width=0)
-> Seq Scan on test_new (cost=0.00..35.50 rows=2550 width=4)
(5 行)
下面这个查询虽然也是子查询,但是在查询编译阶段被优化了(提升子连接,主要是把ANY和EXIST子句转换为半连接)
postgres=# explain select id from test_new where exists (select id from test_dm where id = test_new.id);
QUERY PLAN
-----------------------------------------------------------------------------
Hash Semi Join (cost=38753.00..42736.38 rows=1275 width=4)
Hash Cond: (test_new.id = test_dm.id)
-> Seq Scan on test_new (cost=0.00..35.50 rows=2550 width=4)
-> Hash (cost=22346.00..22346.00 rows=1000000 width=4)
-> Seq Scan on test_dm (cost=0.00..22346.00 rows=1000000 width=4)
(5 行)
有关内容,这里有一篇讲得很好:PostgreSQL查询优化之子查询优化
SubqueryScan节点在Scan节点之上扩展定义了子计划的根节点指针(subplan字段),而subrtable字段是査询编译器使用的结构,执行器运行时其值为空。
typedef struct SubqueryScan
{
Scan scan;
Plan *subplan;
} SubqueryScan;
显然,SubqueryScan节点的初始化过程(ExecInitSubqueryScan函数)会使用ExecInitNode处理SubqueryScan的subplan字段指向的子计划树,并将子计划的PlanStale树根节点指针賦值给SubqueryScanState 的subplan字段。
typedef struct SubqueryScanState
{
ScanState ss; /* its first field is NodeTag */
PlanState *subplan;
} SubqueryScanState;
我认为SubqueryScan节点其实就是一个壳子,为什么这么说呢?因为SubqueryScan节点的执行(ExecSubqueryScan 函数)通过将SubqueryNext 传递给 ExecScan函数处理来实现的。SubqueryNext实际则是调用ExecProcNode处理subplan来获得元组。也就是说,这里SubqueryScan是运行了一个独立的查询计划,然后获取它的结果,而不是自己去扫描表。因此recheck工作就在独立的查询计划里做过了,SubqueryScan节点不必再做。
所以我们可以看到:
static bool
SubqueryRecheck(SubqueryScanState *node, TupleTableSlot *slot)
{
/* nothing to check */
return true;
}
上面说了在执行时调用了ExecProcNode处理subplan,那么在清理过程中,很显然需要额外调用ExecEndNode来清理子计划。
9.FunctionScan 节点
例子:
postgres=# CREATE FUNCTION dup(int) RETURNS TABLE(f1 int, f2 text)
postgres-# AS $$ SELECT $1, CAST($1 AS text) || ' is text' $$
postgres-# LANGUAGE SQL;
CREATE FUNCTION
postgres=# explain SELECT * FROM dup(42);
QUERY PLAN
-------------------------------------------------------------
Function Scan on dup (cost=0.25..10.25 rows=1000 width=36)
(1 行)

本文详细解析了PostgreSQL中各种Scan节点的功能与实现原理,包括SubqueryScan、FunctionScan、ValuesScan、CteScan等,深入探讨了这些节点的数据结构、初始化过程、执行流程及清理操作。
最低0.47元/天 解锁文章
608

被折叠的 条评论
为什么被折叠?



