子查询优化

上拉子连接

上拉子连接主要是把ANY和EXIST子句转换为半连接

void
pull_up_sublinks(PlannerInfo *root)
{
Node *jtnode; //子连接上拉生成的结果
Relids relids; /* Begin recursion through the jointree ,jointree代表From和join子句*/
jtnode = pull_up_sublinks_jointree_recurse(root,
(Node *) root->parse->jointree,
&relids); //把生成的结果jtnode,放入jointree表达式中
if (IsA(jtnode, FromExpr))
root->parse->jointree = (FromExpr *) jtnode;
else
root->parse->jointree = makeFromExpr(list_make1(jtnode), NULL);
}

pull_up_sublinks_jointree_recurse递归上拉子连接函数

static Node *
pull_up_sublinks_jointree_recurse(PlannerInfo *root, Node *jtnode,
Relids *relids)
{
if (jtnode == NULL)
else if (IsA(jtnode, RangeTblRef))//如果是范围表,直接合并到表示关系的relid中
//处理FromExpr
else if (IsA(jtnode, FromExpr))
{
/* 递归上拉From中所有子连接 */
foreach(l, f->fromlist)
{
newchild = pull_up_sublinks_jointree_recurse(root,
lfirst(l),
&childrelids);
//合并上拉的子连接到本层
newfromlist = lappend(newfromlist, newchild);
frelids = bms_join(frelids, childrelids);
}
//递归上拉子连接中的条件
newf->quals = pull_up_sublinks_qual_recurse(root, f->quals,
&jtlink, frelids,
NULL, NULL);
}
else if (IsA(jtnode, JoinExpr))
{
/* 上拉左右连接中的子连接 */
j->larg = pull_up_sublinks_jointree_recurse(root, j->larg,
&leftrelids);
j->rarg = pull_up_sublinks_jointree_recurse(root, j->rarg,
&rightrelids);
//递归上拉子连接中的条件
switch (j->jointype)
{
case JOIN_INNER:
j->quals = pull_up_sublinks_qual_recurse(root, j->quals,
&jtlink,
bms_union(leftrelids,
rightrelids),
NULL, NULL);
break;
case JOIN_LEFT:
j->quals = pull_up_sublinks_qual_recurse(root, j->quals,
&j->rarg,
rightrelids,
NULL, NULL);
break;
case JOIN_FULL:
/* can't do anything with full-join quals */
break;
case JOIN_RIGHT:
j->quals = pull_up_sublinks_qual_recurse(root, j->quals,
&j->larg,
leftrelids,
NULL, NULL);
break;
default:
elog(ERROR, "unrecognized join type: %d",
(int) j->jointype);
break;
} }
else
elog(ERROR, "unrecognized node type: %d",
(int) nodeTag(jtnode));
return jtnode;
}

pull_up_sublinks_qual_recurse上拉限制条件中的子连接

用于递归上拉限制条件中存在的子连接(ANY,EXISTS),经过convert_ANY_sublink_to_join转按ANY子连接,经过convert_EXISTS_sublink_to_join消除EXISTS递归消除

static Node *
//node为子连接节点,jtlink1和available_rels1都是上拉后的返回结果
pull_up_sublinks_qual_recurse(PlannerInfo *root, Node *node,
Node **jtlink1, Relids available_rels1,
Node **jtlink2, Relids available_rels2)
{
if (node == NULL)
return NULL;
if (IsA(node, SubLink))
{
/* 上拉ANY_SUBLINK类型子连接*/
if (sublink->subLinkType == ANY_SUBLINK)
{
if ((j = convert_ANY_sublink_to_join(root, sublink,
available_rels1)) != NULL)//上拉ANY子连接成功
{
//处理刚上拉的右关系
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;
}
//处理available_rels2对应项
if (available_rels2 != NULL &&
(j = convert_ANY_sublink_to_join(root, sublink,
available_rels2)) != NULL) }
//上拉EXIST_SUBLINKS对应项
else if (sublink->subLinkType == EXISTS_SUBLINK)
{
if ((j = convert_EXISTS_sublink_to_join(root, sublink, false,
available_rels1)) != NULL)
{
//基本逻辑和ANY块相同
}
if (available_rels2 != NULL &&
(j = convert_EXISTS_sublink_to_join(root, sublink, false,
available_rels2)) != NULL)
{
//基本逻辑和ANY块相同
}
}
/* Else return it unmodified */
return node;
}
//处理NOT_EXISTS
if (not_clause(node))
{
}
if (and_clause(node)) //处理AND每一个子项
{
/* Recurse into AND clause */
List *newclauses = NIL;
ListCell *l; foreach(l, ((BoolExpr *) node)->args)
{
Node *oldclause = (Node *) lfirst(l);
Node *newclause; newclause = pull_up_sublinks_qual_recurse(root,
oldclause,
jtlink1,
available_rels1,
jtlink2,
available_rels2);
if (newclause)
newclauses = lappend(newclauses, newclause);
}
/* We might have got back fewer clauses than we started with */
if (newclauses == NIL)
return NULL;
else if (list_length(newclauses) == 1)
return (Node *) linitial(newclauses);
else
return (Node *) make_andclause(newclauses);
}
/* Stop if not an AND */
return node;
}

