| ||
{code:SQL | title= _optimizer_native_full_outer_join = OFF; | borderStyle=solid} alter session set "_optimizer_native_full_outer_join" = OFF; |
ALTER SESSION SET EVENTS '10053 trace name context forever, level 1';
SELECT a.employee_id, a.first_name, a.last_name, a.email, b.department_name
FROM employee a FULL OUTER JOIN department b
ON (a.department_id = b.department_id) ;
ALTER SESSION SET EVENTS '10053 trace name context off';
Id | Operation | Name | Rows | Bytes | Cost | Time |
0 | SELECT STATEMENT | 7 | ||||
1 | VIEW | 124 | 8680 | 7 | 00:00:01 | |
2 | UNION-ALL | |||||
3 | HASH JOIN OUTER | 107 | 4708 | 5 | 00:00:01 | |
4 | TABLE ACCESS FULL | EMPLOYEE | 107 | 3210 | 2 | 00:00:01 |
5 | TABLE ACCESS FULL | DEPARTMENT | 27 | 378 | 2 | 00:00:01 |
6 | MERGE JOIN ANTI | 17 | 289 | 1 | 00:00:01 | |
7 | TABLE ACCESS BY INDEX ROWID | DEPARTMENT | 27 | 378 | 0 | |
8 | INDEX FULL SCAN | DEPT_ID_PK | 27 | 0 | ||
9 | SORT UNIQUE | 107 | 321 | 1 | 00:00:01 | |
10 | INDEX FULL SCAN | EMP_DEPARTMENT_IX | 107 | 321 | 0 |
* 윗부분은 분명히 Outer 조인으로 풀렸지만 아랫부분은 Anti 조인으로 풀렸으므로 이것은 서브쿼리를 사용한 것이다
즉,옵티마이져는 Full Outer Join을 아래처럼 Union All로 재작성한 것이다
|{code:SQL|title= SQL변환 |borderStyle=solid}
explain plan for
SELECT *
FROM (SELECT a.employee_id, a.first_name, a.last_name, a.email, b.department_name
FROM employee a, department b
WHERE a.department_id = b.department_id(+)
UNION ALL
SELECT NULL, NULL, NULL, NULL, b.department_name
FROM department b
WHERE NOT EXISTS (SELECT 1
FROM employee a
WHERE a.department_id = b.department_id)
);
select * from table(dbms_xplan.display);
----------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 124 | 8680 | 7 (43)| 00:00:01 |
| 1 | VIEW | | 124 | 8680 | 7 (43)| 00:00:01 |
| 2 | UNION-ALL | | | | | |
|* 3 | HASH JOIN OUTER | | 107 | 4708 | 5 (20)| 00:00:01 |
| 4 | TABLE ACCESS FULL | EMPLOYEE | 107 | 3210 | 2 (0)| 00:00:01 |
| 5 | TABLE ACCESS FULL | DEPARTMENT | 27 | 378 | 2 (0)| 00:00:01 |
| 6 | MERGE JOIN ANTI | | 17 | 289 | 1 (100)| 00:00:01 |
| 7 | TABLE ACCESS BY INDEX ROWID| DEPARTMENT | 27 | 378 | 0 (0)| 00:00:01 |
| 8 | INDEX FULL SCAN | DEPT_ID_PK | 27 | | 0 (0)| 00:00:01 |
|* 9 | SORT UNIQUE | | 107 | 321 | 1 (100)| 00:00:01 |
| 10 | INDEX FULL SCAN | EMP_DEPARTMENT_IX | 107 | 321 | 0 (0)| 00:00:01 |
----------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("A"."DEPARTMENT_ID"="B"."DEPARTMENT_ID"(+))
9 - access("A"."DEPARTMENT_ID"="B"."DEPARTMENT_ID")
filter("A"."DEPARTMENT_ID"="B"."DEPARTMENT_ID")
24 rows selected.
| title= Query Block Registry | borderStyle=solid} Query Block Registry: SEL$5 0x10b08af0 (PARSER) FINAL SET$1 0x10b00048 (PARSER) FINAL SEL$4 0x10b00318 (PARSER) SEL$45B11F62 0x10b00318 (SUBQUERY UNNEST SEL$4; SEL$3) FINAL SEL$3 0x10afcf08 (PARSER) SEL$45B11F62 0x10b00318 (SUBQUERY UNNEST SEL$4; SEL$3) FINAL SEL$2 0x10affd78 (PARSER) SEL$58A6D7F6 0x10affd78 (VIEW MERGE SEL$2; SEL$1) FINAL SEL$1 0x10afe938 (PARSER) SEL$58A6D7F6 0x10affd78 (VIEW MERGE SEL$2; SEL$1) FINAL {code} |
|
{code:SQL | title= UNPARSED OUERY | borderStyle=solid} SELECT "A"."EMPLOYEE_ID" "EMPLOYEE_ID", "A"."FIRST_NAME" "FIRST_NAME", "A"."LAST_NAME" "LAST_NAME", "A"."EMAIL" "EMAIL", "A"."DEPARTMENT_ID" "QCSJ_C000000000300000", "from$_subquery$_004"."DEPARTMENT_ID_1" "QCSJ_C000000000300001", "from$_subquery$_004"."DEPARTMENT_NAME_0" "DEPARTMENT_NAME" FROM "TLO"."EMPLOYEE" "A", LATERAL( (SELECT "B"."DEPARTMENT_NAME" "DEPARTMENT_NAME_0", "B"."DEPARTMENT_ID" "DEPARTMENT_ID_1" FROM "TLO"."DEPARTMENT" "B" WHERE "A"."DEPARTMENT_ID"="B"."DEPARTMENT_ID") )(+) "from$_subquery$_004" {code} |
|
| title= QUERY BLOCK SIGNATURE | borderStyle=solid} Registered qb: SEL$1 0x10afe938 (PARSER) - QUERY BLOCK SIGNATURE - signature (): qb_name=SEL$1 nbfros=1 flg=0 fro(0): flg=4 objn=459677 hint_alias="B"@"SEL$1" |
Registered qb: SEL$2 0x10affd78 (PARSER)
Registered qb: SEL$3 0x10afcf08 (PARSER)
Registered qb: SEL$4 0x10b00318 (PARSER)
|* Query Parser가 SQL을 수집하여 개별 QUERY BLOCK으로 구분한 것이다.
Query Transformation이 발생하면 QUERY VLOCK의 명이 바뀔수 있으므로 힌트에 쿼리블록명을 사용할 경우 항상 확인 후 사용해야 함
{code:SQL|title= 쿼리블록명 확인 |borderStyle=solid}
SELECT * FROM TABLE(DBMS_XPlan.dispay_cursor(NULL,NULL,'allstats last -rows +alias +outline +predicate')); -- alias추가
|