Lately I have been trying to help my non-DBA colleague to understand optimizer operations. Now that I am putting this effort , why not add it to my blog. I will be writing a series of blogs discussing optimizer operations. Let me start the honors with NESTED LOOPS.
NESTED LOOPS operations in the execution plan instead of one because of this implementation change. Two nested loop are required for
The above implementation change can be controlled with init.ora parameter optimizer_features_enable or with a new hint “no_nlj_batching”
For example, Let us run the a query with optimizer_features_enable set to Oracle version 11 and then again rerun the same query with Oracle version 10.
Case-1 : optimizer_features_enable=11.2.0.2
In this case you will see 2 nested loops in the execution plan and new operation NLJ_BATCHING in the outline_data.
SQL> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(format => 'ADVANCED'));
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID apzagcxrnwka8, child number 0
-------------------------------------
SELECT A.OBJECT_NAME , A. LAST_DDL_TIME , B.CREATED FROM A, B WHERE
b.OBJECT_ID=a.OBJECT_ID AND a.OBJECT_ID BETWEEN 1 AND 30
Plan hash value: 1521750179
----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 5 (100)| |
| 1 | NESTED LOOPS | | | | | |
| 2 | NESTED LOOPS | | 3 | 135 | 5 (0)| 00:00:01 |
| 3 | TABLE ACCESS BY INDEX ROWID| A | 16 | 512 | 3 (0)| 00:00:01 |
|* 4 | INDEX RANGE SCAN | IDX_A | 16 | | 2 (0)| 00:00:01 |
|* 5 | INDEX UNIQUE SCAN | INDX_B | 1 | | 0 (0)| |
| 6 | TABLE ACCESS BY INDEX ROWID | B | 1 | 13 | 1 (0)| 00:00:01 |
----------------------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
1 - SEL$1
3 - SEL$1 / A@SEL$1
4 - SEL$1 / A@SEL$1
5 - SEL$1 / B@SEL$1
6 - SEL$1 / B@SEL$1
Outline Data
-------------
/*+
BEGIN_OUTLINE_DATA
IGNORE_OPTIM_EMBEDDED_HINTS
OPTIMIZER_FEATURES_ENABLE('11.2.0.2')
DB_VERSION('11.2.0.2')
OPT_PARAM('query_rewrite_enabled' 'false')
FIRST_ROWS(10)
OUTLINE_LEAF(@"SEL$1")
INDEX_RS_ASC(@"SEL$1" "A"@"SEL$1" ("A"."OBJECT_ID"))
INDEX(@"SEL$1" "B"@"SEL$1" ("B"."OBJECT_ID"))
LEADING(@"SEL$1" "A"@"SEL$1" "B"@"SEL$1")
USE_NL(@"SEL$1" "B"@"SEL$1")
NLJ_BATCHING(@"SEL$1" "B"@"SEL$1") END_OUTLINE_DATA
*/
Predicate Information (identified by operation id):
---------------------------------------------------
4 - access("A"."OBJECT_ID">=1 AND "A"."OBJECT_ID"<=30)
5 - access("B"."OBJECT_ID"="A"."OBJECT_ID")
filter(("B"."OBJECT_ID"<=30 AND "B"."OBJECT_ID">=1))
Column Projection Information (identified by operation id):
-----------------------------------------------------------
1 - "A"."OBJECT_NAME"[VARCHAR2,128], "A"."LAST_DDL_TIME"[DATE,7],
"B"."CREATED"[DATE,7]
2 - "A"."OBJECT_NAME"[VARCHAR2,128], "A"."LAST_DDL_TIME"[DATE,7],
"B".ROWID[ROWID,10]
3 - "A"."OBJECT_NAME"[VARCHAR2,128], "A"."OBJECT_ID"[NUMBER,22],
"A"."LAST_DDL_TIME"[DATE,7]
4 - "A".ROWID[ROWID,10], "A"."OBJECT_ID"[NUMBER,22]
5 - "B".ROWID[ROWID,10]
6 - "B"."CREATED"[DATE,7]
Case-2 : optimizer_features_enable=10.2.0.3
In this case you will see 1 nested loops in the execution plan and new operation NLJ_BATCHING in missing in outline_data.
SQL> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(format => 'ADVANCED'));
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID apzagcxrnwka8, child number 1
-------------------------------------
SELECT A.OBJECT_NAME , A. LAST_DDL_TIME , B.CREATED FROM A, B WHERE
b.OBJECT_ID=a.OBJECT_ID AND a.OBJECT_ID BETWEEN 1 AND 30
Plan hash value: 1900124691
---------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 5 (100)| |
| 1 | NESTED LOOPS | | 3 | 135 | 5 (0)| 00:00:01 |
| 2 | TABLE ACCESS BY INDEX ROWID| A | 16 | 512 | 3 (0)| 00:00:01 |
|* 3 | INDEX RANGE SCAN | IDX_A | 16 | | 2 (0)| 00:00:01 |
| 4 | TABLE ACCESS BY INDEX ROWID| B | 1 | 13 | 1 (0)| 00:00:01 |
|* 5 | INDEX UNIQUE SCAN | INDX_B | 1 | | 0 (0)| |
---------------------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
1 - SEL$1
2 - SEL$1 / A@SEL$1
3 - SEL$1 / A@SEL$1
4 - SEL$1 / B@SEL$1
5 - SEL$1 / B@SEL$1
Outline Data
-------------
/*+
BEGIN_OUTLINE_DATA
IGNORE_OPTIM_EMBEDDED_HINTS
OPTIMIZER_FEATURES_ENABLE('10.2.0.3')
DB_VERSION('11.2.0.2')
OPT_PARAM('query_rewrite_enabled' 'false')
FIRST_ROWS(10)
OUTLINE_LEAF(@"SEL$1")
INDEX_RS_ASC(@"SEL$1" "A"@"SEL$1" ("A"."OBJECT_ID"))
INDEX_RS_ASC(@"SEL$1" "B"@"SEL$1" ("B"."OBJECT_ID"))
LEADING(@"SEL$1" "A"@"SEL$1" "B"@"SEL$1")
USE_NL(@"SEL$1" "B"@"SEL$1")
END_OUTLINE_DATA
*/
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("A"."OBJECT_ID">=1 AND "A"."OBJECT_ID"<=30)
5 - access("B"."OBJECT_ID"="A"."OBJECT_ID")
filter(("B"."OBJECT_ID"<=30 AND "B"."OBJECT_ID">=1))
Column Projection Information (identified by operation id):
-----------------------------------------------------------
1 - "A"."OBJECT_NAME"[VARCHAR2,128], "A"."LAST_DDL_TIME"[DATE,7],
"B"."CREATED"[DATE,7]
2 - "A"."OBJECT_NAME"[VARCHAR2,128], "A"."OBJECT_ID"[NUMBER,22],
"A"."LAST_DDL_TIME"[DATE,7]
3 - "A".ROWID[ROWID,10], "A"."OBJECT_ID"[NUMBER,22]
4 - "B"."CREATED"[DATE,7]
5 - "B".ROWID[ROWID,10]
63 rows selected.