Oracle 多表 连接 顺序 与 性能关系 测试
?
一. 創建表并insert 數據
create table ta (id number,name varchar2(10));
create table tb(id number,job varchar2(10));
?
begin
for i in 1..1000000 loop
begin
insert into ta values(i,'dave');
commit;
end;
end loop;
end;
?
begin
for i in 1..1000000 loop
begin
if i<10 then
insert into tb values(i,'boy');
elsif i<20 and i>10 then
insert into tb values(i,'girl');
commit;
end if;
end;
end loop;
end;
?
二.在沒有索引的情況關聯ta 和 tb 查詢
?
相關鏈接:
?????? Oracle Optimizer CBO RBO
?????? http://blog.csdn.net/tianlesoftware/archive/2010/08/19/5824886.aspx
?
?????? 多表連接的三種方式詳解 HASH JOIN MERGE JOIN NESTED LOOP
?????? http://blog.csdn.net/tianlesoftware/archive/2010/08/21/5826546.aspx
?
?????? Oracle Hint
?????? http://blog.csdn.net/tianlesoftware/archive/2010/08/23/5833020.aspx
?
2.1? optimizer選擇 CBO(10g 默認)
--ta 在前
select ta.id, ta.name,tb.job from ta,tb where ta.id=tb.id;
?
?
--tb 在前
select ta.id, ta.name,tb.job from tb,ta where ta.id=tb.id;
?
總結:????
?????? 兩條SQL 執行計劃是一樣的, ta和tb 的順序沒有影響。
?????? 因為ta和tb 的記錄相差較大,ta是100萬,tb 只有20條。 所以這里CBO 選擇使用Hash Join。
?????? CBO 選擇2個表中記錄較小的表tb,將其數據放入內存,對Join key構造hash 表,然后去掃描大表ta。 找出與散列表匹配的行。
?
2.2 對ta和tb 的ID 建b-tree 索引后在查看
?
--建索引
create index idx_ta_id on ta(id);
create index idx_tb_id on tb(id);
?
--tb 在前
select ta.id, ta.name,tb.job from tb,ta where ta.id=tb.id;
?
?
--ta 在前
select ta.id, ta.name,tb.job from ta,tb where ta.id=tb.id;
?
總結:
?????? 執行計劃還是一樣,不同的是表之間的關聯模式發生的改變,從Hash Join 變成了Nested Loops。
?????? Nested loop一般用在連接的表中有索引,并且索引選擇性較好的時候. 在我們這個示例中,CBO 選擇把返回結果集較小的表tb 作為outer table,CBO 下,默認把outer table 作為驅動表,然后用outer table 的每一行與inner table(我們這里是ta)進行Join,去匹配結果集。 由此可見,在tb(inner table) 有索引的情況,這種匹配就非常快。
?
?????? 這種情況下整個SQL的cost:
?????? cost = outer access cost + (inner access cost * outer cardinality)
?
?????? 從某種角度上看,可以把Nested loop 看成2層for 循環。
?
?
2.3 使用RBO 查看
?????? 在10g里,optimizer 默認已經使用CBO了,如果我們想使用RBO, 只能通過Hint 來實現。
??????
-- ta 在前
select /*+rule*/ta.id, ta.name,tb.job from ta,tb where ta.id=tb.id;
?
?
SYS@anqing2(rac2)> select /*+rule*/ta.id, ta.name,tb.job from ta,tb where ta.id<100 and? ta.id=tb.id;
Elapsed: 00:00:00.00
-- 注意這個SQL里,我們加了ta.id<100 的條件
Execution Plan
----------------------------------------------------------
Plan hash value: 3943212106
?
---------------------------------------------------
| Id? | Operation???????????????????? | Name????? |
---------------------------------------------------
|?? 0 | SELECT STATEMENT????????????? |?????????? |
|?? 1 |? TABLE ACCESS BY INDEX ROWID? | TB??????? |
|?? 2 |?? NESTED LOOPS??????????????? |?????????? |
|?? 3 |??? TABLE ACCESS BY INDEX ROWID| TA??????? |
|*? 4 |???? INDEX RANGE SCAN????????? | IDX_TA_ID |
|*? 5 |??? INDEX RANGE SCAN?????????? | IDX_TB_ID |
---------------------------------------------------
-- 當我們加上條件之后,就先走ta了,而不是tb。 因為先走ta,用ta的限制條件過濾掉一部分結果,這樣剩下的匹配工作就會減少。
Predicate Information (identified by operation id):
---------------------------------------------------
?
?? 4 - access("TA"."ID"<100)
?? 5 - access("TA"."ID"="TB"."ID")
?
Note
-----
?? - rule based optimizer used (consider using cbo)
?
--tb 在前
select /*+rule*/ta.id, ta.name,tb.job from tb,ta where ta.id=tb.id;
?
?
?
總結:
?????? 這2個就區別很明顯。 因為Oracle對sql的解析是從后向前的。 那么當先遇到tb時,那么會對tb進行全表掃描,然后用這個結果匹配ta。因為ta有索引,所以通過索引去匹配。
?????? 如果先遇到ta,那么就會對ta進行全表掃描。 因為2個表的差距很大,所以全表掃描的成本也就很大。
?????? 所以在RBO 下,大表在前,小表在后。這樣就會先遇到小表,后遇到大表。 如果有指定限定的where 條件,會先走限定條件的表。
?
?
2.4 drop 索引之后,在走RBO
?
drop index idx_ta_id;
drop index idx_tb_id;
?
?
--ta 在前
select /*+rule*/ta.id, ta.name,tb.job from ta,tb where ta.id=tb.id;
--tb 在前
select /*+rule*/ta.id, ta.name,tb.job from tb,ta where ta.id=tb.id;
?
總結:
?????? 這里選擇了Sort Merge Join 來連接2張表。Sort Merge join 用在沒有索引,并且數據已經排序的情況.
??????
?????? 我們表中的記錄是按照順序插敘的,所以符合這個條件。 SQL 的解析還是按照從后往前,所以這里ta和tb 在前先掃描的順序不一樣,不過都是全表掃描。 效率都不高。
?
?
2.5 ?引深一個問題:使用 字段名 代替 *
?????? * 能方便很多,但在ORACLE解析的過程中, 會通過查詢數據字典,會將’*’ 依次轉換成所有的列名,這就需要耗費更多的時間. 從而降低了效率。
?
SYS@anqing2(rac2)> set timing on
SYS@anqing2(rac2)> select * from ta where rownum=1;
??????? ID NAME
---------- ----------
???????? 1 dave
?
Elapsed: 00:00:00.03
SYS@anqing2(rac2)> desc ta
?Name???????????????? Null???? Type
?----------------------------------------- -------- ----------------------------
?ID?????????????????????????? NUMBER
?NAME?????? ????????????????VARCHAR2(10)
?
SYS@anqing2(rac2)> select id,name from ta where rownum=1;
?
??????? ID NAME
---------- ----------
???????? 1 dave
?
Elapsed: 00:00:00.02
?????????????????????????????????????
時間已經縮短。 但不明顯,用Toad 來查看一下:
?
寫全字段,執行時間是161 毫秒,用* 是561毫秒。 差距很明顯。
?
查看一下他們的執行計劃:
SYS@anqing2(rac2)> select * from ta where rownum=1;
Elapsed: 00:00:00.00
?
Execution Plan
----------------------------------------------------------
Plan hash value: 761731071
?
---------------------------------------------------------------------------
| id? | operation????????? | name | rows ?| bytes | cost (%cpu)| time???? |
---------------------------------------------------------------------------
|?? 0 | select statement?? |????? |???? 1 |??? 20 |???? 7? (72)| 00:00:01 |
|*? 1 |? count stopkey?? |????? |?????? |????? |???????? ??|???????? |
|?? 2 |?? table access full| ta?? |?? 890k|??? 16m|???? 7? (72)| 00:00:01 |
---------------------------------------------------------------------------
?
Predicate Information (identified by operation id):
---------------------------------------------------
?
?? 1 - filter(ROWNUM=1)
?
Note
-----
?? - dynamic sampling used for this statement
?
SYS@anqing2(rac2)> select id,name from ta where rownum=1;
Elapsed: 00:00:00.00
Execution Plan
----------------------------------------------------------
Plan hash value: 761731071
?
---------------------------------------------------------------------------
| id? | operation????????? | name | rows? | bytes | cost (%cpu)| time???? |
---------------------------------------------------------------------------
|?? 0 | select statement?? |????? |???? 1 |??? 20 |???? 7? (72)| 00:00:01 |
|*? 1 |? count stopkey?? |????? |?????? |????? |?????????? |??????? |
|?? 2 |?? table access full| ta?? |?? 890k|??? 16m|???? 7? (72)| 00:00:01 |
---------------------------------------------------------------------------
?
Predicate Information (identified by operation id):
---------------------------------------------------
?
?? 1 - filter(ROWNUM=1)
?
Note
-----
?? - dynamic sampling used for this statement
?
?
注意:
?????? 使用 * 和 寫全字段名,他們的執行計劃是一樣的,但是執行時間不一樣。
?
?
?
?
?
?
?
-------------------------------------------------------------------------------------------------------
Blog: http://blog.csdn.net/tianlesoftware
Email: dvd.dba@gmail.com
DBA1 群:62697716(滿);?? DBA2 群:62697977(滿)?? DBA3 群:62697850(滿)??
DBA 超級群:63306533(滿);? DBA4 群: 83829929? DBA5群: 142216823???
DBA6 群:158654907? 聊天 群:40132017?? 聊天2群:69087192
--加群需要在備注說明Oracle表空間和數據文件的關系,否則拒絕申請
轉載于:https://www.cnblogs.com/zlja/archive/2011/06/16/2449636.html
總結
以上是生活随笔為你收集整理的Oracle 多表 连接 顺序 与 性能关系 测试的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: Unity TexMeshPro中文字体
- 下一篇: MarkdownPad-显示Awesom
