相关子查询与不相关子查询的优化(二)

 

四 PostgreSQL对相关子查询的优化


4.1 非相关子查询,多种优化情况


示例1:普通的非相关聚集子查询,被一次性地求解

test=# EXPLAIN SELECT * FROM t1 WHERE t1.a1>(SELECT MIN(t2.a2) FROM t2);
                                             QUERY PLAN
--------------------------------------------------------------------------------------------
 Bitmap Heap Scan on t1  (cost=65.19..150.31 rows=3210 width=8)
   Recheck Cond: (a1 > $1)
   InitPlan 2 (returns $1) //非相关子查询的结果作为输入,帮助在t1表上完成过滤操作
     ->  Result  (cost=0.05..0.06 rows=1 width=0)
           InitPlan 1 (returns $0)
             ->  Limit  (cost=0.00..0.05 rows=1 width=4) //以只读索引扫描完成MIN操作
                   ->  Index Only Scan using t2_a2_key on t2  (cost=0.00..467.94 rows=9582 width=4)
                         Index Cond: (a2 IS NOT NULL)
   ->  Bitmap Index Scan on t1_a1_key  (cost=0.00..64.33 rows=3210 width=0)
         Index Cond: (a1 > $1)
(10 行记录)

从查询执行计划看,非相关的、聚集子查询利用索引优化,并首先被执行且只被执行了一次,结果作为t1表的参数($1)参与了位图堆扫描(Bitmap Heap Scan),尽管子查询没有被消除,但是子查询被优化只被执行一次。


示例2: IN谓词表达的非相关子查询

IN非相关子查询,查询执行计划如下:

test=# EXPLAIN SELECT * FROM t1 WHERE t1.a1 IN (SELECT a2 FROM t2 WHERE t2.a2>10);
                            QUERY PLAN
------------------------------------------------------------------
 Hash Join  (cost=27.50..58.61 rows=990 width=8)
   Hash Cond: (t2.a2 = t1.a1)
   ->  Seq Scan on t2  (cost=0.00..17.50 rows=990 width=4)
         Filter: (a2 > 10)
   ->  Hash  (cost=15.00..15.00 rows=1000 width=8)
         ->  Seq Scan on t1  (cost=0.00..15.00 rows=1000 width=8)
(6 行记录)

从查询执行计划看,子查询不存在,表t1和t2直接做了哈希连接(Hash Join),把子查询上拉到父查询中用哈希连接实现IN非相关子查询的优化。


另外一个IN非相关子查询,查询执行计划如下:

test=# EXPLAIN SELECT * FROM t1 WHERE t1.a1 IN (SELECT a2 FROM t2 WHERE t2.a2=10);
                                  QUERY PLAN
-------------------------------------------------------------------------------
 Nested Loop Semi Join  (cost=0.00..16.55 rows=1 width=8)
   ->  Index Scan using t1_a1_key on t1  (cost=0.00..8.27 rows=1 width=8)
         Index Cond: (a1 = 10)
   ->  Index Only Scan using t2_a2_key on t2  (cost=0.00..8.27 rows=1 width=4)
         Index Cond: (a2 = 10)
(5 行记录)

从查询执行计划看,子查询不存在,表t1和t2直接做了嵌套循环半连接(Nested Loop Semi Join),把子查询上拉到父查询中用嵌套循环半连接实现IN操作。另外,由于子查询上拉,使得增加连接条件“a1=a2”,而原先的条件“a2=10”可以利用常量传递优化技术,使得“a1=a2=10”,所以查询执行计划中,两个索引扫描的条件分别为:a1 = 10、a2 = 10。



4.2 相关子查询,多种优化情况

再对比一个IN相关子查询,子查询没有别优化,查询执行计划如下:

test=# EXPLAIN SELECT * FROM t1 WHERE t1.a1 IN (SELECT a2 FROM t2 WHERE t1.a1=10);
                             QUERY PLAN
--------------------------------------------------------------------
 Seq Scan on t1  (cost=0.00..8770.00 rows=500 width=8)
   Filter: (SubPlan 1)
   SubPlan 1
     ->  Result  (cost=0.00..15.00 rows=1000 width=4)
           One-Time Filter: (t1.a1 = 10) //表t2的结果上执行的过滤和t2没有任何关系,多此一举
           ->  Seq Scan on t2  (cost=0.00..15.00 rows=1000 width=4)
(6 行记录)

从查询执行计划看,子查询存在(SubPlan 1),表t2上执行了顺序扫描后的结果作为了子查询(SubPlan 1)。 “a1=10”与t2表没有任何关联却被作为过滤器在子查询中使用,不合适。对于此种情况,PostgreSQL没有进行子查询的优化。另外,这个IN子查询如果把t2上拉,WHERE条件变为“a1=a2 AND t1.a1=10”,是完全可以支持子查询优化的。如果 t1.a1存在选择率较低的索引,则优化效果更好。


另外一个相关子查询的例子,子查询被优化:

test=# EXPLAIN SELECT * FROM t1 WHERE EXISTS (SELECT 1 FROM t2 WHERE t1.b1= t2.b2 AND t1.a1=10);
                                QUERY PLAN
--------------------------------------------------------------------------
 Nested Loop Semi Join  (cost=0.00..30.41 rows=1 width=8)
   Join Filter: (t1.b1 = t2.b2)
   ->  Index Scan using t1_a1_key on t1  (cost=0.00..8.27 rows=1 width=8)
         Index Cond: (a1 = 10)
   ->  Seq Scan on t2  (cost=0.00..15.00 rows=1000 width=4)
(5 行记录)

从查询执行计划看,子查询不存在,用嵌套循环半连接(Nested Loop Semi Join)实现EXISTS操作,连接条件上有索引被利用。


从以上几个例子看,PostgreSQL对子查询的优化,并没有明确的规律区分是相关或非相关子查询。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值