I have a problem with a query that uses a FULL TABLE SCAN.
When this query runs on our UAT env, it uses a TABLE ACCESS BY INDEX ROWID, but in prod it uses FULL TABLE SCAN. UAT runs much better than PROD.
We have the same tables and indexes structure in prod and uat.
I already tried rebuilding and recreating the indexes but the same explain plan is used.
Table and indexes statics were also updated.
Can you help me to make this query use INDEX access instead of FUll table access?
Please see below the explain plan of our prod and uat.
EXPLAIN PLAN PROD ===================================================================== SQL> explain plan for SELECT ASV_ODC_BRANCH.CODE, ASV_ODC_BRANCH.DESCRIPTION, ASV_ODC_BRANCH.BRSTN, DEB.VIEW_DORMANT.ACCTNO AS DORMANT_ACCT, DEB.VIEW_DORMANT.SHORTNAME AS DORMANT_NAME, DEB.VIEW_DORMANT.OPID_ENTRY, DEB.CUSTOMER.CUSTOMERNO, DEB.CUSTOMER.TIME_STAMP_ENTRY FROM ASV_ODC_BRANCH, DEB.VIEW_DORMANT, DEB.CUSTOMER WHERE trim(ASV_ODC_BRANCH.CODE) = decode(SUBSTR(DEB.VIEW_DORMANT.ACCTNO, 3, 1) || SUBSTR(DEB.VIEW_DORMANT.ACCTNO, 7, 1), ’29’, SUBSTR(DEB.VIEW_DORMANT.ACCTNO, 4, 3), SUBSTR(DEB.VIEW_DORMANT.ACCTNO, 3, 3)) AND DEB.VIEW_DORMANT.ACCTNO = DEB.CUSTOMER.CUSTOMERNO AND (DEB.VIEW_DORMANT.ACCTNO = :Xacct) ORDER BY ASV_ODC_BRANCH.CODE, DORMANT_ACCT; Explained. PLAN_TABLE_OUTPUT | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| | 0 | SELECT STATEMENT | | 3 | 489 | 3601 (2)| | 1 | SORT ORDER BY | | 3 | 489 | 3601 (2)| | 2 | HASH JOIN | | 3 | 489 | 3600 (2)| | 3 | MERGE JOIN CARTESIAN | | 1 | 90 | 3595 (2)| | 4 | NESTED LOOPS | | 1 | 66 | 3592 (2)| | 5 | **TABLE ACCESS FULL** | ACCOUNT | 1 | 56 | 3590 (2)| | 6 | TABLE ACCESS BY INDEX ROWID| EXTENSION1 | 1 | 10 | 2 (0)| | 7 | INDEX UNIQUE SCAN | PKEXT10 | 1 | | 1 (0)| | 8 | BUFFER SORT | | 1 | 24 | 3593 (2)| | 9 | TABLE ACCESS BY INDEX ROWID| CUSTOMER | 1 | 24 | 3 (0)| | 10 | INDEX RANGE SCAN | UXCUST1 | 1 | | 2 (0)| | 11 | TABLE ACCESS FULL | ASV_ODC_BRANCH | 334 | 24382 | 5 (0)| **EXPLAIN PLAN UAT** ====================================================================================== SQL> explain plan for SELECT ASV_ODC_BRANCH.CODE, ASV_ODC_BRANCH.DESCRIPTION, ASV_ODC_BRANCH.BRSTN, DEB.VIEW_DORMANT.ACCTNO AS DORMANT_ACCT, DEB.VIEW_DORMANT.SHORTNAME AS DORMANT_NAME, DEB.VIEW_DORMANT.OPID_ENTRY, DEB.CUSTOMER.CUSTOMERNO, DEB.CUSTOMER.TIME_STAMP_ENTRY FROM ASV_ODC_BRANCH, DEB.VIEW_DORMANT, DEB.CUSTOMER WHERE trim(ASV_ODC_BRANCH.CODE) = decode(SUBSTR(DEB.VIEW_DORMANT.ACCTNO, 3, 1) || SUBSTR(DEB.VIEW_DORMANT.ACCTNO, 7, 1), ’29’, SUBSTR(DEB.VIEW_DORMANT.ACCTNO, 4, 3), SUBSTR(DEB.VIEW_DORMANT.ACCTNO, 3, 3)) AND DEB.VIEW_DORMANT.ACCTNO = DEB.CUSTOMER.CUSTOMERNO AND (DEB.VIEW_DORMANT.ACCTNO = :Xacct) ORDER BY ASV_ODC_BRANCH.CODE, DORMANT_ACCT; Explained. SQL> / PLAN_TABLE_OUTPUT | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| | 0 | SELECT STATEMENT | | 5 | 5930 | 19 (11)| | 1 | SORT ORDER BY | | 5 | 5930 | 19 (11)| | 2 | HASH JOIN | | 5 | 5930 | 18 (6)| | 3 | MERGE JOIN CARTESIAN | | 2 | 2220 | 12 (0)| | 4 | NESTED LOOPS | | 1 | 1085 | 9 (0)| | 5 | **TABLE ACCESS BY INDEX ROWID**| ACCOUNT | 1 | 57 | 7 (0)| | 6 | INDEX SKIP SCAN | UXACCT2 | 1 | | 6 (0)| | 7 | TABLE ACCESS BY INDEX ROWID| EXTENSION1 | 1 | 1028 | 2 (0)| | 8 | INDEX UNIQUE SCAN | PKEXT10 | 1 | | 1 (0)| | 9 | BUFFER SORT | | 1 | 25 | 10 (0)| | 10 | TABLE ACCESS BY INDEX ROWID| CUSTOMER | 1 | 25 | 3 (0)| | 11 | INDEX RANGE SCAN | UXCUST1 | 1 | | 2 (0)| | 12 | TABLE ACCESS FULL | ASV_ODC_BRANCH | 336 | 25536 | 5 (0)|