QueryBlock(查询块)

在查询语句中,系统会自动根据SELECT在SQL中出现得顺序给每个QueryBlock生成一个 SEL$ + 序号 组成的查询块名称, 形如 SEL$1、SEL$2...

用户可以通过在Hint中添加这些查询块名称来指定Hint生效的查询块,也可以通过在Hint的表名后添加查询块名称来指定表。

查看QueryBlock

在开启显示QueryBlock参数(ENABLE_DISPLAY_QUERY_BLOCK)后,可以通过Explain来查看表所属的QueryBlock

从下面的计划可以看到表上存在QueryBlock信息 T1@SEL$1 QueryBlock @SEL$1T2@SEL$2 QueryBlock @SEL$2

T1@SEL$1 说明 T1 在SQL上属于第一个查询块(SEL$1),也就是第一个SELECT中

QueryBlock @SEL$1 说明 T1 在进行规则优化后,最后所属的查询块是第一个查询块(SEL$1)

T1@SEL$2 说明 T2 在SQL上属于第二个查询块(SEL$2),也就是第二个SELECT中

QueryBlock @SEL$2 说明 T2 在进行规则优化后,最后所属的查询块是第二个查询块(SEL$2)

-- 环境准备
create table t1(a int);
create table t2(a int);
create index idx1 on t1(a);
create index idx2 on t2(a);

-- 开启参数
set ENABLE_DISPLAY_QUERY_BLOCK = TRUE;

explain select * from t1,(select * from t2 where t2.a = 1 limit 1) where t1.a = 1;
                                                     QUERY PLAN
--------------------------------------------------------------------------------------------------------------------
 Nested Loop(Inner Join)  (cost=0.00..215.74 rows=10602 width=8)
   ->  Subquery Scan "__Anonymous_0"  (cost=0.00..2.70 rows=1 width=4)
         ->  Limit  (cost=0.00..2.70 rows=1 width=4)
               ->  Seq Scan on T2@SEL$2 QueryBlock @SEL$2  (cost=0.00..110.40 rows=1 width=4)
                     Scan Key: (A = 1)
   ->  Index Scan using IDX1(Fast Index Scan) on T1@SEL$1 QueryBlock @SEL$1  (cost=0.00..107.02 rows=10602 width=4)
         Index Key: (A = 1)
(7 rows)

通过下面的计划更容易理解什么是表最后所属的查询块

对于 T2@SEL$2 QueryBlock @SEL$1 来说,因为T2在SQL上属于第二个查询块,所以仍然是T2@SEL$2,但是我们看到QueryBlock后变成了 @SEL$1

说明 T2 表最后所属的查询块是第一个查询块,这是因为 T2 表进行了子查询的提升,将其提升到了第一个查询块中。

explain select * from t1,(select * from t2 where t2.a = 1) where t1.a = 1;
                                                     QUERY PLAN
--------------------------------------------------------------------------------------------------------------------
 Nested Loop(Inner Join)  (cost=0.00..8840.82 rows=434246 width=8)
   ->  Seq Scan on T2@SEL$2 QueryBlock @SEL$1  (cost=0.00..110.40 rows=41 width=4)
         Scan Key: (A = 1)
   ->  Index Scan using IDX1(Fast Index Scan) on T1@SEL$1 QueryBlock @SEL$1  (cost=0.00..107.02 rows=10602 width=4)
         Index Key: (A = 1)
(5 rows)

在Hint中使用QueryBlock

方式一:

通过 EXPLAIN OUTLINE 可以查看组成此计划所需的Hint,这些Hint已经携带了QueryBlock信息

如果想控制表的扫描方式或者连接方式,可以直接复制 Outline 中的Hint进行修改

注解

推荐使用这种方式来使用带QueryBlock的Hint

explain outline select * from t1,(select * from t2 where t2.a = 1) where t1.a = 1;
                                      QUERY PLAN
