驱动表、为什么不走索引 小实验
编程技术  /  houtizong 发布于 3年前   70
两种连接方式,有驱动表的概念(其实应该说是驱动行源更为准确)
-- hash join
-- nested-loop join
对于Nested-loop join
适合于小表(几千条,几万条记录)与大表做联接在联接列上有索引。分内表和外表(驱动表),靠近from子句的是内表。
从效率上讲,小表应该作外表,大表应该作内表,即大表查询时走索引。
COST= Access cost of A(驱动表) + (access cost of B * number of rows from A)
成本计算方法:
设小表100行,大表100000行。
两表均有索引:
如果小表在内,大表在外(驱动表)的话,则扫描次数为:
100000+100000*2 (其中2表示IO次数,一次索引,一次数据)
如果大表在内,小表在外(驱动表)的话,则扫描次数为:
100+100*2.
两表均无索引:
如果小表在内,大表在外的话,则扫描次数为:
100000+100*100000
如果大表在内,小表在外的话,则扫描次数为:
100+100000*100
注意:如果一个表有索引,一个表没有索引,ORACLE会将没有索引的表作驱动表。如果两个表都有索引
,则外表作驱动表。如果两个都没索引的话,则也是外表作驱动表。
基本的执行计划如下所示:
NESTED LOOPS
TABLE ACCESS (BY ROWID) OF our_outer_table
INDEX (..SCAN) OF outer_table_index(….)
TABLE ACCESS (BY ROWID) OF our_inner_table
INDEX (..SCAN) OF inner_table_index(….)
从效率上讲
from B, --内表,大表
A --外表,小表,驱动表
COST = Access cost of A + (access cost of B * number of rows from A)
---------------------------实验(条件要求,小表很小,大表很大,效果更具有可见性)、结论(仅代表个人意见,有不同意见的可以和我讨论)
大表tt1:
CREATE table tt1
as select * from dba_objects;
小表tt2:
CREATE table tt2
as select * from user_objects;
分析表:
select num_rows,blocks,table_name
from user_tables
where table_name in('TT1','TT2');
1、两表都无索引,大表在内,小表在外
select tt1.object_name,tt2.object_name
from tt1,
tt2
where tt1.object_id = tt2.object_id;
2、两表都无索引,小表在内,大表在外
select tt1.object_name,tt2.object_name
from tt2,
tt1
where tt1.object_id = tt2.object_id;
3、两表无索引,使用hint /*+use_nl(tt2)*/
select /*+use_nl(tt2)*/tt1.object_name,tt2.object_name
from tt2,
tt1
where tt1.object_id = tt2.object_id;
执行计划:
---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 84 | 4032 | 162 (2)| 00:00:02 |
|* 1 | HASH JOIN | | 84 | 4032 | 162 (2)| 00:00:02 |
| 2 | TABLE ACCESS FULL| TT2 | 86 | 1548 | 3 (0)| 00:00:01 |
| 3 | TABLE ACCESS FULL| TT1 | 49863 | 1460K| 158 (2)| 00:00:02 |
---------------------------------------------------------------------------
结论:
在没有索引的情况下:
-- hash join的cost更小,优化器自动选择hash join
-- 优化器自动选择小表做驱动表
-- hints也不能改变连接方式
create index tt1_ind on tt1(object_id);
4、大表有索引,小表无索引,大表在内,小表在外
select tt1.object_name,tt2.object_name
from tt2,
tt1
where tt1.object_id = tt2.object_id;
5、大表有索引,小表无索引,小表在内,大表在外
select tt1.object_name,tt2.object_name
from tt1,
tt2
where tt1.object_id = tt2.object_id;
---------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 673 | 32304 | 950 (1)| 00:00:12 |
| 1 | TABLE ACCESS BY INDEX ROWID| TT1 | 8 | 240 | 11 (0)| 00:00:01 |
| 2 | NESTED LOOPS | | 673 | 32304 | 950 (1)| 00:00:12 |
| 3 | TABLE ACCESS FULL | TT2 | 86 | 1548 | 3 (0)| 00:00:01 |
|* 4 | INDEX RANGE SCAN | TT1_IND | 8 | | 2 (0)| 00:00:01 |
---------------------------------------------------------------------------------------
结论:
大表有索引:
-- 在大表够大的情况下,优化器采用了NL的连接方式
-- 驱动表的选择与内外没有关系,自动选择小的行源做为驱动表
drop index tt1_ind
create index tt2_ind on tt2(object_id);
6、小表有索引,大表无索引,小表在内,大表在外
select tt1.object_name,tt2.object_name
from tt1,
tt2
where tt1.object_id = tt2.object_id;
7、小表有索引,大表无索引,小表在内,大表在外,加hints
select /*+use_nl(tt1)*/*tt1.object_name,tt2.object_name
from tt1,
tt2
where tt1.object_id = tt2.object_id;
---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 673 | 32304 | 1230 (2)| 00:00:15 |
|* 1 | HASH JOIN | | 673 | 32304 | 1230 (2)| 00:00:15 |
| 2 | TABLE ACCESS FULL| TT2 | 86 | 1548 | 3 (0)| 00:00:01 |
| 3 | TABLE ACCESS FULL| TT1 | 398K| 11M| 1220 (2)| 00:00:15 |
---------------------------------------------------------------------------
结论:
小表有索引:
-- 始终不能用大表做驱动表
create index tt1_ind on tt1(object_id);
8、大表小表都有索引
select tt1.object_name,tt2.object_name
from tt1,
tt2
where tt1.object_id = tt2.object_id;
---------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 673 | 32304 | 950 (1)| 00:00:12 |
| 1 | TABLE ACCESS BY INDEX ROWID| TT1 | 8 | 240 | 11 (0)| 00:00:01 |
| 2 | NESTED LOOPS | | 673 | 32304 | 950 (1)| 00:00:12 |
| 3 | TABLE ACCESS FULL | TT2 | 86 | 1548 | 3 (0)| 00:00:01 |
|* 4 | INDEX RANGE SCAN | TT1_IND | 8 | | 2 (0)| 00:00:01 |
---------------------------------------------------------------------------------------
结论:
小表、大表都有索引:
-- 符合原则,小且做驱动,大表走索引
9、两个表一样大,且都有索引
select tt1.object_name,tt2.object_name
from tt1,
tt2
where tt1.object_id = tt2.object_id;
-----------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
-----------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 47358 | 7307K| | 816 (1)| 00:00:10 |
|* 1 | HASH JOIN | | 47358 | 7307K| 4216K| 816 (1)| 00:00:10 |
| 2 | TABLE ACCESS FULL| TT2 | 47358 | 3653K| | 158 (2)| 00:00:02 |
| 3 | TABLE ACCESS FULL| TT1 | 52860 | 4078K| | 158 (2)| 00:00:02 |
-----------------------------------------------------------------------------------
select /*+index(tt2 tt2_ind)*/tt1.object_name,tt2.object_name
from tt1,
tt2
where tt1.object_id = tt2.object_id;
------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 47358 | 7307K| | 1898 (1)| 00:00:23 |
|* 1 | HASH JOIN | | 47358 | 7307K| 4216K| 1898 (1)| 00:00:23 |
| 2 | TABLE ACCESS BY INDEX ROWID| TT2 | 47358 | 3653K| | 1240 (1)| 00:00:15 |
| 3 | INDEX FULL SCAN | TT2_IND | 47358 | | | 124 (2)| 00:00:02 |
| 4 | TABLE ACCESS FULL | TT1 | 52860 | 4078K| | 158 (2)| 00:00:02 |
------------------------------------------------------------------------------------------------
结论:
--两个表一样大,无论用哪个来做驱动表,cost都会很高。
--优化器自动选择hash join
--如果要走索引,显然会让cost值增大(因为每一条记录,都要先用索引获得rowid,再回表。)
************************************************************************************************************************
set linesize 200 pagesize 1000
explain plan for
select /*+index(tt2 tt2_ind)*/tt1.object_name,tt2.object_name
from tt1,
tt2
where tt1.object_id = tt2.object_id;
select * from table(dbms_xplan.display());
请勿发布不友善或者负能量的内容。与人为善,比聪明更重要!
技术博客集 - 网站简介:
前后端技术:
后端基于Hyperf2.1框架开发,前端使用Bootstrap可视化布局系统生成
网站主要作用:
1.编程技术分享及讨论交流,内置聊天系统;
2.测试交流框架问题,比如:Hyperf、Laravel、TP、beego;
3.本站数据是基于大数据采集等爬虫技术为基础助力分享知识,如有侵权请发邮件到站长邮箱,站长会尽快处理;
4.站长邮箱:[email protected];
文章归档
文章标签
友情链接