瀚高数据库
目录
文档用途
详细信息
文档用途
已知查询树基本结构的基础上,在一定条件下对SQL中的子连接提升,
详细信息
子连接提升流程
SQL:
SELECT sname FROM student WHERE sno > ANY (SELECT sno FROM score);
图1.1是该SQL对应的查询树:
优化后为:
图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来描述半连接。
下边给出提升过程对查询树重写的具体过程。
首先明确提升子连接的函数调用关系:
分析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所示的结构。