--------------------------------------------------------------------------------------
 Nested Loop(Inner Join)  (cost=0.00..261.57 rows=10602 width=8)
   ->  Index Scan using IDX2(Fast Index Scan) on T2  (cost=0.00..1.01 rows=1 width=4)
         Index Key: (A = 1)
   ->  Seq Scan on T1  (cost=0.00..154.54 rows=10602 width=4)
         Scan Key: (A = 1)


 Outline
 -------

     INDEX(@SEL$1 SYSDBA.T2@SEL$2 IDX2)
     FULL(@SEL$1 SYSDBA.T1@SEL$1)
     USE_NL(@SEL$1 (SYSDBA.T2@SEL$2),(SYSDBA.T1@SEL$1))
(13 rows)


-- 如果想控制T2走顺序扫描,则将 Outline 中的 INDEX(@SEL$1 SYSDBA.T2@SEL$2 IDX2) 改为 full(@SEL$1 SYSDBA.T2@SEL$2) 即可
SQL=> explain select /*+ full(@SEL$1 SYSDBA.T2@SEL$2) */ * from t1,(select * from t2 where t2.a = 1) where t1.a = 1;
                            QUERY PLAN
------------------------------------------------------------------
 Nested Loop(Inner Join)  (cost=110.44..254.41 rows=1678 width=8)
   ->  Seq Scan on T1  (cost=0.00..110.40 rows=41 width=4)
         Scan Key: (A = 1)
   ->  Materialize  (cost=110.44..110.85 rows=41 width=4)
         ->  Seq Scan on T2  (cost=0.00..110.40 rows=41 width=4)
               Scan Key: (A = 1)


 Hint Report
 -----------
 Total hint num: 1

 @SEL$1

         - ENABLE
             full(@sel$1 sysdba.t2@sel$2)
(16 rows)

方式二:

根据计划上显示的QueryBlock信息来构造Hint,Hint形式为 index(@SEL$1 T2@SEL$2)

Hint中最前面添加的@SEL$1指的是Hint作用的查询块为第一个查询块,表上指定的查询块T2@SEL$2指的是表在SQL上所属的查询块

简单来说,Hint的查询块应该添加计划上 QueryBlock 后的查询块,表上的查询块则直接使用计划上表的查询块即可

我们可以通过此 Hint 来控制下面计划中的 T2 表走索引扫描

explain select /*+ index(@SEL$1 T2@SEL$2)  */ * from t1,(select * from t2 where t2.a = 1) where t1.a = 1;
                                                     QUERY PLAN
--------------------------------------------------------------------------------------------------------------------
 Nested Loop(Inner Join)  (cost=0.00..8731.84 rows=434246 width=8)
   ->  Index Scan using IDX2(Fast Index Scan) on T2@SEL$2 QueryBlock @SEL$1  (cost=0.00..1.41 rows=41 width=4)
         Index Key: (A = 1)
   ->  Index Scan using IDX1(Fast Index Scan) on T1@SEL$1 QueryBlock @SEL$1  (cost=0.00..107.02 rows=10602 width=4)
         Index Key: (A = 1)


 Hint Report
 -----------
 Total hint num: 1

 @SEL$1

         - ENABLE
             index(@sel$1 t2@sel$2)
(15 rows)

注解

指定了查询块的Hint应该添加到第一个查询块中

SEL$1可以省略,所以可以将 index(@SEL$1 T2@SEL$2) 写成 index(T2@SEL$2)

如果想控制 T1 走顺序扫描,那么可以将Hint写成 full(@SEL$1 T1@SEL$1)full(T1)

explain select /*+ index(T2@SEL$2) full(T1) */ * from t1,(select * from t2 where t2.a = 1) where t1.a = 1;
                                                   QUERY PLAN
----------------------------------------------------------------------------------------------------------------
 Nested Loop(Inner Join)  (cost=165.14..8855.71 rows=434246 width=8)
   ->  Index Scan using IDX2(Fast Index Scan) on T2@SEL$2 QueryBlock @SEL$1  (cost=0.00..1.41 rows=41 width=4)
         Index Key: (A = 1)
   ->  Materialize  (cost=165.14..271.16 rows=10602 width=4)
         ->  Seq Scan on T1@SEL$1 QueryBlock @SEL$1  (cost=0.00..154.54 rows=10602 width=4)
               Scan Key: (A = 1)


 Hint Report
 -----------
 Total hint num: 2

 @SEL$1

         - ENABLE
             index(t2@sel$2)
             full(t1)
(17 rows)