I'm having a couple of issues with a query, and I can't figure out the best way to reach a solution.
Platform Information
Windows Server 2003 R2
Oracle 10.2.0.4
Optimizer Settings
SQL > show parameter optimizer
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
optimizer_dynamic_sampling integer 2
optimizer_features_enable string 10.2.0.4
optimizer_index_caching integer 90
optimizer_index_cost_adj integer 30
optimizer_mode string ALL_ROWS
optimizer_secure_view_merging boolean TRUE
The query below, is a simple "Top N" query, where the top result is returned. Here it is, with bind variables in the same location as the application code:
SELECT PRODUCT_DESC
FROM
(
SELECT PRODUCT_DESC
, COUNT(*) AS CNT
FROM USER_VISITS
JOIN PRODUCT ON PRODUCT.PRODUCT_OID = USER_VISITS.PRODUCT_OID
WHERE PRODUCT.PRODUCT_DESC != 'Home'
AND VISIT_DATE
BETWEEN
ADD_MONTHS
(
TRUNC
(
TO_DATE
(
:vCurrentYear
, 'YYYY'
)
, 'YEAR'
)
, 3*(:vCurrentQuarter-1)
)
AND
ADD_MONTHS
(
TRUNC
(
TO_DATE
(
:vCurrentYear
, 'YYYY'
)
, 'YEAR'
)
, 3*:vCurrentQuarter
) - INTERVAL '1' DAY
GROUP BY PRODUCT_DESC
ORDER BY CNT DESC
)
WHERE ROWNUM <= 1;
Explain Plan
The explain plan I receive when running the query above.
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | OMem | 1Mem | Used-Mem |
--------------------------------------------------------------------------------------------------------------------------------------------------------
|* 1 | COUNT STOPKEY | | 1 | | 1 |00:00:34.92 | 66343 | | | |
| 2 | VIEW | | 1 | 1 | 1 |00:00:34.92 | 66343 | | | |
|* 3 | FILTER | | 1 | | 1 |00:00:34.92 | 66343 | | | |
| 4 | SORT ORDER BY | | 1 | 1 | 1 |00:00:34.92 | 66343 | 2048 | 2048 | 2048 (0)|
| 5 | SORT GROUP BY NOSORT | | 1 | 1 | 27 |00:00:34.92 | 66343 | | | |
| 6 | NESTED LOOPS | | 1 | 2 | 12711 |00:00:34.90 | 66343 | | | |
| 7 | TABLE ACCESS BY INDEX ROWID| PRODUCT | 1 | 74 | 77 |00:00:00.01 | 44 | | | |
|* 8 | INDEX FULL SCAN | PRODUCT_PRODDESCHAND_UNQ | 1 | 1 | 77 |00:00:00.01 | 1 | | | |
|* 9 | INDEX FULL SCAN | USER_VISITS#PK | 77 | 2 | 12711 |00:00:34.88 | 66299 | | | |
--------------------------------------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter(ROWNUM<=1)
3 - filter(ADD_MONTHS(TRUNC(TO_DATE(TO_CHAR(:VCURRENTYEAR),'YYYY'),'fmyear'),3*(:VCURRENTQUARTER-1))<=ADD_MONTHS(TRUNC(TO_DATE(TO_CHAR(:VCURR
ENTYEAR),'YYYY'),'fmyear'),3*:VCURRENTQUARTER)-INTERVAL'+01 00:00:00' DAY(2) TO SECOND(0))
8 - filter("PRODUCT"."PRODUCT_DESC"<>'Home')
9 - access("USER_VISITS"."VISIT_DATE">=ADD_MONTHS(TRUNC(TO_DATE(TO_CHAR(:VCURRENTYEAR),'YYYY'),'fmyear'),3*(:VCURRENTQUARTER-1)) AND
"USER_VISITS"."PRODUCT_OID"="PRODUCT"."PRODUCT_OID" AND "USER_VISITS"."VISIT_DATE"<=ADD_MONTHS(TRUNC(TO_DATE(TO_CHAR(:VCURRENTYEAR),'YYYY')
,'fmyear'),3*:VCURRENTQUARTER)-INTERVAL'+01 00:00:00' DAY(2) TO SECOND(0))
filter(("USER_VISITS"."VISIT_DATE">=ADD_MONTHS(TRUNC(TO_DATE(TO_CHAR(:VCURRENTYEAR),'YYYY'),'fmyear'),3*(:VCURRENTQUARTER-1)) AND
"USER_VISITS"."VISIT_DATE"<=ADD_MONTHS(TRUNC(TO_DATE(TO_CHAR(:VCURRENTYEAR),'YYYY'),'fmyear'),3*:VCURRENTQUARTER)-INTERVAL'+01 00:00:00' DAY(2)
TO SECOND(0) AND "USER_VISITS"."PRODUCT_OID"="PRODUCT"."PRODUCT_OID"))
Row Source Generation
TKPROF Row Source Generation
call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.00 0.00 0 0 0 0
Execute 1 0.00 0.01 0 0 0 0
Fetch 2 35.10 35.13 0 66343 0 1
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 4 35.10 35.14 0 66343 0 1
Misses in library cache during parse: 1
Misses in library cache during execute: 1
Optimizer mode: ALL_ROWS
Parsing user id: 62
Rows Row Source Operation
------- ---------------------------------------------------
1 COUNT STOPKEY (cr=66343 pr=0 pw=0 time=35132008 us)
1 VIEW (cr=66343 pr=0 pw=0 time=35131996 us)
1 FILTER (cr=66343 pr=0 pw=0 time=35131991 us)
1 SORT ORDER BY (cr=66343 pr=0 pw=0 time=35131936 us)
27 SORT GROUP BY NOSORT (cr=66343 pr=0 pw=0 time=14476309 us)
12711 NESTED LOOPS (cr=66343 pr=0 pw=0 time=22921810 us)
77 TABLE ACCESS BY INDEX ROWID PRODUCT (cr=44 pr=0 pw=0 time=3674 us)
77 INDEX FULL SCAN PRODUCT_PRODDESCHAND_UNQ (cr=1 pr=0 pw=0 time=827 us)(object id 52355)
12711 INDEX FULL SCAN USER_VISITS#PK (cr=66299 pr=0 pw=0 time=44083746 us)(object id 52949)
However when I run the query with an ALL_ROWS hint I receive this explain plan (reasoning for this can be found here Jonathan's Lewis' response:
http://www.freelists.org/post/oracle-l/ORDER-BY-and-first-rows-10-madness,4):
---------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 39 | 223 (25)| 00:00:03 |
|* 1 | COUNT STOPKEY | | | | | |
| 2 | VIEW | | 1 | 39 | 223 (25)| 00:00:03 |
|* 3 | FILTER | | | | | |
| 4 | SORT ORDER BY | | 1 | 49 | 223 (25)| 00:00:03 |
| 5 | HASH GROUP BY | | 1 | 49 | 223 (25)| 00:00:03 |
|* 6 | HASH JOIN | | 490 | 24010 | 222 (24)| 00:00:03 |
|* 7 | TABLE ACCESS FULL | PRODUCT | 77 | 2849 | 2 (0)| 00:00:01 |
|* 8 | INDEX FAST FULL SCAN| USER_VISITS#PK | 490 | 5880 | 219 (24)| 00:00:03 |
---------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter(ROWNUM<=1)
3 - filter(ADD_MONTHS(TRUNC(TO_DATE(:VCURRENTYEAR,'YYYY'),'fmyear'),3*(TO_NUMBER(:
VCURRENTQUARTER)-1))<=ADD_MONTHS(TRUNC(TO_DATE(:VCURRENTYEAR,'YYYY'),'fmyear'),3*TO_N
UMBER(:VCURRENTQUARTER))-INTERVAL'+01 00:00:00' DAY(2) TO SECOND(0))
6 - access("USER_VISITS"."PRODUCT_OID"="PRODUCT"."PRODUCT_OID")
7 - filter("PRODUCT"."PRODUCT_DESC"<>'Home')
8 - filter("USER_VISITS"."VISIT_DATE">=ADD_MONTHS(TRUNC(TO_DATE(:VCURRENTYEAR,'YYY
Y'),'fmyear'),3*(TO_NUMBER(:VCURRENTQUARTER)-1)) AND
"USER_VISITS"."VISIT_DATE"<=ADD_MONTHS(TRUNC(TO_DATE(:VCURRENTYEAR,'YYYY'),'fmyear'),
3*TO_NUMBER(:VCURRENTQUARTER))-INTERVAL'+01 00:00:00' DAY(2) TO SECOND(0))
And the TKPROF Row Source Generation:
call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.00 0.00 0 0 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 3 0.51 0.51 0 907 0 27
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 5 0.51 0.51 0 907 0 27
Misses in library cache during parse: 1
Misses in library cache during execute: 1
Optimizer mode: ALL_ROWS
Parsing user id: 62
Rows Row Source Operation
------- ---------------------------------------------------
27 FILTER (cr=907 pr=0 pw=0 time=513472 us)
27 SORT ORDER BY (cr=907 pr=0 pw=0 time=513414 us)
27 HASH GROUP BY (cr=907 pr=0 pw=0 time=512919 us)
12711 HASH JOIN (cr=907 pr=0 pw=0 time=641130 us)
77 TABLE ACCESS FULL PRODUCT (cr=5 pr=0 pw=0 time=249 us)
22844 INDEX FAST FULL SCAN USER_VISITS#PK (cr=902 pr=0 pw=0 time=300356 us)(object id 52949)
The query with the ALL_ROWS hint returns data instantly, while the other one takes about 70 times as long.
Interestingly enough BOTH queries generate plans with estimates that are WAY off. The first plan is estimating 2 rows, while the second plan is estimating 490 rows. However the real number of rows is correctly reported in the Row Source Generation as 12711 (after the join operation).
TABLE_NAME NUM_ROWS BLOCKS
------------------------------ ---------- ----------
USER_VISITS 196044 1049
INDEX_NAME BLEVEL LEAF_BLOCKS DISTINCT_KEYS CLUSTERING_FACTOR LAST_ANALYZED
------------------------------ ---------- ----------- ------------- ----------------- -------------------
USER_VISITS#PK 2 860 196002 57761 07/24/2009 13:17:59
COLUMN_NAME NUM_DISTINCT LOW_VALUE HIGH_VALUE DENSITY NUM_NULLS HISTOGRAM
------------------------------ ------------ -------------------- -------------------- -------------------------------- ---------- ---------------
VISIT_DATE 195900 786809010E0910 786D0609111328 .0000051046452272 0 NONE
I don't know how the first one is estimating 2 rows, but I can compute the second's cardinality estimates by assuming a 5% selectivity for the TO_DATE() functions:
SQL > SELECT ROUND(0.05*0.05*196044) FROM DUAL;
ROUND(0.05*0.05*196044)
-----------------------
490
However, removing the bind variables (and clearing the shared pool), does not change the cardinality estimates at all.
I would like to avoid hinting this plan if possible and that is why I'm looking for advice. I also have a followup question.
Edited by: Centinul on Sep 20, 2009 4:10 PM
See my last post for 11.2.0.1 update.