조인 제거(Join Elimination)
{tip} 1:M 조인시 조인외에 1쪽 테이블을 참조하지 않는다면? 조인 제거!{tip}
{code:sql}
-- 준비
drop table emp;
drop table dept;

create table emp as select * from scott.emp;
create table dept as select * from scott.dept;

|
||조인 제거 데모 (PK, FK 없다)||조인 제거 데모 (PK, FK 있다)||
|{code:sql}
select /*+ gather_plan_statistics */ e.empno, e.ename, e.deptno, e.sal, e.hiredate
  from dept d, emp e
 where d.deptno = e.deptno;

|


alter table emp add constraint emp_pk primary key (empno);
alter table dept add constraint dept_pk primary key (deptno);
-- FK 를 만들었는데 오히려 성능이 향상되는 경우
alter table emp add constraint emp_fk_dept foreign key (deptno) references dept (deptno);

exec dbms_stats.gather_table_stats (ownname => 'uadmin', tabname => 'emp' , degree => 1, cascade => TRUE);
exec dbms_stats.gather_table_stats (ownname => 'uadmin', tabname => 'dept' , degree => 1, cascade => TRUE);

select /*+ gather_plan_statistics */ e.empno, e.ename, e.deptno, e.sal, e.hiredate
  from dept d, emp e
 where d.deptno = e.deptno;

|

{code:sql}




























IdOperationNameStartsE-RowsA-RowsA-TimeBuffersOMem1MemUsed-Mem





























0SELECT STATEMENT11400:00:00.017
  • 1
HASH JOIN1141400:00:00.0171517K1517K637K (0)
2TABLE ACCESS FULLDEPT14400:00:00.013
3TABLE ACCESS FULLEMP1141400:00:00.014





























Outline Data




-

/*+
BEGIN_OUTLINE_DATA
IGNORE_OPTIM_EMBEDDED_HINTS
OPTIMIZER_FEATURES_ENABLE('11.2.0.1')
DB_VERSION('11.2.0.1')
OPT_PARAM('optimizer_index_cost_adj' 25)
OPT_PARAM('optimizer_index_caching' 90)
FIRST_ROWS(10)
OUTLINE_LEAF(@"SEL$1")
FULL(@"SEL$1" "D"@"SEL$1")
FULL(@"SEL$1" "E"@"SEL$1")
LEADING(@"SEL$1" "D"@"SEL$1" "E"@"SEL$1")
USE_HASH(@"SEL$1" "E"@"SEL$1")
END_OUTLINE_DATA
*/

Predicate Information (identified by operation id):













---

1 - access("D"."DEPTNO"="E"."DEPTNO")

Note


-

  • dynamic sampling used for this statement (level=2)
|{code:sql}
------------------------------------------------------------------------------------
| Id  | Operation         | Name | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |      1 |        |     14 |00:00:00.01 |       4 |
|*  1 |  TABLE ACCESS FULL| EMP  |      1 |     10 |     14 |00:00:00.01 |       4 |
------------------------------------------------------------------------------------

Outline Data
-------------

  /*+
      BEGIN_OUTLINE_DATA
      IGNORE_OPTIM_EMBEDDED_HINTS
      OPTIMIZER_FEATURES_ENABLE('11.2.0.1')
      DB_VERSION('11.2.0.1')
      OPT_PARAM('optimizer_index_cost_adj' 25)
      OPT_PARAM('optimizer_index_caching' 90)
      FIRST_ROWS(10)
      OUTLINE_LEAF(@"SEL$F7859CDE")
      ELIMINATE_JOIN(@"SEL$1" "D"@"SEL$1")
      OUTLINE(@"SEL$1")
      FULL(@"SEL$F7859CDE" "E"@"SEL$1")
      END_OUTLINE_DATA
  */

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter("E"."DEPTNO" IS NOT NULL)

|

조인 제거 데모 (eliminate_join)조인 제거 데모 (no_eliminate_join)
{code:sql}
select /*+ gather_plan_statistics eliminate_join(d) */ e.empno, e.ename, e.deptno, e.sal, e.hiredate
from dept d, emp e
where d.deptno = e.deptno;
{code}
{code:sql}
select /*+ gather_plan_statistics no_eliminate_join(d) */ e.empno, e.ename, e.deptno, e.sal, e.hiredate
from dept d, emp e
where d.deptno = e.deptno;
{code}
{code:sql}





















IdOperationNameStartsE-RowsA-RowsA-TimeBuffers






















0SELECT STATEMENT11400:00:00.014
  • 1
TABLE ACCESS FULLEMP1101400:00:00.014






















Outline Data




-

/*+
BEGIN_OUTLINE_DATA
IGNORE_OPTIM_EMBEDDED_HINTS
OPTIMIZER_FEATURES_ENABLE('11.2.0.1')
DB_VERSION('11.2.0.1')
OPT_PARAM('optimizer_index_cost_adj' 25)
OPT_PARAM('optimizer_index_caching' 90)
FIRST_ROWS(10)
OUTLINE_LEAF(@"SEL$F7859CDE")
ELIMINATE_JOIN(@"SEL$1" "D"@"SEL$1")
OUTLINE(@"SEL$1")
FULL(@"SEL$F7859CDE" "E"@"SEL$1")
END_OUTLINE_DATA
*/

