表的连接原理
表的连接方式:
1、循环嵌套连接(nested loop join)
2、哈西连接(hash join)
3、排序合并连接(merge join)
一、循环嵌套连接
A B 循环嵌套连接
方法:
1、扫描其中的一个小表,每读到一条记录,就根据这条记录(连接字段)的值到另外一个表中去查找。
2、另外一个表一般是大表,大表的连接字段上有索引,而且索引用的比较好。
3、扫描小表每读到一个连接字段的指,就到大表上走索引去扫描,以此循环小表,最后的结果合集就是要求查询的结果。
小表——又叫做驱动表,或者外表
大表——又叫做被驱动表,或者内表
CBO法则下,oracle会自动去选择驱动表,被驱动表的连接字段要求有索引而且条件要好,如果驱动表过大,也不适合这种方法。
SQL> conn plsql/plsql
Connected.
SQL> set autotrace trace exp
SQL> set linesize 1000
SQL> select h.hrc_descr,o.org_short_name from org_tab o,hrc_tab h where o.hrc_code=h.hrc_code order by 2;
Execution Plan
----------------------------------------------------------
Plan hash value: 566430324
-------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 9 | 306 | 8 (25)| 00:00:01 |
| 1 | SORT ORDER BY | | 9 | 306 | 8 (25)| 00:00:01 |
|* 2 | HASH JOIN | | 9 | 306 | 7 (15)| 00:00:01 |
| 3 | TABLE ACCESS FULL| ORG_TAB | 9 | 198 | 3 (0)| 00:00:01 |
| 4 | TABLE ACCESS FULL| HRC_TAB | 9 | 108 | 3 (0)| 00:00:01 |
-------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("O"."HRC_CODE"="H"."HRC_CODE")
强制循环嵌套连接
SQL> select /*+ use_nl(o h) */ h.hrc_descr,o.org_short_name from org_tab o,hrc_tab h where o.hrc_code=h.hrc_code order by 2;
Execution Plan
----------------------------------------------------------
Plan hash value: 3315346234
--------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 9 | 306 | 13 (8)| 00:00:01 |
| 1 | SORT ORDER BY | | 9 | 306 | 13 (8)| 00:00:01 |
| 2 | NESTED LOOPS | | 9 | 306 | 12 (0)| 00:00:01 |
| 3 | TABLE ACCESS FULL | ORG_TAB | 9 | 198 | 3 (0)| 00:00:01 |
| 4 | TABLE ACCESS BY INDEX ROWID| HRC_TAB | 1 | 12 | 1 (0)| 00:00:01 |
|* 5 | INDEX UNIQUE SCAN | PK_HRC_TAB | 1 | | 0 (0)| 00:00:01 |
--------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
5 - access("O"."HRC_CODE"="H"."HRC_CODE")
将HRC_TAB的数据加大
SQL> conn plsql/plsql
Connected.
SQL> desc hrc_tab
Name Null? Type
----------------------------------------- -------- ----------------------------
HRC_CODE NOT NULL NUMBER(4)
HRC_DESCR NOT NULL VARCHAR2(20)
SQL> alter table hrc_tab modify hrc_code number(10);
Table altered.
SQL> begin
2 for i in 1..100000 loop
3 insert into hrc_tab values(hrc_org_seq.nextval,'111');
4 end loop;
5 end;
6 /
PL/SQL procedure successfully completed.
SQL> commit;
Commit complete.
SQL> select h.hrc_descr,o.org_short_name from org_tab o,hrc_tab h where o.hrc_code=h.hrc_code order by 2;
Execution Plan
----------------------------------------------------------
Plan hash value: 566430324
-------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 9 | 306 | 8 (25)| 00:00:01 |
| 1 | SORT ORDER BY | | 9 | 306 | 8 (25)| 00:00:01 |
|* 2 | HASH JOIN | | 9 | 306 | 7 (15)| 00:00:01 | --为什么还是hash join
| 3 | TABLE ACCESS FULL| ORG_TAB | 9 | 198 | 3 (0)| 00:00:01 |
| 4 | TABLE ACCESS FULL| HRC_TAB | 9 | 108 | 3 (0)| 00:00:01 |
-------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("O"."HRC_CODE"="H"."HRC_CODE")
因为性能数据还是旧的。
分析:
SQL> exec dbms_stats.gather_table_stats(user,'hrc_tab',cascade=>true,estimate_percent=>100);
PL/SQL procedure successfully completed.
索引的结构没有问题,因为插入数据是一条条插入的。
SQL> analyze index PK_HRC_TAB validate structure;
Index analyzed.
SQL> set autotrace off
SQL> select btree_space,height,pct_used,(del_lf_rows/decode(lf_rows,0,1,lf_rows))*100||'%' as delete_pct from index_stats;
BTREE_SPACE HEIGHT PCT_USED DELETE_PCT
----------- ---------- ---------- -----------------------------------------
1503280 2 100 0%
SQL> set autotrace trace exp
SQL> exec dbms_stats.gather_table_stats(user,'org_tab',cascade=>true,estimate_percent=>100);
PL/SQL procedure successfully completed.
SQL> select h.hrc_descr,o.org_short_name from org_tab o,hrc_tab h where o.hrc_code=h.hrc_code order by 2;
Execution Plan
----------------------------------------------------------
Plan hash value: 3315346234
--------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 9 | 270 | 13 (8)| 00:00:01 |
| 1 | SORT ORDER BY | | 9 | 270 | 13 (8)| 00:00:01 |
| 2 | NESTED LOOPS | | 9 | 270 | 12 (0)| 00:00:01 |
| 3 | TABLE ACCESS FULL | ORG_TAB | 9 | 198 | 3 (0)| 00:00:01 |
| 4 | TABLE ACCESS BY INDEX ROWID| HRC_TAB | 1 | 8 | 1 (0)| 00:00:01 |
|* 5 | INDEX UNIQUE SCAN | PK_HRC_TAB | 1 | | 0 (0)| 00:00:01 |
--------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
5 - access("O"."HRC_CODE"="H"."HRC_CODE")
SQL> select /*+ use_hash(o h) */ h.hrc_descr,o.org_short_name from org_tab o,hrc_tab h where o.hrc_code=h.hrc_code order by 2;
Execution Plan
----------------------------------------------------------
Plan hash value: 566430324
-------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 9 | 270 | 64 (10)| 00:00:01 |
| 1 | SORT ORDER BY | | 9 | 270 | 64 (10)| 00:00:01 |
|* 2 | HASH JOIN | | 9 | 270 | 63 (8)| 00:00:01 |
| 3 | TABLE ACCESS FULL| ORG_TAB | 9 | 198 | 3 (0)| 00:00:01 |
| 4 | TABLE ACCESS FULL| HRC_TAB | 100K| 781K| 58 (6)| 00:00:01 |
-------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("O"."HRC_CODE"="H"."HRC_CODE")
此时,hash join代价比nested loop高。
二、哈西连接方式
hash算法——数据结构中讲述的一种算法,需要有一个哈西函数,用哈西函数将集合的值哈西化以后,集合的重复值将会变多,叫做簇值,根据簇值来分簇。
x和y关联
mod(x,10) --> 10个簇
mod(y,10) --> 10个簇
簇间匹配,再簇内匹配。
假设两个数据集合:
S={1,1,1,3,3,4,4,4,4,5,8,8,8,8,10}
B={0,0,1,1,2,2,3,8,9,9,9,10,11}
hash函数还是mod(x,10),将每个子分区用hash算法分出来,匹配后再进行原值的匹配:
分区 B0 B1 B2 B3 B4 B5 B6 B7 B8 B9
值 {0,0,10} {1,1,11} {2,2} {3} null null null null {8} {9,9,9}
--------------------------------------------------------
S0{10} Y
S1{1,1,1} Y
S2 NULL N
S3{3,3} Y
S4{4,4,4,4} N
S5{5} N
S6 NULL N
S7 NULL N
S8{8,8,8,8} Y
S9 NULL N
-------------------------------------------------------------------------------------------------
只有4个簇可以匹配
hash join的过程:
1、存放数据到hash区域——PGA区域
将小表(B)的数据,经过hash算法得到哈西簇(bucket),将原值放到相应的哈西簇中。
2、创建位图向量
将B表的连接字段集合读取到hash簇,oracle记录连接键的唯一值(原值),构建位图向量,这里的位图向量{0,1,2,3,8,9,10,11}
3、超出内存大小的部分(私有PGA区域耗完)会被移动到磁盘的临时表空间,任何需要写入到磁盘的操作都会生成IO的损耗,会影响性能, 所以最理想的情况是在PGA中完成HASH的操作。
4、对散列簇进行排序
为了能充分的利用内存,尽量存储更多的散列簇,oracle按照各个散列簇的大小在内存和磁盘中排序。
5、读取大表数据进行hash运算簇间匹配。如果一致,再进行原值的匹配返回结果。如果没有匹配上,就将S中没有匹配到的写到新的散列簇中。算法是一样的,也就是说在S中,S2,6,7,9被过滤了,同样把B中的位图向量进行过滤,这叫做位图向量的过滤。
hash区域:
SQL> show parameter hash
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
hash_area_size integer 131072
因为PGA是自动管理方式,所以这个大小不需要手工调整的。
使用hash join的场合:
1、确认小表存在,驱动表,因为小表是优先在hash区域构建hash簇的表
2、确认涉及到的表和表的连接键都被分析过了。
3、确定用hash join方式了,不需要在连接键上创建B树索引。
4、如果SQL语句中,有where的过滤条件,此时这个字段不是连接字段,还是需要考虑创建B树索引的。如果过滤条件是连接字段,也要考虑创建B树索引,可以通过索引的搜索将参与运算结果集变小。
5、如果连接键值数值分布不均匀还是要做直方图的。
6、PGA不足的话,要调整PGA
7、hash join适合大表和超大表的连接,返回大型的结果集合。
SQL> conn scott/scott
Connected.
SQL> create table tt as select * from all_objects;
Table created.
SQL> create table tt1 as select * from all_objects;
Table created.
SQL> exec dbms_stats.gather_table_stats(user,'tt',estimate_percent=>100);
PL/SQL procedure successfully completed.
SQL> exec dbms_stats.gather_table_stats(user,'tt1',estimate_percent=>100);
PL/SQL procedure successfully completed.
SQL> set autotrace trace exp
SQL> set linesize 1000
SQL> select * from tt,tt1 where tt.object_id=tt1.object_id;
Execution Plan
----------------------------------------------------------
Plan hash value: 2918007165
-----------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
-----------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 40909 | 7590K| | 686 (2)| 00:00:09 |
|* 1 | HASH JOIN | | 40909 | 7590K| 4280K| 686 (2)| 00:00:09 |
| 2 | TABLE ACCESS FULL| TT1 | 40910 | 3795K| | 134 (3)| 00:00:02 |
| 3 | TABLE ACCESS FULL| TT | 40909 | 3795K| | 134 (3)| 00:00:02 |
-----------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("TT"."OBJECT_ID"="TT1"."OBJECT_ID")
SQL> create index ind_tt on tt(object_id);
Index created.
SQL> create index ind_tt1 on tt1(object_id);
Index created.
SQL> exec dbms_stats.gather_table_stats(user,'tt1',estimate_percent=>100,cascade=>true);
PL/SQL procedure successfully completed.
SQL> exec dbms_stats.gather_table_stats(user,'tt',estimate_percent=>100,cascade=>true);
PL/SQL procedure successfully completed.
SQL> select * from tt,tt1 where tt.object_id=tt1.object_id;
Execution Plan
----------------------------------------------------------
Plan hash value: 2918007165
-----------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
-----------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 40909 | 7590K| | 686 (2)| 00:00:09 |
|* 1 | HASH JOIN | | 40909 | 7590K| 4280K| 686 (2)| 00:00:09 |
| 2 | TABLE ACCESS FULL| TT1 | 40910 | 3795K| | 134 (3)| 00:00:02 |
| 3 | TABLE ACCESS FULL| TT | 40909 | 3795K| | 134 (3)| 00:00:02 |
-----------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("TT"."OBJECT_ID"="TT1"."OBJECT_ID")
此时加一个非连接字段的过滤条件:
SQL> select * from tt,tt1 where tt.object_id=tt1.object_id and tt.object_type='TYPE';
Execution Plan
----------------------------------------------------------
Plan hash value: 3719458023
---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1705 | 316K| 268 (3)| 00:00:04 |
|* 1 | HASH JOIN | | 1705 | 316K| 268 (3)| 00:00:04 |
|* 2 | TABLE ACCESS FULL| TT | 1705 | 158K| 133 (2)| 00:00:02 |
| 3 | TABLE ACCESS FULL| TT1 | 40910 | 3795K| 134 (3)| 00:00:02 |
---------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("TT"."OBJECT_ID"="TT1"."OBJECT_ID")
2 - filter("TT"."OBJECT_TYPE"='TYPE')
SQL> create index ind_tt2 on tt(object_type);
Index created.
SQL> exec dbms_stats.gather_table_stats(user,'tt',estimate_percent=>100,cascade=>true);
PL/SQL procedure successfully completed.
SQL> select * from tt,tt1 where tt.object_id=tt1.object_id and tt.object_type='TYPE';
Execution Plan
----------------------------------------------------------
Plan hash value: 712899355
----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1249 | 231K| 179 (3)| 00:00:03 |
|* 1 | HASH JOIN | | 1249 | 231K| 179 (3)| 00:00:03 |
| 2 | TABLE ACCESS BY INDEX ROWID| TT | 1249 | 115K| 44 (0)| 00:00:01 |
|* 3 | INDEX RANGE SCAN | IND_TT2 | 1249 | | 4 (0)| 00:00:01 |
| 4 | TABLE ACCESS FULL | TT1 | 40910 | 3795K| 134 (3)| 00:00:02 |
----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("TT"."OBJECT_ID"="TT1"."OBJECT_ID")
3 - access("TT"."OBJECT_TYPE"='TYPE')
此时一个过滤条件落在了连接键上。
SQL> select * from tt,tt1 where tt.object_id=tt1.object_id and tt.object_id=259;
Execution Plan
----------------------------------------------------------
Plan hash value: 2300561305
-----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 190 | 4 (0)| 00:00:01 |
| 1 | MERGE JOIN CARTESIAN | | 1 | 190 | 4 (0)| 00:00:01 |
| 2 | TABLE ACCESS BY INDEX ROWID | TT | 1 | 95 | 2 (0)| 00:00:01 |
|* 3 | INDEX RANGE SCAN | IND_TT | 1 | | 1 (0)| 00:00:01 |
| 4 | BUFFER SORT | | 1 | 95 | 2 (0)| 00:00:01 |
| 5 | TABLE ACCESS BY INDEX ROWID| TT1 | 1 | 95 | 2 (0)| 00:00:01 |
|* 6 | INDEX RANGE SCAN | IND_TT1 | 1 | | 1 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("TT"."OBJECT_ID"=259)
6 - access("TT1"."OBJECT_ID"=259)
SQL> drop index ind_tt1;
Index dropped.
SQL> drop index ind_tt;
Index dropped.
SQL> exec dbms_stats.gather_table_stats(user,'tt',estimate_percent=>100,cascade=>true);
PL/SQL procedure successfully completed.
SQL> exec dbms_stats.gather_table_stats(user,'tt1',estimate_percent=>100,cascade=>true);
PL/SQL procedure successfully completed.
SQL> select * from tt,tt1 where tt.object_id=tt1.object_id and tt.object_id=259;
Execution Plan
----------------------------------------------------------
Plan hash value: 3719458023
---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 190 | 267 (2)| 00:00:04 |
|* 1 | HASH JOIN | | 1 | 190 | 267 (2)| 00:00:04 |
|* 2 | TABLE ACCESS FULL| TT | 1 | 95 | 133 (2)| 00:00:02 |
|* 3 | TABLE ACCESS FULL| TT1 | 1 | 95 | 133 (2)| 00:00:02 |
---------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("TT"."OBJECT_ID"="TT1"."OBJECT_ID")
2 - filter("TT"."OBJECT_ID"=259)
3 - filter("TT1"."OBJECT_ID"=259)
三、sort merge join排序合并连接
连接过程:
1、对连接的每个表作全表扫描或者索引全扫————代价很大
2、对其中一个表或者两个全表扫的结果排序(如果其中的一个表是全索引扫描的方式是不需要排序的,因为索引本身是排序的)——代价最大
3、连接的时候将两个结果集合合并 ——代价不大
SQL> select /*+ use_merge(o h) */ h.hrc_descr,o.org_short_name from org_tab o,hrc_tab h where o.hrc_code=h.hrc_code order by 2;
Execution Plan
----------------------------------------------------------
Plan hash value: 3620910538
--------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 9 | 270 | 394 (3)| 00:00:05 |
| 1 | SORT ORDER BY | | 9 | 270 | 394 (3)| 00:00:05 |
| 2 | MERGE JOIN | | 9 | 270 | 393 (2)| 00:00:05 |
| 3 | TABLE ACCESS BY INDEX ROWID| HRC_TAB | 100K| 781K| 389 (2)| 00:00:05 |
| 4 | INDEX FULL SCAN | PK_HRC_TAB | 100K| | 191 (2)| 00:00:03 |
|* 5 | SORT JOIN | | 9 | 198 | 4 (25)| 00:00:01 |
| 6 | TABLE ACCESS FULL | ORG_TAB | 9 | 198 | 3 (0)| 00:00:01 |
--------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
5 - access("O"."HRC_CODE"="H"."HRC_CODE")
filter("O"."HRC_CODE"="H"."HRC_CODE")
SQL> select h.hrc_descr,o.org_short_name from org_tab o,hrc_tab h where o.hrc_code=h.hrc_code order by 2;
Execution Plan
----------------------------------------------------------
Plan hash value: 3315346234
--------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 9 | 270 | 13 (8)| 00:00:01 |
| 1 | SORT ORDER BY | | 9 | 270 | 13 (8)| 00:00:01 |
| 2 | NESTED LOOPS | | 9 | 270 | 12 (0)| 00:00:01 |
| 3 | TABLE ACCESS FULL | ORG_TAB | 9 | 198 | 3 (0)| 00:00:01 |
| 4 | TABLE ACCESS BY INDEX ROWID| HRC_TAB | 1 | 8 | 1 (0)| 00:00:01 |
|* 5 | INDEX UNIQUE SCAN | PK_HRC_TAB | 1 | | 0 (0)| 00:00:01 |
--------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
5 - access("O"."HRC_CODE"="H"."HRC_CODE")
merge join使用场合:
1、9i开始,因为其排序和扫描的成本太高,大多被hash join取代。
2、如果源表(或者索引)在存储的时候已经排过序了,或者两个表的连接列都有索引且都是索引全扫的过程,此时性能会高于hash join、循环嵌套,出现的几率不大。
----------------------------------------------------------------
Hint——hint是oracle中的SQL语法,允许用户在SQL语句中插入提示(hint),影响SQL的执行方式。
误区:
1、/*+ index(table_name index_name) */认为这种做法一定能加快执行。
2、hint只要语法没错,一定会生效。
SQL> create table t as select rownum id,object_name from all_objects where rownum<100;
Table created.
SQL> create index t_ind on t(id);
Index created.
SQL> exec dbms_stats.gather_table_stats(user,'t',cascade=>true,estimate_percent=>100);
PL/SQL procedure successfully completed.
SQL> set autotrace trace exp
SQL> set linesize 1000
SQL> select * from t where id=100;
Execution Plan
----------------------------------------------------------
Plan hash value: 1376202287
-------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 18 | 2 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| T | 1 | 18 | 2 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | T_IND | 1 | | 1 (0)| 00:00:01 |
-------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("ID"=100)
SQL> select count(*) from t;
Execution Plan
----------------------------------------------------------
Plan hash value: 2966233522
-------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
-------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 3 (0)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | | |
| 2 | TABLE ACCESS FULL| T | 99 | 3 (0)| 00:00:01 |
-------------------------------------------------------------------
SQL> select /*+ index(t t_ind)*/ count(*) from t;
Execution Plan
----------------------------------------------------------
Plan hash value: 2966233522
-------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
-------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 3 (0)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | | |
| 2 | TABLE ACCESS FULL| T | 99 | 3 (0)| 00:00:01 | --为什么hint没有生效?
-------------------------------------------------------------------
被忽略的原因:count是聚合函数,聚合函数对于null值是不起作用的。如果强制走索引的话,索引中是不索引NULL值的,NULL肯定不会被计算进去的,需求是计算所有不为null的行,算出的结果中,如果ID为null,name也不为空,此时走索引,忽略掉这种情况。强制走索引回导致错误的结果。所以被忽略。
SQL> select count(comm),count(1) from emp;
COUNT(COMM) COUNT(1)
----------- ----------
4 14
SQL> select /*+ index(t t_ind)*/ count(id) from t; --需求就是不计算id为null的值
Execution Plan
----------------------------------------------------------
Plan hash value: 646498162
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 3 | 1 (0)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | 3 | | |
| 2 | INDEX FULL SCAN| T_IND | 99 | 297 | 1 (0)| 00:00:01 |
--------------------------------------------------------------------------
如果此时索引是unique索引,会不会被忽略?
unique索引含义——不索引null值,要求不为null的键值是不重复的。
SQL> drop index t_ind;
Index dropped.
SQL> create unique index t_ind on t(id);
Index created.
SQL> exec dbms_stats.gather_table_stats(user,'t',cascade=>true,estimate_percent=>100);
PL/SQL procedure successfully completed.
SQL> select /*+ index(t t_ind)*/ count(*) from t; --走索引计算的是不为null的值,但是需求是所有行只要有一个字段不为null就要计算的。
Execution Plan
----------------------------------------------------------
Plan hash value: 2966233522
-------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
-------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 3 (0)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | | |
| 2 | TABLE ACCESS FULL| T | 99 | 3 (0)| 00:00:01 | --忽略
-------------------------------------------------------------------
如果此时的索引是主键的话,会不会被忽略?
SQL> drop index t_ind;
Index dropped.
SQL> alter table t add constraint t_pk primary key(id);
Table altered.
SQL> exec dbms_stats.gather_table_stats(user,'t',cascade=>true,estimate_percent=>100);
PL/SQL procedure successfully completed.
SQL> select /*+ index(t t_pk)*/ count(*) from t;
Execution Plan
----------------------------------------------------------
Plan hash value: 56794325
-----------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
-----------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 1 (0)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | | |
| 2 | INDEX FULL SCAN| T_PK | 99 | 1 (0)| 00:00:01 |
-----------------------------------------------------------------
--没有被忽略,主键不能为NULL的,需求所有行只要有一个字段不为null就要计算的,主键不为NULL,此时计算主键的个数和计算行数是一样的。不会被忽略。
SQL> select count(*) from t;
Execution Plan
----------------------------------------------------------
Plan hash value: 56794325
-----------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
-----------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 1 (0)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | | |
| 2 | INDEX FULL SCAN| T_PK | 99 | 1 (0)| 00:00:01 |
-----------------------------------------------------------------
SQL> select /*+ full(t) */ count(*) from t; --全表扫的代价高
Execution Plan
----------------------------------------------------------
Plan hash value: 2966233522
-------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
-------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 3 (0)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | | |
| 2 | TABLE ACCESS FULL| T | 99 | 3 (0)| 00:00:01 |
-------------------------------------------------------------------
第二种情况:如果表使用了别名,提示中也要用别名,否则会被忽略。
SQL> select /*+ full(t) */ count(*) from t a;
Execution Plan
----------------------------------------------------------
Plan hash value: 56794325
-----------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
-----------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 1 (0)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | | |
| 2 | INDEX FULL SCAN| T_PK | 99 | 1 (0)| 00:00:01 |
-----------------------------------------------------------------
SQL> select /*+ full(a) */ count(*) from t a;
Execution Plan
----------------------------------------------------------
Plan hash value: 2966233522
-------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
-------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 3 (0)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | | |
| 2 | TABLE ACCESS FULL| T | 99 | 3 (0)| 00:00:01 |
-------------------------------------------------------------------
总结:
1、如果所强制走索引可能导致错误的查询结果的时候,hint会被忽略。
2、如果使用了别名,hint中没有用,也会被忽略。