文章目录
with
在数据库看来,把with语句看作是一个单独的操作,从而实现一次求值,多次使用
对于使用了RECURSIVE的而言会生成Recursive Union,通过Filter来判定结束
子查询提升
根据相关性,子查询可以分为相关子查询和非相关子查询,前者由于在子查询引用了外层表的列,所以外层表每获得一个元组就需要执行一次子查询。而后者由于没有相关性所以可以复用。
根据位置,子查询可以分为SubLink和SubQuery,前者查询得到的是一个标量,出现在Select/Where语句中,而后者得到的是一个范围表,出现在From语句
SubLink
这个结构体定义在primnodes.h
typedef enum SubLinkType
{
EXISTS_SUBLINK,//
ALL_SUBLINK,
ANY_SUBLINK,//
ROWCOMPARE_SUBLINK,
EXPR_SUBLINK,
MULTIEXPR_SUBLINK,
ARRAY_SUBLINK,
CTE_SUBLINK /* for SubPlans only */
} SubLinkType;
typedef struct SubLink
{
Expr xpr;
SubLinkType subLinkType; /* see above */
int subLinkId; /* ID (1..n); 0 if not MULTIEXPR */
Node *testexpr; /* outer-query test for ALL/ANY/ROWCOMPARE */
List *operName; /* originally specified operator name */
Node *subselect; /* subselect as Query* or raw parsetree */
int location; /* token location, or -1 if unknown */
} SubLink;
而SubLinkType的确定是由gram.y文件将SQL中的in/not in/any/some转换为ANY_SUBLINK,将EXISTS转换为EXISTS_SUBLINK。
优化规则
对于SubLink的处理遵循以下规则
- 关联
-
复杂连接(有函数)
不做处理,维持下面的格式Seq Scan on outer_table Filter: (SubPlan 1) SubPlan 1 -> Aggregate -> Seq Scan on inner_table Filter:(outer_table.o_key=i_key)
-
简单连接(无函数)
一般会提升,处理成下面执行计划Hash Semi Join Hash Cond:(outer_table.o_key=inner_table.i_key) -> Seq Scan on outer_table -> Hash ->Seq Scan on inner_table
- [NOT] IN
可以提升为[Anti-]Semi Join - ANY/SOME
可以提升为Semi Join - [NOT] EXISTS
可以提升为[Anti-]Semi Join
- [NOT] IN
-
- 非相关
- exists
不做处理,当做一次单独求解 - any
简单函数则提升
- exists
ANY_SUBLINK是天然带有相关性的,而EXISTS_SUBLINK可能相关也可能不相关。
优化代码
isA
在阅读具体的代码前我们先来认识一个定义在nodes.h里的宏:
#define nodeTag(nodeptr) (((const Node*)(nodeptr))->type)
#define IsA(nodeptr,_type_) (nodeTag(nodeptr) == T_##_type_)
其实这里的命名已经比较清楚了,nodeTag获取type属性,然后看是否是指定的type。不过这里用到了##这个语法,这个语法可以简单的理解为字符串拼接,顺便也介绍一下#这个宏语法,是将变量转换为字符串(和js,python中取全局变量的特性相反),这里有对这个语法更详尽的说明和举例
pull_up_sublinks
void pull_up_sublinks(PlannerInfo *root)
{
Node *jtnode;
Relids relids;
jtnode = pull_up_sublinks_jointree_recurse(root,(Node *) root->parse->jointree,&relids);
if (IsA(jtnode, FromExpr))
root->parse->jointree = (FromExpr *) jtnode;
else
root->parse->jointree = makeFromExpr(list_make1(jtnode), NULL);
}
pull_up_sublinks_jointree_recurse里面根据jtnode的不同type来做不同的处理,然后把值赋给relids