【数据库】查询优化之子连接优化

作者:chirpyli

来源:恒生LIGHT云社区

子查询的逻辑优化内容很多,我们先以 select * from t1 where id in (select a from t2);这条SQL语句为例分析PostgreSQL对子连接的逻辑优化过程。

在往下讲之前,先区别一下子连接与子查询,因为代码中分别会对子连接和子查询进行上拉操作。怎么区别呢?通常,以范围表的方式存在的称为子查询,以表达式的方式存在的称为子连接。实际上也可以通过子句所处的位置来区分子连接和子查询,出现在FROM关键字之后的子句时子查询语句,出现在WHERE等约束条件中的是子连接。

我们先通过执行计划进行观察分析,数据库通过 Hash Semi Join对子连接进行了逻辑优化。

--查看执行计划
postgres=# explain select * from t1 where id in (select a from t2);
                          QUERY PLAN               
---------------------------------------------------------------
 Hash Semi Join  (cost=1.09..2.21 rows=4 width=8)
   Hash Cond: (t1.id = t2.a)
   ->  Seq Scan on t1  (cost=0.00..1.06 rows=6 width=8)
   ->  Hash  (cost=1.04..1.04 rows=4 width=4)
         ->  Seq Scan on t2  (cost=0.00..1.04 rows=4 width=4)
解析层分析

我们先分析子连接在解析层的表示(以这条语句为例,其他子连接形式类似):

 /* A SubLink represents a subselect appearing in an expression, and in some
 * cases also the combining operator(s) just above it. */
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;

语法定义如下:

// 匹配 where id in (select a from t2);
where_clause:
			WHERE a_expr							{ $$ = $2; }
			| /*EMPTY*/								{ $$ = NULL; }
		;
// 匹配 id in (select a from t2);
a_expr:		c_expr									{ $$ = $1; }
			| a_expr IN_P in_expr
				{
					/* in_expr returns a SubLink or a list of a_exprs */
					if (IsA($3, SubLink))
					{
						/* generate foo = ANY (subquery) */
						SubLink *n = (SubLink *) $3;
						n->subLinkType = ANY_SUBLINK;
						n->subLinkId = 0;
						n->testexpr = $1;   // 具体到上面的SQL语句是id column
						n->operName = NIL;		/* show it's IN not = ANY */
						n->location = @2;
						$$ = (Node *)n;
					}
					else
					{
						/* generate scalar IN expression */
						$$ = (Node *) makeSimpleA_Expr(AEXPR_IN, "=", $1, $3, @2);
					}
				}
// 匹配 (select a from t2)
in_expr:	select_with_parens
				{
					SubLink *n = makeNode(SubLink);
					n->subselect = $1;
					/* other fields will be filled later */
					$$ = (Node *)n;
				}
			| '(' expr_list ')'						{ $$ = (Node *)$2; }
		;

我们知道 "x IN (select)", convert to "x = ANY (select)"这个转换是等价的。在解析阶段,会进行 INANY的处理,在调用过程如下:transformSelectStmt->transformWhereClause->transformExpr->transformSubLink。在 transformSublink中进行这个转换处理:

static Node *transformSubLink(ParseState *pstate, SubLink *sublink)
{
	  Node	   *result = (Node *) sublink;
	  Query	   *qtree;

    // 省略中间代码......
		/* If the source was "x IN (select)", convert to "x = ANY (select)".*/
		if (sublink->operName == NIL)
			sublink->operName = list_make1(makeString("="));

    // 省略中间代码......

	  return result;
}  

理解了子连接在解析层的表示后,我们重点分析一下逻辑优化阶段的处理。

逻辑优化

逻辑优化的基础是关系代数的等价变换,等价推理。有点类似于中学时数学中的简便运算思想。最容易理解的优化思想是:先选择后投影&

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值