pull_up_sublinks_qual_recurse和pull_up_sublinks_qual_recurse交替调用,穷尽所有ANY/EXISTS类型子连接

convert_ANY_sublink_to_join

ANY(类型包括ANY,NOT ANY,SOME, NOT SOME, IN, NOT IN)

JoinExpr *
convert_ANY_sublink_to_join(PlannerInfo *root, SubLink *sublink,
Relids available_rels)
{
JoinExpr *result;//子链接转换为连接关系 Assert(sublink->subLinkType == ANY_SUBLINK); //子连接右操作数:不能出现包含上层任何Var对象
if (contain_vars_of_level((Node *) subselect, 1))
return NULL; /*子连接左操作数
*a. 一定与上层出现的Var结构体表示的对象有相同,如果没有,可以直接求解,不用和上层关联
*b. 不能引用上层出现的关系
*c. 不能出现易失函数
*/
upper_varnos = pull_varnos(sublink->testexpr);//情况a
if (bms_is_empty(upper_varnos))
return NULL; /*
* However, it can't refer to anything outside available_rels.
*/
if (!bms_is_subset(upper_varnos, available_rels))//情况b
return NULL; /*
* The combining operators and left-hand expressions mustn't be volatile.
*/
if (contain_volatile_functions(sublink->testexpr))情况c
return NULL; /* Create a dummy ParseState for addRangeTableEntryForSubquery */
pstate = make_parsestate(NULL); /*上拉子链接到上层范围表中,作为未来连接的对象 */
rte = addRangeTableEntryForSubquery(pstate,
subselect,
makeAlias("ANY_subquery", NIL),
false,
false);
parse->rtable = lappend(parse->rtable, rte);
rtindex = list_length(parse->rtable); /*
* Form a RangeTblRef for the pulled-up sub-select.
*/
rtr = makeNode(RangeTblRef);
rtr->rtindex = rtindex; /*
* Build a list of Vars representing the subselect outputs.
*/
subquery_vars = generate_subquery_vars(root,
subselect->targetList,
rtindex); /* 连接条件*/
quals = convert_testexpr(root, sublink->testexpr, subquery_vars); /*
* And finally, build the JoinExpr node.
*/
result = makeNode(JoinExpr);
result->jointype = JOIN_SEMI;//半连接 return result;
}

有几种情况会上拉失败:

  1. 右操作数不能包含上层操作对象(包含就是关联子查询了)
  2. 左边操作符
    1. 与上层对象无关
    2. 不能引用上层的关系?
    3. 含有易失函数

convert_EXISTS_sublink_to_join

将EXISTS,NOT EXISTS上拉

JoinExpr *
convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink,
bool under_not, Relids available_rels)
{
JoinExpr *result;
Query *parse = root->parse; Assert(sublink->subLinkType == EXISTS_SUBLINK); /*对于右操作数
* a. 不支持带有WITH子句的格式
* b. 不支持集合操作或者带有CTE,聚合函数, HAVING,LIMIT等格式
* c. 不支持FROM或者WHERE子句为空
*/
if (subselect->cteList)//情况a
return NULL; if (!simplify_EXISTS_query(root, subselect))//情况b
return NULL; if (subselect->jointree->fromlist == NIL)//情况c
return NULL; whereClause = subselect->jointree->quals;//子查询条件保存
subselect->jointree->quals = NULL; //右操作树的子查询不能包含上层中出现的任何Var对象
if (contain_vars_of_level((Node *) subselect, 1))
return NULL;
//右操作数的where需要有山层的Var,这样才能够成功连接
if (!contain_vars_of_level(whereClause, 1))
return NULL;
//易失函数不能优化
if (contain_volatile_functions(whereClause))
return NULL; //上拉成功,到顶层范围表
rtoffset = list_length(parse->rtable);
OffsetVarNodes((Node *) subselect, rtoffset, 0);
OffsetVarNodes(whereClause, rtoffset, 0); /* 从技术上看来,上拉子查询就是把子查询中范围表拉到From子句,并把Where条件合并 */
parse->rtable = list_concat(parse->rtable, subselect->rtable); /*
* And finally, build the JoinExpr node.
*/
result = makeNode(JoinExpr);
result->jointype = under_not ? JOIN_ANTI : JOIN_SEMI;
return result;
}

