nest loop內層迴圈會按照rowid排序嗎?
create table wxh_tbd1 as select * from dba_objects;
create table wxh_tbd2 as select * from dba_objects;
create index t_1 on wxh_tbd1(object_name);
create index t_2 on wxh_tbd1(object_id);
create index t_3 on wxh_tbd2(object_name);
insert into wxh_tbd1 select * from wxh_tbd1;
insert into wxh_tbd2 select * from wxh_tbd2;
commit;
select /*+ NO_NLJ_BATCHING(b) */
dbms_rowid.rowid_block_number(b.rowid) bl, a.object_id,b.object_type
from wxh_tbd1 a, wxh_tbd2 b
where a.object_name = b.object_name
and a.object_id > 899 and a.object_id<902;
/*+ NO_NLJ_BATCHING(b) */
2 dbms_rowid.rowid_block_number(b.rowid) bl, a.object_id,b.object_type
3 from wxh_tbd1 a, wxh_tbd2 b
4 where a.object_name = b.object_name
5 and a.object_id > 899 and a.object_id<902;
BL OBJECT_ID OBJECT_TYPE
---------- ---------- --------------------------------------
23667 900 TABLE
32537 900 TABLE
23667 900 TABLE
32537 900 TABLE
23667 901 INDEX
32537 901 INDEX
23667 901 INDEX
32537 901 INDEX
8 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 1274335627
------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 25 | 4200 | 13 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID | WXH_TBD2 | 6 | 534 | 3 (0)| 00:00:01 |
| 2 | NESTED LOOPS | | 25 | 4200 | 13 (0)| 00:00:01 |
| 3 | TABLE ACCESS BY INDEX ROWID| WXH_TBD1 | 4 | 316 | 3 (0)| 00:00:01 |
|* 4 | INDEX RANGE SCAN | T_2 | 4 | | 2 (0)| 00:00:01 |
|* 5 | INDEX RANGE SCAN | T_3 | 6 | | 1 (0)| 00:00:01 |
------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
4 - access("A"."OBJECT_ID">899 AND "A"."OBJECT_ID"<902)
5 - access("A"."OBJECT_NAME"="B"."OBJECT_NAME")
Note
-----
- dynamic sampling used for this statement
Statistics
----------------------------------------------------------
12 recursive calls
0 db block gets
166 consistent gets
0 physical reads
0 redo size
790 bytes sent via SQL*Net to client
524 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
2 sorts (memory)
0 sorts (disk)
8 rows processed
dbms_rowid.rowid_block_number(b.rowid) bl, a.object_id,b.object_type
2 3 from wxh_tbd1 a, wxh_tbd2 b
4 where a.object_name = b.object_name
5 and a.object_id > 899 and a.object_id<902;
BL OBJECT_ID OBJECT_TYPE
---------- ---------- --------------------------------------
23667 900 TABLE
32537 900 TABLE
23667 900 TABLE
32537 900 TABLE
23667 901 INDEX
32537 901 INDEX
23667 901 INDEX
32537 901 INDEX
8 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 748461386
------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 25 | 4200 | 13 (0)| 00:00:01 |
| 1 | NESTED LOOPS | | | | | |
| 2 | NESTED LOOPS | | 25 | 4200 | 13 (0)| 00:00:01 |
| 3 | TABLE ACCESS BY INDEX ROWID| WXH_TBD1 | 4 | 316 | 3 (0)| 00:00:01 |
|* 4 | INDEX RANGE SCAN | T_2 | 4 | | 2 (0)| 00:00:01 |
|* 5 | INDEX RANGE SCAN | T_3 | 6 | | 1 (0)| 00:00:01 |
| 6 | TABLE ACCESS BY INDEX ROWID | WXH_TBD2 | 6 | 534 | 3 (0)| 00:00:01 |
------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
4 - access("A"."OBJECT_ID">899 AND "A"."OBJECT_ID"<902)
5 - access("A"."OBJECT_NAME"="B"."OBJECT_NAME")
Note
-----
- dynamic sampling used for this statement
Statistics
----------------------------------------------------------
10 recursive calls
0 db block gets
166 consistent gets
0 physical reads
0 redo size
790 bytes sent via SQL*Net to client
524 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
2 sorts (memory)
0 sorts (disk)
8 rows processed
------------------------------------------------
| Id | Operation | Name |
------------------------------------------------
| 0 | SELECT STATEMENT | |
|* 1 | COUNT STOPKEY | |
| 2 | TABLE ACCESS BY INDEX ROWID | T2 | | 3 | NESTED LOOPS | |
| 4 | TABLE ACCESS BY INDEX ROWID| T1 |
|* 5 | INDEX RANGE SCAN | T1_N1 |
|* 6 | INDEX RANGE SCAN | T2_N1 |
------------------------------------------------
------------------------------------------------
| Id | Operation | Name |
------------------------------------------------
| 0 | SELECT STATEMENT | |
|* 1 | COUNT STOPKEY | |
| 2 | NESTED LOOPS | |
| 3 | NESTED LOOPS | |
| 4 | TABLE ACCESS BY INDEX ROWID| T1 |
|* 5 | INDEX RANGE SCAN | T1_N1 |
|* 6 | INDEX RANGE SCAN | T2_N1 |
| 7 | TABLE ACCESS BY INDEX ROWID | T2 | ------------------------------------------------
You might note that Oracle 10g and 11g have very different location of the table lookup operation on the table T2. This is a symptom of the nested loops join optimization of Oracle 11g. By using batching NLJ, you might see a decreased logical reads and improved performance.
as
select 1 as c1, mod(level, 4) as c2, level as c3, level as c4, rpad('x',1000) as dummy
from dual
connect by level <= 1000;
as
select 1001-level as c1, level as c2, rpad('x',1000) as dummy
from dual
connect by level <= 100;
create index t2_n1 on t2(c1);
exec dbms_stats.gather_table_stats(user, 't2');
select /*+ leading(t1 t2) use_nl(t2) index_asc(t1) index_asc(t2) */
rownum as rnum,
t2.c1,
t1.c4,
t2.c2
from t1, t2
where
t1.c3 = t2.c1
and t1.c1 = 1
and t1.c2 = 0
and rownum <= 20
;
alter system flush buffer_cache;
select /*+ leading(t1 t2) use_nl(t2) index_asc(t1) index_asc(t2) */
rownum as rnum,
t2.c1,
t1.c4,
t2.c2
from t1, t2
where
t1.c3 = t2.c1
and t1.c1 = 1
and t1.c2 = 0
and rownum <= 20
) where rnum >= 15
;
select * from (
select /*+ leading(t1 t2) use_nl(t2) index_asc(t1) index_asc(t2) */
rownum as rnum,
t2.c1,
t1.c4,
t2.c2
from t1, t2
where
t1.c3 = t2.c1
and t1.c1 = 1
and t1.c2 = 0
and rownum <= 20
) where rnum >= 15
;
alter session set "_nlj_batching_misses_enabled" = 0;
alter system flush buffer_cache;
select /*+ leading(t1 t2) use_nl(t2) index_asc(t1) index_asc(t2) */
rownum as rnum,
t2.c1,
t1.c4,
t2.c2
from t1, t2
where
t1.c3 = t2.c1
and t1.c1 = 1
and t1.c2 = 0
and rownum <= 20
) where rnum >= 15
;
select * from (
select /*+ leading(t1 t2) use_nl(t2) index_asc(t1) index_asc(t2) */
rownum as rnum,
t2.c1,
t1.c4,
t2.c2
from t1, t2
where
t1.c3 = t2.c1
and t1.c1 = 1
and t1.c2 = 0
and rownum <= 20
) where rnum >= 15
;
To save your time, let me show you the result of the above test case with some comments appended.
-- when the query reads from the disk
RNUM C1 C4 C2
---------- ---------- ---------- ----------
15 960 960 41
16 964 964 37
17 980 980 21 18 968 968 33
19 972 972 29
20 976 976 25
RNUM C1 C4 C2
---------- ---------- ---------- ----------
15 960 960 41
16 964 964 37
17 968 968 33
18 972 972 29
19 976 976 25
20 980 980 21
-- when the query reads from the disk
RNUM C1 C4 C2
---------- ---------- ---------- ----------
15 960 960 41
16 964 964 37
17 968 968 33
18 972 972 29
19 976 976 25
20 980 980 21
RNUM C1 C4 C2
---------- ---------- ---------- ----------
15 960 960 41
16 964 964 37
17 968 968 33
18 972 972 29
19 976 976 25
20 980 980 21
The simple explanation is :
“The new optimization code of the nested loops join does not guarantee that the rows would be returned as an order of the outer table, especially when it reads the data from the disk.”
Set parameter – _nlj_batching_enabled = 0;
Append hint – NO_NLJ_BATCHING(t2)
It seems that I need to do further research on the batching NLJ – it’s exact mechanism and the pitfalls.
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/22034023/viewspace-703358/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- thinkphp where in order 按照順序in的迴圈排序PHP排序
- 事件迴圈(event loop)事件OOP
- JS事件迴圈Event LoopJS事件OOP
- Javascript 事件迴圈event loopJavaScript事件OOP
- JavaScript事件迴圈(Event Loop)JavaScript事件OOP
- JS 事件迴圈(Event Loop)JS事件OOP
- 事件迴圈(Event Loop)淺析事件OOP
- pl/sql for loop迴圈的使用SQLOOP
- lisp 裡的迴圈loop macroLispOOPMac
- 0165-loop 迴圈OOP
- Event Loop事件迴圈,看完你總會有點收穫!OOP事件
- 瀏覽器事件迴圈Event Loop瀏覽器事件OOP
- Flutter Flame 教程2 -- Game Loop遊戲迴圈FlutterGAMOOP遊戲
- JavaScript的事件迴圈(Event loop)(附圖)JavaScript事件OOP
- 淺談js的事件迴圈(Event Loop)JS事件OOP
- nodejs中的事件迴圈 - Event LoopNodeJS事件OOP
- 談談 Event Loop(事件迴圈)機制OOP事件
- for 迴圈境實現遊標LOOP提取OOP
- mysql 的procedure 中 loop迴圈的用法。MySqlOOP
- 二維陣列按照其內層陣列的某個鍵值排序陣列排序
- 一文梳理JavaScript 事件迴圈(Event Loop)JavaScript事件OOP
- 瀏覽器事件迴圈機制(event loop)瀏覽器事件OOP
- JavaScript深入之事件迴圈機制(event loop)JavaScript事件OOP
- 事件迴圈Event loop到底是什麼事件OOP
- 學習筆記之事件迴圈-Event loop筆記事件OOP
- [SceneKit專題]3-Render-Loop渲染迴圈OOP
- 按照NSArray內部的某個物件排序物件排序
- loop迴圈 長時間沒有返回結果OOP
- MYSQL儲存過程-練習4 loop迴圈MySql儲存過程OOP
- connect by層內排序排序
- 效能優化篇 - js事件迴圈機制(event loop)優化JS事件OOP
- 為什麼要有事件迴圈機制(Event Loop)事件OOP
- 瀏覽器和Node不同的事件迴圈(Event Loop)瀏覽器事件OOP
- NEST LOOP改為HASH JOIN 效能提高6倍OOP
- JAVA多層迴圈裡面怎麼一個break直接跳出到最外層迴圈位置Java
- 筆試題——JavaScript事件迴圈機制(event loop、macrotask、microtask)筆試JavaScript事件OOPMac
- 按照價格排序!排序
- 笛卡爾積和NEST LOOP產生的影響OOP