view optimization (10gr2)
When talking about view optimization, we need to know about mergeable view and unmergeable view. 10gr2 concept gives very clear definition.
The optimizer can merge a view into a referencing query block when the view has one or more base tables, provided the view does not contain:
- set operators (UNION, UNION ALL, INTERSECT, MINUS)
- a CONNECT BY clause
- a ROWNUM pseudocolumn
- aggregate functions (AVG, COUNT, MAX, MIN, SUM) in the select list
When a view contains one of the following structures, it can be merged into a referencing query block only ifcomplex view mergingis enabled (as described below):
View merging is not possible for a view that has multiple base tables if it is on the right side of an outer join. If a view on the right side of an outer join has only one base table, however, the optimizer can use complex view merging even if an expression in the view can return a non-null value for a NULL. See"Views in Outer Joins"for more information.
We can use three hint to instruct CBO to chose a better execution plan when there is view in query.
merge
This is easy to understand, if a view is not merged in a query and you think it may faster after merge, you can use this hint.
un_merge
Consider this situation, you have several well-optimized views. Accessing any of them will be very smoothly. But when you put these views in to a query, oracle merge these view which returns a very slow execution plan. How can you to resolve this situation? you can use un_merge.Since the orignal view is optimized very well then why we merge them? Just use them. Lets see below example
SQL> select count(*) from DBA_OBJECTS; COUNT(*) ---------- 56714 Elapsed: 00:00:00.22 SQL> select count(*) from DBA_SEQUENCES; COUNT(*) ---------- 363 Elapsed: 00:00:00.04
We can see accessing these two data dictionary view is very fast.
SQL> SELECT COUNT(*) FROM DBA_SEQUENCES A, DBA_OBJECTS B; COUNT(*) ---------- 20587182 Elapsed: 00:00:23.68
But the query for puting them together run very slow. Why ? If you check execution plan, you will find the orignal well optiomized view is merged. Now lets use the un_merge
SQL> SELECT /*+ NO_MERGE(A) NO_MERGE(B) */ COUNT(*) FROM DBA_SEQUENCES A, DBA_OBJECTS B; COUNT(*) ---------- 20587182 Elapsed: 00:00:04.16
Check the execution plan , we see view not merged.
SQL> explain plan for SELECT /*+ NO_MERGE(A) NO_MERGE(B) */ COUNT(*) FROM DBA_SEQUENCES A, DBA_OBJECTS B; Explained. Elapsed: 00:00:00.04 SQL> select * from table(dbms_xplan.display()); PLAN_TABLE_OUTPUT -------------------------------------------------------------------------------------------------------------------------------- Plan hash value: 2492168462 --------------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | | 66454 (10)| 00:13:18 | | 1 | SORT AGGREGATE | | 1 | | | | | 2 | MERGE JOIN CARTESIAN | | 19M| | 66454 (10)| 00:13:18 | | 3 | VIEW | DBA_SEQUENCES | 363 | | 173 (6)| 00:00:03 | |* 4 | HASH JOIN | | 363 | 5808 | 173 (6)| 00:00:03 | | 5 | TABLE ACCESS FULL | USER$ | 87 | 261 | 3 (0)| 00:00:01 | |* 6 | HASH JOIN | | 363 | 4719 | 169 (5)| 00:00:03 | | 7 | INDEX FULL SCAN | I_SEQ1 | 363 | 1815 | 1 (0)| 00:00:01 | | 8 | TABLE ACCESS FULL | OBJ$ | 57907 | 452K| 166 (4)| 00:00:02 | | 9 | BUFFER SORT | | 52840 | | 66454 (10)| 00:13:18 | | 10 | VIEW | DBA_OBJECTS | 52840 | | 183 (10)| 00:00:03 | | 11 | UNION-ALL | | | | | | |* 12 | FILTER | | | | | | |* 13 | HASH JOIN | | 57903 | 4636K| 180 (10)| 00:00:03 | | 14 | TABLE ACCESS FULL | USER$ | 87 | 261 | 3 (0)| 00:00:01 | |* 15 | TABLE ACCESS FULL | OBJ$ | 57903 | 4467K| 174 (9)| 00:00:03 | |* 16 | TABLE ACCESS BY INDEX ROWID| IND$ | 1 | 8 | 2 (0)| 00:00:01 | |* 17 | INDEX UNIQUE SCAN | I_IND1 | 1 | | 1 (0)| 00:00:01 | | 18 | NESTED LOOPS | | 2 | 12 | 3 (0)| 00:00:01 | | 19 | INDEX FULL SCAN | I_LINK1 | 2 | 6 | 1 (0)| 00:00:01 | | 20 | TABLE ACCESS CLUSTER | USER$ | 1 | 3 | 1 (0)| 00:00:01 | |* 21 | INDEX UNIQUE SCAN | I_USER# | 1 | | 0 (0)| 00:00:01 | --------------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 4 - access("U"."USER#"="O"."OWNER#") 6 - access("O"."OBJ#"="S"."OBJ#") 12 - filter("O"."TYPE#"<>1 AND "O"."TYPE#"<>10 OR "O"."TYPE#"=1 AND (SELECT 1 FROM "SYS"."IND$" "I" WHERE "I"."OBJ#"=:B1 AND ("I"."TYPE#"=1 OR "I"."TYPE#"=2 OR "I"."TYPE#"=3 OR "I"."TYPE#"=4 OR "I"."TYPE#"=6 OR "I"."TYPE#"=7 OR "I"."TYPE#"=9))=1) 13 - access("O"."OWNER#"="U"."USER#") 15 - filter("O"."NAME"<>'_NEXT_OBJECT' AND "O"."NAME"<>'_default_auditing_options_' AND "O"."LINKNAME" IS NULL) 16 - filter("I"."TYPE#"=1 OR "I"."TYPE#"=2 OR "I"."TYPE#"=3 OR "I"."TYPE#"=4 OR "I"."TYPE#"=6 OR "I"."TYPE#"=7 OR "I"."TYPE#"=9) 17 - access("I"."OBJ#"=:B1) 21 - access("L"."OWNER#"="U"."USER#") 44 rows selected. Elapsed: 00:00:00.04
push_pred
In predicate pushing, the optimizer "pushes" the relevant predicates from the containing query block into the view query block. For views that are not merged, this technique improves the subplan of the unmerged view because the database can use the pushed-in predicates to access indexes or to use as filters.
For example, suppose you create a view that references two employee tables. The view is defined with a compound query that uses the UNION
set operator, as follows:
CREATE VIEW all_employees_vw AS ( SELECT employee_id, last_name, job_id, commission_pct, department_id FROM employees ) UNION ( SELECT employee_id, last_name, job_id, commission_pct, department_id FROM contract_workers );
You then query the view as follows:
SELECT last_name FROM all_employees_vw WHERE department_id = 50;
Because the view is a compound query, the optimizer cannot merge the view's query into the accessing query block. Instead, the optimizer can transform the accessing statement by pushing its predicate, the WHERE
clause condition department_id=50
, into the view's compound query. The equivalent transformed query is as follows:
SELECT last_name FROM ( SELECT employee_id, last_name, job_id, commission_pct, department_id FROM employees WHERE department_id=50 UNION SELECT employee_id, last_name, job_id, commission_pct, department_id FROM contract_workers WHERE department_id=50 );