不能上拉的条件:

  1. 右操作数:

    1. 不支持带有WITH子句的格式
    2. 不支持集合操作或者带有CTE(定义临时表的存在只是一个查询/withi子句),聚合函数, HAVING,LIMIT等格式
    3. 不支持FROM或者WHERE子句为空
    4. 不支持关联子查询,但是Where必须有上层Var
    5. 易失函数不能优化

上拉子查询

搜索From子句中的范围表,上拉其中子查询

void
pull_up_subqueries(PlannerInfo *root)
{
/* Recursion starts with no containing join nor appendrel */
root->parse->jointree = (FromExpr *)
pull_up_subqueries_recurse(root, (Node *) root->parse->jointree,
NULL, NULL, NULL, false);
}

真正执行上拉

static Node *
pull_up_subqueries_recurse(PlannerInfo *root, Node *jtnode,
JoinExpr *lowest_outer_join,
JoinExpr *lowest_nulling_outer_join,
AppendRelInfo *containing_appendrel,
bool deletion_ok)
{
Assert(jtnode != NULL);
if (IsA(jtnode, RangeTblRef))//一个范围表
{
if (rte->rtekind == RTE_SUBQUERY &&
is_simple_subquery(rte->subquery, rte,
lowest_outer_join, deletion_ok) &&//如果是简单子查询,上拉
(containing_appendrel == NULL ||
is_safe_append_member(rte->subquery)))
return pull_up_simple_subquery(root, jtnode, rte,
lowest_outer_join,
lowest_nulling_outer_join,
containing_appendrel,
deletion_ok); if (rte->rtekind == RTE_SUBQUERY &&
is_simple_union_all(rte->subquery))//如果是简单UNION操作,上拉
return pull_up_simple_union_all(root, jtnode, rte); //普通Value,上拉
if (rte->rtekind == RTE_VALUES &&
lowest_outer_join == NULL &&
containing_appendrel == NULL &&
is_simple_values(root, rte, deletion_ok))
return pull_up_simple_values(root, jtnode, rte); /* Otherwise, do nothing at this node. */
}
else if (IsA(jtnode, FromExpr))
{
FromExpr *f = (FromExpr *) jtnode; foreach(l, f->fromlist)
{
lfirst(l) = pull_up_subqueries_recurse(root, lfirst(l),
lowest_outer_join,
lowest_nulling_outer_join,
NULL,
sub_deletion_ok);
}
}
else if (IsA(jtnode, JoinExpr))
{
JoinExpr *j = (JoinExpr *) jtnode; /* Recurse, being careful to tell myself when inside outer join */
switch (j->jointype)
{
//对各种JOIN子句进行上拉
}
}
else
elog(ERROR, "unrecognized node type: %d",
(int) nodeTag(jtnode));
return jtnode;
}

可以上拉的:

  1. 简单子查询,Union ALL,普通Value
  2. 可以递归多个From子句,也可以递归上拉join左右操作数

优化顶层Union all

void
flatten_simple_union_all(PlannerInfo *root)
{
Query *parse = root->parse; /* 存在Union嵌套,就是WITH子句 */
if (root->hasRecursion)
return; /* UNION ALL的列不同*/
if (!is_simple_union_all_recurse((Node *) topop, parse, topop->colTypes))
return;
//构造AppendRefInfo节点优化UNION ALL
pull_up_union_leaf_queries((Node *) topop, root, leftmostRTI, parse, 0);
}

UNION和UNION ALL和OR

  1. UNION和UNION ALL都是使用AppendRefInfo替代,但是UNION会排序和去重
  2. OR里面会有OrFilter

