Skip to Main Content

Breadcrumb

Question and Answer

Tom Kyte

Thanks for the question, Matt.

Asked: August 28, 2001 - 10:04 am UTC

Last updated: August 28, 2001 - 2:40 pm UTC

Version: 8.1.7

Viewed 1000+ times

You Asked


I have 3 tables:

create table fk_tab_master( fk_refno NUMBER NOT NULL PRIMARY KEY )

create table fk_tab_detail( fk_det_refno NUMBER NOT NULL PRIMARY KEY,
fk_refno NUMBER NOT NULL REFERENCES fk_tab_master( fk_refno ) )

create table drive_tab ( drive_refno NUMBER NOT NULL PRIMARY KEY,
fk_det_refno NUMBER NULL REFERENCES fk_tab_detail( fk_det_refno ) )

When running the following SQL I get:

select count(*)
from fk_tab_master mst,
fk_tab_detail det,
drive_tab drv
Where mst.fk_refno = det.fk_refno
and drv.fk_det_refno = det.fk_det_refno (+)

SELECT STATEMENT Optimizer=CHOOSE
NESTED LOOPS
NESTED LOOPS (OUTER)
TABLE ACCESS (FULL) OF DRIVE_TAB
TABLE ACCESS (BY INDEX ROWID) OF FK_TAB_DETAIL
INDEX (UNIQUE SCAN) OF SYS_C0078766 (UNIQUE)
INDEX (UNIQUE SCAN) OF SYS_C0078761 (UNIQUE)


select /*+ push_join_pred( il1 ) */ count(*)
from ( select mst.fk_refno,
det.fk_det_refno
from fk_tab_master mst,
fk_tab_detail det
where mst.fk_refno = det.fk_refno
) il1,
drive_tab drv
where drv.fk_det_refno = il1.fk_det_refno (+)


SELECT STATEMENT Optimizer=CHOOSE
MERGE JOIN (OUTER)
SORT (JOIN)
TABLE ACCESS (FULL) OF DRIVE_TAB
SORT (JOIN)
VIEW
NESTED LOOPS
TABLE ACCESS (FULL) OF FK_TAB_DETAIL
INDEX (UNIQUE SCAN) OF SYS_C0078761 (UNIQUE)


The predicate from the outer query doesn't get pushed into
the inline view.

This is a much simplified example to the one I am using.

Also, could you point me in the direction of a document explaining
how to gauge the optimal HASH_AREA_SIZE for an instance.





and Tom said...

We'll, we don't push the predicate down because that would change the meaning of the query in this case. You seem to thing that your two queries should be equivalent -- but they are not. Consider (assume your tables were empty):

ops$tkyte@ORA815> insert into fk_tab_master values ( 1 );

1 row created.

ops$tkyte@ORA815>
ops$tkyte@ORA815> insert into drive_tab values ( 100, null );

1 row created.

ops$tkyte@ORA815>
ops$tkyte@ORA815> select count(*)
2 from fk_tab_master mst,
3 fk_tab_detail det,
4 drive_tab drv
5 Where mst.fk_refno = det.fk_refno
6 and drv.fk_det_refno = det.fk_det_refno (+)
7 /

COUNT(*)
----------
0

ops$tkyte@ORA815>
ops$tkyte@ORA815> select /*+ push_join_pred( il1 ) */ count(*)
2 from ( select mst.fk_refno,
3 det.fk_det_refno
4 from fk_tab_master mst,
5 fk_tab_detail det
6 where mst.fk_refno = det.fk_refno
7 ) il1,
8 drive_tab drv
9 where drv.fk_det_refno = il1.fk_det_refno (+)
10 /

COUNT(*)
----------
1

They give different answers. Looking at the query:


select count(*)
from fk_tab_master mst,
fk_tab_detail det,
drive_tab drv
Where mst.fk_refno = det.fk_refno
and drv.fk_det_refno = det.fk_det_refno (+)

I would say it was in improper use of an outer join. In this case -- if we find a row in DRV that doesn't have a match in DET and we "make up a row" for it -- the predicate "Where mst.fk_refno = det.fk_refno" will immediately drop it for us (nothing is ever equal to NULL). Hence the outer join in this example is only useful to negatively impact performance (it limits the plans available to the optimizer but its existence does not change the outcome ever).

You can drop the (+) in that query and you'll have exactly the same query!

the second query however, outer joins to the JOIN. As demonstrated above -- the predicate "Where mst.fk_refno = det.fk_refno" no longer drops the row from the result set since IT is done first and the result of that is outer joined to the other table.


See
</code> http://metalink.oracle.com/metalink/plsql/ml2_documents.showDocument?p_database_id=NOT&p_id=41954.1 <code>
for the hash_area_size. It describes the process and how this parameter is used.

Rating

  (1 rating)

Is this answer out of date? If it is, please let us know via a Comment

Comments

Matt, August 28, 2001 - 1:28 pm UTC

I think in this case I gave a bad example to compare with.
I am still surprised that the plan for the inline view
did a full table scan on fk_tab_detail when it could have gone
in on the index.

Tom Kyte
August 28, 2001 - 2:40 pm UTC

If you mean this query and plan:

select /*+ push_join_pred( il1 ) */ count(*)
from ( select mst.fk_refno,
det.fk_det_refno
from fk_tab_master mst,
fk_tab_detail det
where mst.fk_refno = det.fk_refno
) il1,
drive_tab drv
where drv.fk_det_refno = il1.fk_det_refno (+)


SELECT STATEMENT Optimizer=CHOOSE
MERGE JOIN (OUTER)
SORT (JOIN)
TABLE ACCESS (FULL) OF DRIVE_TAB
SORT (JOIN)
VIEW
NESTED LOOPS
TABLE ACCESS (FULL) OF FK_TAB_DETAIL
INDEX (UNIQUE SCAN) OF SYS_C0078761 (UNIQUE)



Well, look at the inline view:

select mst.fk_refno, det.fk_det_refno
from fk_tab_master mst,
fk_tab_detail det
where mst.fk_refno = det.fk_refno

in light of the fact that there are no PREDICATES other then the join -- one of the two tables would be full scanned. There is no "index access" to either table that could be used to START the query. The index into the other table was used (because it made sense).

More to Explore

Performance

Get all the information about database performance in the Database Performance guide.