Database: Oracle 9.2.0.6 EE
OS:Solaris 9
I am trying to tune a query that is generated via Siebel Analytics. I am seeing a behaviour which is puzzling me but hopefully would be 'elementary' for someone like JPL.
The query is based on a total of 7 tables. If I comment out any 2 dimension tables, the query picks up the right index on the fact table. However, the moment I add another table to the query, the plan goes awry.
The query with 5 tables is as below:
select count(distinct decode( T30256.HEADER_FLG , 'N' , T30256.ROW_WID ) ) as c1,
T352305.DAY_DT as c2,
case when T44643.PRODUCT_CLASS_NAME = 'MobileSubscription' then T40081.ATTR15_CHAR_VAL else 'Unspecified' end as c3,
T352305.ROW_WID as c5
from
W_PRODUCT_D T30955,
W_PRDATTRNM_D T44643,
W_DAY_D T352305,
W_ORDERITEM_F T30256,
W_PRDATTR_D T40081
where ( T30955.ROW_WID = T44643.ROW_WID
and T30256.LAST_UPD_DT_WID = T352305.ROW_WID
and T30256.PROD_ATTRIB_WID = T40081.ROW_WID
and T30256.PROD_WID = T30955.ROW_WID
and T30955.PROD_NAME = 'Mobile Subscription'
and (case when T44643.PRODUCT_CLASS_NAME = 'MobileSubscription' then T40081.ATTR15_CHAR_VAL else 'Unspecified' end in ('BT150BB-18M', 'BT250BB-18M', 'BT50BB-18M', 'BT600BB-18M'))
and T352305.DAY_DT between TO_DATE('2008-09-27' , 'YYYY-MM-DD') - 7 and TO_DATE('2008-09-27' , 'YYYY-MM-DD') - 1
)
group by
T352305.ROW_WID, T352305.DAY_DT,
case when T44643.PRODUCT_CLASS_NAME = 'MobileSubscription' then T40081.ATTR15_CHAR_VAL else 'Unspecified' end
;
And the execution plan is as below:
----------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|
----------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 269 | 25824 | 18660 (3)|
| 1 | SORT GROUP BY | | 269 | 25824 | 18660 (3)|
| 2 | NESTED LOOPS | | 269 | 25824 | 18658 (3)|
| 3 | NESTED LOOPS | | 6826 | 579K| 4734 (3)|
| 4 | MERGE JOIN CARTESIAN | | 8 | 544 | 6 (17)|
| 5 | NESTED LOOPS | | 1 | 54 | 4 (25)|
| 6 | TABLE ACCESS BY INDEX ROWID| W_PRODUCT_D | 1 | 37 | 3 (34)|
|* 7 | INDEX RANGE SCAN | W_PRODUCT_D_M2 | 1 | | 2 (50)|
| 8 | TABLE ACCESS BY INDEX ROWID| W_PRDATTRNM_D | 1 | 17 | 2 (50)|
|* 9 | INDEX UNIQUE SCAN | W_PRDATTRNM_D_P1 | 1 | | |
| 10 | BUFFER SORT | | 8 | 112 | 4 (0)|
| 11 | TABLE ACCESS BY INDEX ROWID| W_DAY_D | 8 | 112 | 3 (34)|
|* 12 | INDEX RANGE SCAN | W_DAY_D_M39 | 8 | | 2 (50)|
| 13 | TABLE ACCESS BY INDEX ROWID | W_ORDERITEM_F | 849 | 16131 | 592 (3)|
|* 14 | INDEX RANGE SCAN | W_ORDERITEM_F_INDX9 | 852 | | 4 (25)|
|* 15 | INDEX RANGE SCAN | W_PRDATTR_D_M29_T1 | 1 | 9 | 3 (34)|
----------------------------------------------------------------------------------------------
Note how the dimension tables W_PRODUCT_D & W_DAY_D are joined using cartesian join before joining to the fact table W_ORDERITEM_F using the composite index 'W_ORDERITEM_F_INDX9'. This index consists of LAST_UPD_DT_WID, PROD_WID and ACTION_TYPE_WID, which are foreign keys to the dimension tables.
Now if I add one more table to the query:
select count(distinct decode( T30256.HEADER_FLG , 'N' , T30256.ROW_WID ) ) as c1,
T352305.DAY_DT as c2,
case when T44643.PRODUCT_CLASS_NAME = 'MobileSubscription' then T40081.ATTR15_CHAR_VAL else 'Unspecified' end as c3,
T30371.X_BT_DLR_GROUP as c4,
T352305.ROW_WID as c5
from W_PRODUCT_D T30955,
W_PRDATTRNM_D T44643,
W_DAY_D T352305,
W_ORDERITEM_F T30256,
W_ORDER_D T30371,
W_PRDATTR_D T40081
where ( T30955.ROW_WID = T44643.ROW_WID
and T30256.LAST_UPD_DT_WID = T352305.ROW_WID
and T30256.PROD_ATTRIB_WID = T40081.ROW_WID
and T30256.PROD_WID = T30955.ROW_WID
and T30256.ORDER_WID = T30371.ROW_WID
and T30955.PROD_NAME = 'Mobile Subscription'
and T30371.STATUS_CD = 'Complete'
and T30371.ORDER_TYPE = 'Sales Order'
and (case when T44643.PRODUCT_CLASS_NAME = 'MobileSubscription' then T40081.ATTR15_CHAR_VAL else 'Unspecified' end in ('BT150BB-18M', 'BT250BB-18M', 'BT50BB-18M', 'BT600BB-18M'))
and T352305.DAY_DT between TO_DATE('2008-09-27' , 'YYYY-MM-DD') - 7 and TO_DATE('2008-09-27' , 'YYYY-MM-DD') - 1
)
group by T30371.X_BT_DLR_GROUP, T352305.ROW_WID, T352305.DAY_DT,
case when T44643.PRODUCT_CLASS_NAME = 'MobileSubscription' then T40081.ATTR15_CHAR_VAL else 'Unspecified' end;
I have added a single table W_ORDER_D to the query, and the execution plan is:
-----------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|
-----------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 44 | 6336 | 78695 (3)|
| 1 | SORT GROUP BY | | 44 | 6336 | 78695 (3)|
| 2 | NESTED LOOPS | | 44 | 6336 | 78694 (3)|
| 3 | NESTED LOOPS | | 269 | 27707 | 78145 (3)|
|* 4 | HASH JOIN | | 6826 | 626K| 64221 (3)|
| 5 | TABLE ACCESS BY INDEX ROWID | W_DAY_D | 8 | 112 | 4 (25)|
|* 6 | INDEX RANGE SCAN | W_DAY_D_M39 | 1 | | 3 (34)|
| 7 | TABLE ACCESS BY INDEX ROWID | W_ORDERITEM_F | 86886 | 2206K| 64197 (3)|
| 8 | NESTED LOOPS | | 87004 | 6797K| 64200 (3)|
| 9 | NESTED LOOPS | | 1 | 54 | 4 (25)|
| 10 | TABLE ACCESS BY INDEX ROWID| W_PRODUCT_D | 1 | 37 | 3 (34)|
|* 11 | INDEX RANGE SCAN | W_PRODUCT_D_M2 | 1 | | 2 (50)|
| 12 | TABLE ACCESS BY INDEX ROWID| W_PRDATTRNM_D | 1 | 17 | 2 (50)|
|* 13 | INDEX UNIQUE SCAN | W_PRDATTRNM_D_P1 | 1 | | |
|* 14 | INDEX RANGE SCAN | W_ORDERITEM_F_N6 | 86886 | | 212 (18)|
|* 15 | INDEX RANGE SCAN | W_PRDATTR_D_M29_T1 | 1 | 9 | 3 (34)|
|* 16 | INDEX RANGE SCAN | W_ORDER_D_N6 | 1 | 41 | 3 (34)|
-----------------------------------------------------------------------------------------------
Now CBO doesn't choose the composite index and the cost also has increased to 78695. But if I simply add an /*+ORDERED*/ hint to the above query, so that it should join the dimension tables before joining to fact table, then the cost drops to 20913. This means that CBO is not choosing the plan with the lowest cost. I tried increasing the optimizer_max_permutations to 80000, setting session level optimizer_dynamic_sampling to 8 (just to see if it works), but no success.
Could you please advise how to overcome this problem?
Many thanks.