PostgreSQL查询优化(三)逻辑重写

with

在数据库看来,把with语句看作是一个单独的操作,从而实现一次求值,多次使用
对于使用了RECURSIVE的而言会生成Recursive Union,通过Filter来判定结束

子查询提升

根据相关性,子查询可以分为相关子查询非相关子查询,前者由于在子查询引用了外层表的列,所以外层表每获得一个元组就需要执行一次子查询。而后者由于没有相关性所以可以复用。
根据位置,子查询可以分为SubLinkSubQuery,前者查询得到的是一个标量,出现在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
  • 非相关
    • exists
      不做处理,当做一次单独求解
    • any
      简单函数则提升

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

NULL
RangeTblRef
FromExpr
JoinExpr
JOIN_RIGHT
JOIN_INNER
JOIN_LEFT
JOIN_FULL
jtnode
jtnode
relids=null
return
bms_make_singleton
foreach jtnode->fromlist
pull_up_sublinks_jointree_recurse
lappend
frelids=bms_join
makeFromExpr
pull_up_sublinks_qual_recurse
*relids = frelids
j=palloc&memcpy
j.larg=pull_up_sublinks_jointree_recurse
j.rarg=pull_up_sublinks_jointree_recurse
j.jointype
pull_up_sublinks_qual_recurse
bms_union
*reliads=bms_join

UNION ALL

展开表继承

预处理表达式

处理HAVING子句

Group By键值消除

外连接消除

grouping_planner的说明

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值