查询优化-ANY类型子连接提升

瀚高数据库
目录
文档用途
详细信息

文档用途
已知查询树基本结构的基础上,在一定条件下对SQL中的子连接提升,

详细信息
子连接提升流程
SQL:

SELECT sname FROM student WHERE sno > ANY (SELECT sno FROM score);

图1.1是该SQL对应的查询树:

image.png

优化后为:

image.png

图1.2是优化后的查询树结构,对应的SQL如下:

SELECT sname FROM STUDENT SEMI JOIN (SELECT sno FROM SCORE) ANY subquery WHERE STUDENT. sno > ANY subquery.sno;

根据SQL和查询树前后对比发现:子查询提升涉及到的对查询树的改写主要包括:

1.RangeTblEntry多出一项用来描述提升后的子查询

2.主查询中的JoinTree也进行改写,新的JoinExpr来描述半连接。

下边给出提升过程对查询树重写的具体过程。

首先明确提升子连接的函数调用关系:

image.png

分析pull_up_sublinks_jointree_recurse函数:

对出现在jointree的三种节点类型分别处理,这是一个递归的过程。

第一种FromExpr:遍历fromlist列表,保存引用的表和遍历的节点,保存表为以后判断使用:因为子连接能提升的必要条件之一是TestExpr中必须包含父查询的列属性(要不然不构成连接关系)并且TestExpr中出现的表应该是上层查询中表的子集。

/* First, recurse to process children and collect their relids */



foreach(l, f->fromlist)

{

Node    *newchild;

Relids childrelids;

newchild = pull_up_sublinks_jointree_recurse(root,

lfirst(l),

&childrelids);

newfromlist = lappend(newfromlist, newchild);

frelids = bms_join(frelids, childrelids);

}

第二种:RangeTblRef,这种节点不会再有下层节点,作为递归出口,所以只需要记录表达式中变量引用的表

int varno = ((RangeTblRef *) jtnode)->rtindex;
*relids = bms_make_singleton(varno);

第三种:JoinExpr:表示显示的连接关系,比如某些情况下连接3个表,那么JoinExpr的下的左孩子或者右孩子仍然是JoinExpr,这种仍然需要递归处理。

j->larg = pull_up_sublinks_jointree_recurse(root, j->larg,

				&leftrelids);

j->rarg = pull_up_sublinks_jointree_recurse(root, j->rarg,

				&rightrelids);

针对本例,接下来要构造一个新的FromExpr来描述优化后SQL中的…FROM … WHERE 结构,这部分由

pull_up_sublinks_qual_recurse完成,最终的JoinTree将是这个新的FromExpr。

 

/* Build the replacement FromExpr; no quals yet */

newf = makeFromExpr(newfromlist, NULL);

/* Set up a link representing the rebuilt jointree */

jtlink = (Node *) newf;

/* Now process qual --- all children are available for use */

newf->quals = pull_up_sublinks_qual_recurse(root, f->quals,

						&jtlink, frelids,

							NULL, NULL);

分析pull_up_sublinks_qual_recurse():

整体上看这部分对应于优化后的查询树的JoinExpr节点的建立,包括JoinExpr结构体中的左操作数、右操作数、连接关系描述。

if ((j = convert_ANY_sublink_to_join(root, sublink,available_rels1)) != NULL)

{



j->larg = *jtlink1;

*jtlink1 = (Node *) j;





j->rarg = pull_up_sublinks_jointree_recurse(root,

j->rarg,

&child_rels);





j->quals = pull_up_sublinks_qual_recurse(root,

j->quals,

&j->larg,

available_rels1,

&j->rarg,

child_rels);



/* Return NULL representing constant TRUE */

return NULL;

			}

分析convert_ANY_sublink_to_join(),生成新的RangeTblEntry,添加到原查询树的rtable,计算rtindex

nsitem = addRangeTableEntryForSubquery(pstate,subselect,makeAlias("ANY_subquery", NIL),false,false);

rte = nsitem->p_rte;

parse->rtable = lappend(parse->rtable, rte);

rtindex = list_length(parse->rtable);

生成新的RangeTblRef

rtr = makeNode(RangeTblRef);

rtr->rtindex = rtindex;

对于新的JoinExpr需要生成新的quals,在这里要对testexpr进行替换,把Param替换成Var

quals = convert_testexpr(root, sublink->testexpr, subquery_vars);

替换规则:Param必然是子查询TargetList中的某一项,根据Param中的paramid获得该属性的列表下标:

if (param->paramkind == PARAM_SUBLINK)

{

if (param->paramid <= 0 ||

param->paramid > list_length(context->subst_nodes))

elog(ERROR, "unexpected PARAM_SUBLINK ID: %d", param->paramid);





return (Node *) copyObject(list_nth(context->subst_nodes,

param->paramid - 1));

}

最后填充:

/*

* And finally, build the JoinExpr node.

*/

result = makeNode(JoinExpr);

result->jointype = JOIN_SEMI;

result->isNatural = false;

result->larg = NULL; /* caller must fill this in */

result->rarg = (Node *) rtr;

result->usingClause = NIL;

result->join_using_alias = NULL;

result->quals = quals;

result->alias = NULL;

result->rtindex = 0;		/* we don't need an RTE for it */

经过上面步骤,最终生成了图1.2所示的结构。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值