Hi Tom,
I have just faced a problem in my work.
Here is a similar case for my work.
CREATE TABLE TABLE_1
(
MY_ID VARCHAR2(20),
REVISION NUMBER(3,0),
DATE_FROM DATE,
DATE_TO DATE,
STATUS VARCHAR2(50),
MS_USER VARCHAR2(50)
);
ALTER TABLE TABLE_1
ADD CONSTRAINT TABLE1_PK PRIMARY KEY (MY_ID);
CREATE TABLE TABLE_2
(
MY_ID VARCHAR2(20) ,
REVISION NUMBER(3,0),
DATE_FROM DATE,
DATE_TO DATE,
STATUS VARCHAR2(50),
MS_USER VARCHAR2(50)
);
ALTER TABLE TABLE_2
ADD CONSTRAINT TABLE2_PK PRIMARY KEY (MY_ID);
CREATE TABLE TABLE_extra
(
extra_ID VARCHAR2(20),
EXT_MY_ID VARCHAR2(20)
);
ALTER TABLE TABLE_extra
ADD CONSTRAINT TABLEext_PK PRIMARY KEY (extra_ID);
,and which Table_1, Table_2 is extremely large
I try the following SQL.
but it full scan the table_1, and table_2 completely rather than pushing the join predicate down the in-line view.
SELECT /*+ PUSH_PRED(inline_1) */ *
FROM TABLE_extra
JOIN ( SELECT MY_ID, MS_USER, MIN(REVISION), MAX(REVISION), MIN(DATE_FROM), MAX(DATE_TO)
FROM TABLE_1
WHERE STATUS <> 'DELETE'
GROUP BY MY_ID, MS_USER
UNION
SELECT MY_ID, MS_USER, MIN(REVISION), MAX(REVISION), MIN(DATE_FROM), MAX(DATE_TO)
FROM TABLE_2
WHERE STATUS <> 'DELETE'
GROUP BY MY_ID, MS_USER
) inline_1 ON TABLE_extra.EXT_MY_ID = inline_1. MY_ID
WHERE TABLE_extra.EXTRA_ID = '123456';I suppose it will
index scan TABLE_extra by extra_ID,
then get the relevant MY_ID information from TABLE_1 using Primary Key Index
then get the relevant MY_ID information from TABLE_2 using Primary Key Index
then union them together
at least, join them together.
However, actually it does is
index scan TABLE_extra by extra_ID
Full Scan TABLE_1
Full Scan TABLE_2
Union both TABLE_1 and TABLE_2
at last, join them together.
Explain plan for the sql:
Other XML
{info}
info type="db_version"
11.2.0.1
info type="parse_schema"
"testDB"
info type="dynamic_sampling"
2
info type="plan_hash"
909077764
info type="plan_hash_2"
1699902986
{hint}
USE_HASH_AGGREGATION(@"SEL$2")
FULL(@"SEL$2" "TABLE_1"@"SEL$2")
USE_HASH_AGGREGATION(@"SEL$3")
FULL(@"SEL$3" "TABLE_2"@"SEL$3")
USE_NL(@"SEL$16C51A37" "INLINE_1"@"SEL$1")
LEADING(@"SEL$16C51A37" "TABLE_EXTRA"@"SEL$1" "INLINE_1"@"SEL$1")
NO_ACCESS(@"SEL$16C51A37" "INLINE_1"@"SEL$1")
INDEX_RS_ASC(@"SEL$16C51A37" "TABLE_EXTRA"@"SEL$1" ("TABLE_EXTRA"."EXTRA_ID"))
OUTLINE(@"SEL$1")
OUTLINE(@"SEL$4")
MERGE(@"SEL$1")
OUTLINE_LEAF(@"SEL$16C51A37")
OUTLINE_LEAF(@"SET$1")
OUTLINE_LEAF(@"SEL$3")
OUTLINE_LEAF(@"SEL$2")
ALL_ROWS
DB_VERSION('11.2.0.1')
OPTIMIZER_FEATURES_ENABLE('11.2.0.1')
IGNORE_OPTIM_EMBEDDED_HINTS I try to use push_pred() hint on the inline_1 but nothing happened,
please suggest how to optimize this kind of SQL which join push pred is not working.
Thank you,
Derek
Puhsing predicates into subqueries containing aggregates is always a challenge (for the optimizer and for us) because you never know if by doing you might change the result.
See
https://asktom.oracle.com/pls/apex/f?p=100:11:0::NO::P11_QUESTION_ID:3469884600671 for a good example
But we can work around it if you're prepared to use some object types. Here's a top to bottom example, showing the poor plan and how to alter the SQL to make it a better one.
SQL> CREATE TABLE TABLE_1
2 (
3 MY_ID VARCHAR2(20),
4 REVISION NUMBER(3,0),
5 DATE_FROM DATE,
6 DATE_TO DATE,
7 STATUS VARCHAR2(50),
8 MS_USER VARCHAR2(50)
9 );
Table created.
SQL> insert /*+ APPEND */ into table_1
2 select rownum, mod(rownum,10), trunc(sysdate)+mod(rownum,20), trunc(sysdate)+50 + mod(rownum,20), 'X', 'user'
3 from
4 ( select 1 from dual connect by level <= 1000 ) ,
5 ( select 1 from dual connect by level <= 5000 );
5000000 rows created.
SQL> ALTER TABLE TABLE_1 ADD CONSTRAINT TABLE1_PK PRIMARY KEY (MY_ID);
Table altered.
SQL> CREATE TABLE TABLE_2
2 (
3 MY_ID VARCHAR2(20) ,
4 REVISION NUMBER(3,0),
5 DATE_FROM DATE,
6 DATE_TO DATE,
7 STATUS VARCHAR2(50),
8 MS_USER VARCHAR2(50)
9 );
Table created.
SQL> insert /*+ APPEND */ into table_2
2 select rownum, mod(rownum,10), trunc(sysdate)+mod(rownum,20), trunc(sysdate)+50 + mod(rownum,20), 'X', 'user'
3 from
4 ( select 1 from dual connect by level <= 1000 ) ,
5 ( select 1 from dual connect by level <= 5000 );
5000000 rows created.
SQL> ALTER TABLE TABLE_2
2 ADD CONSTRAINT TABLE2_PK PRIMARY KEY (MY_ID);
Table altered.
SQL> CREATE TABLE TABLE_extra
2 (
3 extra_ID VARCHAR2(20),
4 EXT_MY_ID VARCHAR2(20)
5 );
Table created.
SQL> ALTER TABLE TABLE_extra
2 ADD CONSTRAINT TABLEext_PK PRIMARY KEY (extra_ID);
Table altered.
SQL> insert into TABLE_extra select 123450+rownum, rownum+1000 from dual connect by level <= 20;
20 rows created.
SQL> commit;
Commit complete.
SQL> exec dbms_stats.gather_table_stats('','TABLE_extra')
PL/SQL procedure successfully completed.
SQL> @exp
5 SELECT /*+ PUSH_PRED(inline_1) */ *
6 FROM TABLE_extra
7 JOIN (
8 SELECT MY_ID, MS_USER, MIN(REVISION), MAX(REVISION), MIN(DATE_FROM), MAX(DATE_TO)
9 FROM TABLE_1
10 WHERE STATUS <> 'DELETE'
11 GROUP BY MY_ID, MS_USER
12 UNION
13 SELECT MY_ID, MS_USER, MIN(REVISION), MAX(REVISION), MIN(DATE_FROM), MAX(DATE_TO)
14 FROM TABLE_2
15 WHERE STATUS <> 'DELETE'
16 GROUP BY MY_ID, MS_USER
17 ) inline_1
18 ON TABLE_extra.EXT_MY_ID = inline_1. MY_ID
19 WHERE TABLE_extra.EXTRA_ID = '123456';
Explained.
----------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 353K| 32M| | 162K (1)| 00:00:07 |
| 1 | NESTED LOOPS | | 353K| 32M| | 162K (1)| 00:00:07 |
| 2 | TABLE ACCESS BY INDEX ROWID| TABLE_EXTRA | 1 | 12 | | 1 (0)| 00:00:01 |
|* 3 | INDEX UNIQUE SCAN | TABLEEXT_PK | 1 | | | 0 (0)| 00:00:01 |
|* 4 | VIEW | | 353K| 27M| | 162K (1)| 00:00:07 |
| 5 | SORT UNIQUE | | 7071K| 229M| 497M| 162K (1)| 00:00:07 |
| 6 | UNION-ALL | | | | | | |
| 7 | SORT GROUP BY | | 3535K| 114M| 248M| 81047 (1)| 00:00:04 |
|* 8 | TABLE ACCESS FULL | TABLE_1 | 4999K| 162M| | 7362 (1)| 00:00:01 |
| 9 | SORT GROUP BY | | 3535K| 114M| 248M| 81047 (1)| 00:00:04 |
|* 10 | TABLE ACCESS FULL | TABLE_2 | 4999K| 162M| | 7362 (1)| 00:00:01 |
----------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("TABLE_EXTRA"."EXTRA_ID"='123456')
4 - filter("TABLE_EXTRA"."EXT_MY_ID"="INLINE_1"."MY_ID")
8 - filter("STATUS"<>'DELETE')
10 - filter("STATUS"<>'DELETE')
25 rows selected.
SQL> drop type inline_stuff_list;
Type dropped.
SQL> create or replace
2 type inline_stuff_row as object (
3 MY_ID VARCHAR2(20) ,
4 MS_USER VARCHAR2(50),
5 MIN_REVISION NUMBER(3,0),
6 MAX_REVISION NUMBER(3,0),
7 DATE_FROM DATE,
8 DATE_TO DATE
9 )
10 /
Type created.
SQL> create or replace
2 type inline_stuff_list as table of inline_stuff_Row
3 /
Type created.
SQL> @exp1
5 SELECT *
6 FROM TABLE_extra,
7 TABLE(CAST(MULTISET(
8 SELECT MY_ID, MS_USER, MIN(REVISION), MAX(REVISION), MIN(DATE_FROM), MAX(DATE_TO)
9 FROM TABLE_1
10 WHERE STATUS <> 'DELETE'
11 AND MY_ID = TABLE_extra.EXT_MY_ID
12 GROUP BY MY_ID, MS_USER
13 UNION
14 SELECT MY_ID, MS_USER, MIN(REVISION), MAX(REVISION), MIN(DATE_FROM), MAX(DATE_TO)
15 FROM TABLE_2
16 WHERE STATUS <> 'DELETE'
17 AND MY_ID = TABLE_extra.EXT_MY_ID
18 GROUP BY MY_ID, MS_USER)
19 as inline_stuff_list)) inline_results
20 where TABLE_extra.EXTRA_ID = '123456';
Explained.
------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 2 | 190 | 9 (23)| 00:00:01 |
| 1 | NESTED LOOPS | | 2 | 190 | 9 (23)| 00:00:01 |
| 2 | TABLE ACCESS BY INDEX ROWID | TABLE_EXTRA | 1 | 12 | 1 (0)| 00:00:01 |
|* 3 | INDEX UNIQUE SCAN | TABLEEXT_PK | 1 | | 0 (0)| 00:00:01 |
| 4 | VIEW | | 2 | 166 | 8 (25)| 00:00:01 |
| 5 | SORT UNIQUE | | 2 | 68 | 8 (25)| 00:00:01 |
| 6 | UNION-ALL | | | | | |
| 7 | SORT GROUP BY | | 1 | 34 | 4 (25)| 00:00:01 |
|* 8 | TABLE ACCESS BY INDEX ROWID| TABLE_1 | 1 | 34 | 3 (0)| 00:00:01 |
|* 9 | INDEX UNIQUE SCAN | TABLE1_PK | 1 | | 2 (0)| 00:00:01 |
| 10 | SORT GROUP BY | | 1 | 34 | 4 (25)| 00:00:01 |
|* 11 | TABLE ACCESS BY INDEX ROWID| TABLE_2 | 1 | 34 | 3 (0)| 00:00:01 |
|* 12 | INDEX UNIQUE SCAN | TABLE2_PK | 1 | | 2 (0)| 00:00:01 |
------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("TABLE_EXTRA"."EXTRA_ID"='123456')
8 - filter("STATUS"<>'DELETE')
9 - access("MY_ID"="TABLE_EXTRA"."EXT_MY_ID")
11 - filter("STATUS"<>'DELETE')
12 - access("MY_ID"="TABLE_EXTRA"."EXT_MY_ID")
28 rows selected.
SQL> SELECT *
2 FROM TABLE_extra,
3 TABLE(CAST(MULTISET(
4 SELECT MY_ID, MS_USER, MIN(REVISION), MAX(REVISION), MIN(DATE_FROM), MAX(DATE_TO)
5 FROM TABLE_1
6 WHERE STATUS <> 'DELETE'
7 AND MY_ID = TABLE_extra.EXT_MY_ID
8 GROUP BY MY_ID, MS_USER
9 UNION
10 SELECT MY_ID, MS_USER, MIN(REVISION), MAX(REVISION), MIN(DATE_FROM), MAX(DATE_TO)
11 FROM TABLE_2
12 WHERE STATUS <> 'DELETE'
13 AND MY_ID = TABLE_extra.EXT_MY_ID
14 GROUP BY MY_ID, MS_USER)
15 as inline_stuff_list)) inline_results
16 where TABLE_extra.EXTRA_ID = '123456';
EXTRA_ID EXT_MY_ID MY_ID
-------------------- -------------------- --------------------
MS_USER MIN_REVISION MAX_REVISION DATE_FROM DATE_TO
-------------------------------------------------- ------------ ------------ --------- ---------
123456 1006 1006
user 6 6 21-OCT-15 10-DEC-15