Strange behavior on inline join
Jimmy Lee, March 01, 2004 - 5:31 am UTC
Hi Tom,
Here is similiar case, yet temporary table comes some interesting result. Is there any reason why inline join could not run as fast as PK of temporary table? Thanks.
Create table Objects (GUID varchar2(20) not null, CreateDt date, primary key(GUID));
Create table ObjectContent (GUID varchar2(20) not null, Content clob, primary key(GUID), FOREIGN KEY(GUID) REFERENCES Objects(GUID));
CREATE INDEX OBJGLUEX ON ObjectContent(Content) indextype IS ctxsys.context parameters ('lexer my_lexer');
Case 1: Straightforward query on table join, it takes more than 30 seconds...
select /*+ FIRST_ROWS */ * from (
select /*+ FIRST_ROWS */ a.*, rownum rnum from (
select /*+ FIRST_ROWS */ distinct o.GUID, o.CreateDT
from Objects o, ObjectContent oc
where oc.guid=o.guid
and contains (oc.Content, 'Power') > 0 order by o.CreateDT desc
) a
where rownum <= 20
) where rnum > 0;
20 rows selected.
Execution plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=HINT: FIRST_ROWS (Cost=6 Card=1 Bytes=60)
1 0 VIEW (Cost=6 Card=1 Bytes=60)
2 1 COUNT (STOPKEY)
3 2 VIEW (Cost=6 Card=1 Bytes=47)
4 3 SORT (UNIQUE STOPKEY) (Cost=4 Card=1 Bytes=94)
5 4 NESTED LOOPS (Cost=3 Card=1 Bytes=94)
6 5 TABLE ACCESS (BY INDEX ROWID) OF 'OBJECTCONTENT' (Cost=2 Card=1 Bytes=47)
7 6 DOMAIN INDEX OF 'OBJGLUEX' (Cost=0)
8 5 TABLE ACCESS (BY INDEX ROWID) OF 'OBJECTS' (Cost=1 Card=1 Bytes=47)
9 8 INDEX (UNIQUE SCAN) OF 'SYS_C003964' (UNIQUE)
Statistic
----------------------------------------------------------
4907 recursive calls
0 db block gets
68956 consistent gets
9934 physical reads
0 redo size
1702 bytes sent via SQL*Net to client
514 bytes received via SQL*Net from client
3 SQL*Net roundtrips to/from client
1 sorts (memory)
0 sorts (disk)
20 rows processed
Case 2: Straightforward query on table join, it takes totally less than 10 seconds...
create global temporary table Temp (rid rowid not null, primary key(rid) ) on commit delete rows ;
insert into Temp select rowid from ObjectContent oc where contains (oc.Content, 'Power') > 0;
select /*+ FIRST_ROWS */ * from (
select /*+ FIRST_ROWS */ a.*, rownum rnum from (
select /*+ FIRST_ROWS */ distinct o.GUID, o.CreateDT
from Objects o, ObjectContent oc, Temp t
where oc.guid=o.guid and oc.rowid = t.rid
order by o.CreateDT desc
) a
where rownum <= 20
) where rnum > 0;
15317 row created.
Execution plan
----------------------------------------------------------
0 INSERT STATEMENT Optimizer=CHOOSE (Cost=2 Card=1 Bytes=9)
1 0 DOMAIN INDEX OF 'OBJGLUEX' (Cost=0 Card=1 Bytes=9)
Statistic
----------------------------------------------------------
4963 recursive calls
47689 db block gets
5571 consistent gets
47 physical reads
4623532 redo size
630 bytes sent via SQL*Net to client
597 bytes received via SQL*Net from client
3 SQL*Net roundtrips to/from client
2 sorts (memory)
0 sorts (disk)
15317 rows processed
20 rows selected.
Execution plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=HINT: FIRST_ROWS (Cost=302 Card=20 Bytes=1200)
1 0 VIEW (Cost=302 Card=20 Bytes=1200)
2 1 COUNT (STOPKEY)
3 2 VIEW (Cost=302 Card=8168 Bytes=383896)
4 3 SORT (UNIQUE STOPKEY) (Cost=153 Card=8168 Bytes=808632)
5 4 NESTED LOOPS (Cost=4 Card=8168 Bytes=808632)
6 5 NESTED LOOPS (Cost=4 Card=991351 Bytes=91204292)
7 6 INDEX (FAST FULL SCAN) OF 'OBX' (NON-UNIQUE) (Cost=4 Card=76453 Bytes=3593291)
8 6 INDEX (UNIQUE SCAN) OF 'SYS_C003977' (UNIQUE)
9 5 INDEX (UNIQUE SCAN) OF 'SYS_C004117' (UNIQUE)
Statistic
----------------------------------------------------------
0 recursive calls
0 db block gets
79417 consistent gets
539 physical reads
0 redo size
1702 bytes sent via SQL*Net to client
514 bytes received via SQL*Net from client
3 SQL*Net roundtrips to/from client
1 sorts (memory)
0 sorts (disk)
20 rows processed
March 01, 2004 - 8:26 am UTC
select /*+ first_rows */ guid, createdt
from objects
where guid in (select guid
from objectContent
where contains( content, 'Power' ) > 0)
order by createDT desc
first_rows on that inner most query in your case (with a distinct and a join) could be "the reason". assuming that guid is the primary key of objects (seems reasonable), the distinct isn't necessary and it looks like you just want "in"
Another way to do it would be:
select /*+ FIRST_ROWS */ * from (
select /*+ FIRST_ROWS */ a.*, rownum rnum from (
select /*+ FIRST_ROWS */ o.GUID, o.CreateDT
from Objects o,
(select distinct guid from ObjectContent where contains(content,'Power') > 0 ) oc
where oc.guid=o.guid
order by o.CreateDT desc
) a
where rownum <= 20
) where rnum > 0;
which pretty much mimicks your approach -- in fact, yours could probably go faster even if you inserted the distincted GUID's not rowids.
Jimmy Lee, March 02, 2004 - 11:13 pm UTC
Hi Tom,
You precious advice just happens to be the weird point about GUID (PK of table) and rowid (physical row id). I tried lots combinations on 'first_rows' hint, 'distinct' keyword, 'in' join or subquery. None of them could be even close to the speed of using rowid as PK of temporary table.
In other words, using rowid, instead of GUID, as primary key of temporary table has HUGE different on the inserting temporary table. Once temporary table was generated, no matter using GUID or rowid as primary key, the speed on table join was not much different. It matches the slow-speed observation on "select guid from objectContent where contains( content, 'Power' ) > 0".
The performance bottleneck seems to be that ctxsys.context stores rowid instead of PK inside DR$ tables. Then it takes tons of time to looking up PK by rowid.
I tried to use IOT table but ctxsys.context could not be secondary index of IOT table.
Oracle version is 9.2.0.4 (result is as the same as 9.2.0.1).
Any idea? Thanks so much on your help
March 03, 2004 - 9:39 am UTC
try
select /*+ FIRST_ROWS */ * from (
select /*+ FIRST_ROWS */ a.*, rownum rnum from (
select /*+ FIRST_ROWS */ o.GUID, o.CreateDT
from Objects o,
(select distinct rowid rid from ObjectContent where
contains(content,'Power') > 0 and rownum >= 0 ) oc
where oc.rowid=o.rid
order by o.CreateDT desc
) a
where rownum <= 20
) where rnum > 0;
Jimmy Lee, March 03, 2004 - 11:32 pm UTC
Hi Tom,
Retrive rowid in subquery to join with original table, just like your sample, is just slow as others. Using rowid as primary key of tamporary table still run much faster (about 1:4 ratio or more). Reasonable it is to retrieve all rows if we need to sort query result, yet doubtful it is to understand the performance difference.
Thanks.