PostgreSQL查询优化之子查询优化的更多相关文章

  1. postgresql子查询优化(提升子查询)

    问题背景 在开发项目过程中,客户要求使用gbase8s数据库(基于informix),简单的分页页面响应很慢.排查发现分页sql是先查询出数据在外面套一层后再取多少条,如果去掉嵌套的一层,直接获取则很 ...

  2. PostgreSQL代码分析,查询优化部分,canonicalize_qual

    这里把规范谓词表达式的部分就整理完了.阅读的顺序例如以下: 一.PostgreSQL代码分析,查询优化部分,canonicalize_qual 二.PostgreSQL代码分析,查询优化部分,pull ...

  3. 【MySQL】MySQL中针对大数据量常用技术_创建索引+缓存配置+分库分表+子查询优化(转载)

    原文地址:http://blog.csdn.net/zwan0518/article/details/11972853 目录(?)[-] 一查询优化 1创建索引 2缓存的配置 3slow_query_ ...

  4. 标量子查询优化(用group by 代替distinct)

    标量子查询优化 当使用另外一个SELECT 语句来产生结果中的一列的值的时候,这个查询必须只能返回一行一列的值.这种类型的子查询被称为标量子查询 在某些情况下可以进行优化以减少标量子查询的重复执行,但 ...

  5. PostgreSQL代码分析,查询优化部分,pull_ands()和pull_ors()

    PostgreSQL代码分析,查询优化部分. 这里把规范谓词表达式的部分就整理完了,阅读的顺序例如以下: 一.PostgreSQL代码分析,查询优化部分,canonicalize_qual 二.Pos ...

  6. MySQL实验 子查询优化双参数limit

    MySQL实验 子查询优化双参数limit 没想到双参数limit还有优化的余地,为了亲眼见到,今天来亲自实验一下.   实验准备 使用MySQL官方的大数据库employees进行实验,导入该示例库 ...

  7. Mysql单表访问方法,索引合并,多表连接原理,基于规则的优化,子查询优化

    参考书籍<mysql是怎样运行的> 非常推荐这本书,通俗易懂,但是没有讲mysql主从等内容 书中还讲解了本文没有提到的子查询优化内容, 本文只总结了常见的子查询是如何优化的 系列文章目录 ...

  8. mysql查询优化之三:查询优化器提示(hint)

    目录: <MySQL中的两种临时表>--强制使用临时表 SQL_BUFFER_RESULT <MySQL 多表关联更新及删除> <mysql查询优化之三:查询优化器提示( ...

  9. 由一条sql语句想到的子查询优化

    摘要:相信大家都使用过子查询,因为使用子查询可以一次性的完成很多逻辑上需要多个步骤才能完成的SQL操作,比较灵活,我也喜欢用,可最近因为一条包含子查询的select count(*)语句导致点开管理系 ...

随机推荐

  1. 前端优化之动画为什么要尽量用css3代替js

    导致JavaScript效率低的两大原因:操作DOM和使用页面动画.通常我们会通过频繁的操作 DOM的CSS来实现视觉上的动画效果,导致js效率低的两个因素都包括在内了在频繁的操作DOM和CSS时,浏 ...

  2. 七、Spring Boot Servlet 使用

    Web开发使用 Controller 基本上可以完成大部分需求,但是我们还可能会用到 Servlet.Filter.Listener.Interceptor 等等. 当使用spring-Boot时,嵌 ...

  3. Softmax多分类算法

    List<double[]> inputs_x = new List<double[]>(); inputs_x.Add(new double[] { 0.2, 0.3 }); ...

  4. NanUI文档 - 使用网页来设计整个窗口

    NanUI文档目录 NanUI简介 开始使用NanUI 打包并使用内嵌式的HTML/CSS/JS资源 使用网页来设计整个窗口 如何实现C#与Javascript相互掉用(待更新...) 如何处理Nan ...

  5. JDBC结果集rs.next()注意事项

    写在前面: 用JDBC从数据库中查询数据要用到结果集ResultSet,其中我们在获取结果的时候经常用到rs.next()方法来判断是否查询到了数据. 但是要特别注意,next()方法用一次,游标就往 ...

  6. Spring框架(三) JDBCTemplate,声明式事务,自动装载(注解)

    JDBCTemplate 按照标准正常项目的结构 结构图: model层 dao层 实现  dao:(DateBase Access Object) 数据访问对象,dao层只用来访问数据库和模型层 s ...

  7. AMD与CMD区别

    区别: 1. 对于依赖的模块,AMD 是提前执行,CMD 是延迟执行.不过 RequireJS 从 2.0 开始,也改成可以延迟执行(根据写法不同,处理方式不同).CMD 推崇 as lazy as ...

  8. iOS 数据加密方案

    iOS安全攻防(二十三):Objective-C代码混淆 提交用户的隐私数据 一定要使用POST请求提交用户的隐私数据GET请求的所有参数都直接暴露在URL中请求的URL一般会记录在服务器的访问日志中 ...

  9. ubuntu设置静态ip

    设置固定ip地址 >>>>>>>>>> ifconfig -a,  (注:p1p1为网卡名称) 配置静态ip vim /etc/networ ...

  10. c#访问oracle数据库

    想在c#中访问oracle数据库,毕竟是开发,想要轻量级访问oracle,客户机上无需安装oracle环境就能正常运行程序. 在网上找了相关资料,只需要引用一个dll即可实现. 访问代码(需引用dll ...