Predicate Information (identified by operation id):













---

1 - filter("E"."DEPTNO" IS NOT NULL)

|{code:sql}
----------------------------------------------------------------------------------------
| Id  | Operation          | Name    | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
----------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |         |      1 |        |     14 |00:00:00.01 |       8 |
|   1 |  NESTED LOOPS      |         |      1 |     10 |     14 |00:00:00.01 |       8 |
|   2 |   TABLE ACCESS FULL| EMP     |      1 |     10 |     14 |00:00:00.01 |       4 |
|*  3 |   INDEX UNIQUE SCAN| DEPT_PK |     14 |      1 |     14 |00:00:00.01 |       4 |
----------------------------------------------------------------------------------------

Outline Data
-------------

  /*+
      BEGIN_OUTLINE_DATA
      IGNORE_OPTIM_EMBEDDED_HINTS
      OPTIMIZER_FEATURES_ENABLE('11.2.0.1')
      DB_VERSION('11.2.0.1')
      OPT_PARAM('optimizer_index_cost_adj' 25)
      OPT_PARAM('optimizer_index_caching' 90)
      FIRST_ROWS(10)
      OUTLINE_LEAF(@"SEL$1")
      FULL(@"SEL$1" "E"@"SEL$1")
      INDEX(@"SEL$1" "D"@"SEL$1" ("DEPT"."DEPTNO"))
      LEADING(@"SEL$1" "E"@"SEL$1" "D"@"SEL$1")
      USE_NL(@"SEL$1" "D"@"SEL$1")
      END_OUTLINE_DATA
  */

Predicate Information (identified by operation id):
---------------------------------------------------

   3 - access("D"."DEPTNO"="E"."DEPTNO")

|

조인 제거 데모 (_optimizer_join_elimination_enabled = true)조인 제거 데모 (_optimizer_join_elimination_enabled = false)
{code:sql}
alter session set "_optimizer_join_elimination_enabled" = true;

select /*+ gather_plan_statistics */ e.empno, e.ename, e.deptno, e.sal, e.hiredate
from dept d, emp e
where d.deptno = e.deptno;

|{code:sql}
alter session set "_optimizer_join_elimination_enabled" = false; 

select /*+ gather_plan_statistics */ e.empno, e.ename, e.deptno, e.sal, e.hiredate
  from dept d, emp e
 where d.deptno = e.deptno;

|

{code:sql}





















IdOperationNameStartsE-RowsA-RowsA-TimeBuffers






















0SELECT STATEMENT11400:00:00.014
  • 1
TABLE ACCESS FULLEMP1101400:00:00.014






















Outline Data




-

/*+
BEGIN_OUTLINE_DATA
IGNORE_OPTIM_EMBEDDED_HINTS
OPTIMIZER_FEATURES_ENABLE('11.2.0.1')
DB_VERSION('11.2.0.1')
OPT_PARAM('optimizer_index_cost_adj' 25)
OPT_PARAM('optimizer_index_caching' 90)
FIRST_ROWS(10)
OUTLINE_LEAF(@"SEL$F7859CDE")
ELIMINATE_JOIN(@"SEL$1" "D"@"SEL$1")
OUTLINE(@"SEL$1")
FULL(@"SEL$F7859CDE" "E"@"SEL$1")
END_OUTLINE_DATA
*/

Predicate Information (identified by operation id):













---

1 - filter("E"."DEPTNO" IS NOT NULL)

|{code:sql}
----------------------------------------------------------------------------------------
| Id  | Operation          | Name    | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
----------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |         |      1 |        |     14 |00:00:00.01 |       8 |
|   1 |  NESTED LOOPS      |         |      1 |     10 |     14 |00:00:00.01 |       8 |
|   2 |   TABLE ACCESS FULL| EMP     |      1 |     10 |     14 |00:00:00.01 |       4 |
|*  3 |   INDEX UNIQUE SCAN| DEPT_PK |     14 |      1 |     14 |00:00:00.01 |       4 |
----------------------------------------------------------------------------------------

Outline Data
-------------

  /*+
      BEGIN_OUTLINE_DATA
      IGNORE_OPTIM_EMBEDDED_HINTS
      OPTIMIZER_FEATURES_ENABLE('11.2.0.1')
      DB_VERSION('11.2.0.1')
      OPT_PARAM('_optimizer_join_elimination_enabled' 'false')
      OPT_PARAM('optimizer_index_cost_adj' 25)
      OPT_PARAM('optimizer_index_caching' 90)
      FIRST_ROWS(10)
      OUTLINE_LEAF(@"SEL$1")
      FULL(@"SEL$1" "E"@"SEL$1")
      INDEX(@"SEL$1" "D"@"SEL$1" ("DEPT"."DEPTNO"))
      LEADING(@"SEL$1" "E"@"SEL$1" "D"@"SEL$1")
      USE_NL(@"SEL$1" "D"@"SEL$1")
      END_OUTLINE_DATA
  */

Predicate Information (identified by operation id):
---------------------------------------------------

   3 - access("D"."DEPTNO"="E"."DEPTNO")

|

조인 제거 데모 (IN)
{code:sql}
select /*+ gather_plan_statistics */ * from emp e
where e.deptno in (select deptno from dept);
{code}
{code:sql}





















IdOperationNameStartsE-RowsA-RowsA-TimeBuffers






















0SELECT STATEMENT11400:00:00.014
  • 1
TABLE ACCESS FULLEMP1101400:00:00.014






















Outline Data




-

/*+
BEGIN_OUTLINE_DATA
IGNORE_OPTIM_EMBEDDED_HINTS
OPTIMIZER_FEATURES_ENABLE('11.2.0.1')
DB_VERSION('11.2.0.1')
OPT_PARAM('optimizer_index_cost_adj' 25)
OPT_PARAM('optimizer_index_caching' 90)
FIRST_ROWS(10)
OUTLINE_LEAF(@"SEL$D53E76E4")
ELIMINATE_JOIN(@"SEL$5DA710D3" "DEPT"@"SEL$2")
OUTLINE(@"SEL$5DA710D3")
UNNEST(@"SEL$2")
OUTLINE(@"SEL$1")
OUTLINE(@"SEL$2")
FULL(@"SEL$D53E76E4" "E"@"SEL$1")
END_OUTLINE_DATA
*/

Predicate Information (identified by operation id):













---

1 - filter("E"."DEPTNO" IS NOT NULL)

|
||조인 제거 데모 (EXIST)||
|{code:sql}
select /*+ gather_plan_statistics */ * from emp e
 where exists (select 1 from dept where deptno = e.deptno);

|

{code:sql}





















IdOperationNameStartsE-RowsA-RowsA-TimeBuffers






















0SELECT STATEMENT11400:00:00.014
  • 1
TABLE ACCESS FULLEMP1101400:00:00.014






















Outline Data




-

/*+
BEGIN_OUTLINE_DATA
IGNORE_OPTIM_EMBEDDED_HINTS
OPTIMIZER_FEATURES_ENABLE('11.2.0.1')
DB_VERSION('11.2.0.1')
OPT_PARAM('optimizer_index_cost_adj' 25)
OPT_PARAM('optimizer_index_caching' 90)
FIRST_ROWS(10)
OUTLINE_LEAF(@"SEL$D53E76E4")
ELIMINATE_JOIN(@"SEL$5DA710D3" "DEPT"@"SEL$2")
OUTLINE(@"SEL$5DA710D3")
UNNEST(@"SEL$2")
OUTLINE(@"SEL$1")
OUTLINE(@"SEL$2")
FULL(@"SEL$D53E76E4" "E"@"SEL$1")
END_OUTLINE_DATA
*/

Predicate Information (identified by operation id):













---

1 - filter("E"."DEPTNO" IS NOT NULL)

|
||조인 제거 데모 (OUTER JOIN)||
|{code:sql}
select /*+ gather_plan_statistics */ e.empno, e.ename, e.deptno, e.sal, e.hiredate
  from emp e, dept d
 where e.deptno = d.deptno(+);

|

{code:sql}





















IdOperationNameStartsE-RowsA-RowsA-TimeBuffers






















0SELECT STATEMENT11400:00:00.014
1TABLE ACCESS FULLEMP1101400:00:00.014






















Outline Data




-

/*+
BEGIN_OUTLINE_DATA
IGNORE_OPTIM_EMBEDDED_HINTS
OPTIMIZER_FEATURES_ENABLE('11.2.0.1')
DB_VERSION('11.2.0.1')
OPT_PARAM('optimizer_index_cost_adj' 25)
OPT_PARAM('optimizer_index_caching' 90)
FIRST_ROWS(10)
OUTLINE_LEAF(@"SEL$F7859CDE")
ELIMINATE_JOIN(@"SEL$1" "D"@"SEL$1")
OUTLINE(@"SEL$1")
FULL(@"SEL$F7859CDE" "E"@"SEL$1")
END_OUTLINE_DATA
*/
– Predicate Information 없음 ♡

|
|{code:sql}
-- 조인 제거(Join Elimination)의 제약사항
--  1. JOIN 되는 FK 가 싱글 컬럼만 된다
--  2. JOIN 조건은 Equal(=)만 된다
--  3. SELECT 절에 제거 대상 테이블의 컬럼이 출현하면 안된다 (당근)
--  4. 10g 이하에서는 ANSI SQL JOIN 문은 안된다. (11g 부터 가능)

|


이 문서의 내용은 (주)비투엔컬설팅에서 출간한 '오라클 성능 고도화 원리와 해법II'를 참고하였습니다.
이 문서의 내용은 (주)오픈메이드컨설팅에서 출간한 'THE LOGICAL OPTIMIZER'를 참고하였습니다.