Hi Tom,
I have table with 1 755 001 rows and functional-based index as follows:
create table OC_YKB.TAB_TEST3 (guid number(16), pan varchar2(19 byte));
create sequence oc_ykb.sq_test3;
INSERT /*+ APPEND */ INTO OC_YKB.TAB_TEST3 select oc_ykb.sq_test3.NEXTVAL, 536238||lpad(oc_ykb.sq_test3.NEXTVAL,10,0) from dual connect by level <= 1000000;
INSERT /*+ APPEND */ INTO OC_YKB.TAB_TEST3 select oc_ykb.sq_test3.NEXTVAL, 516823||lpad(oc_ykb.sq_test3.NEXTVAL,10,0) from dual connect by level <= 500000;
INSERT /*+ APPEND */ INTO OC_YKB.TAB_TEST3 select oc_ykb.sq_test3.NEXTVAL, 550222||lpad(oc_ykb.sq_test3.NEXTVAL,10,0) from dual connect by level <= 200000;
INSERT /*+ APPEND */ INTO OC_YKB.TAB_TEST3 select oc_ykb.sq_test3.NEXTVAL, 550358||lpad(oc_ykb.sq_test3.NEXTVAL,10,0) from dual connect by level <= 50000;
INSERT /*+ APPEND */ INTO OC_YKB.TAB_TEST3 select oc_ykb.sq_test3.NEXTVAL, 558649||lpad(oc_ykb.sq_test3.NEXTVAL,10,0) from dual connect by level <= 5000;
INSERT /*+ APPEND */ INTO OC_YKB.TAB_TEST3 select oc_ykb.sq_test3.NEXTVAL, 539638||lpad(oc_ykb.sq_test3.NEXTVAL,10,0) from dual connect by level <= 1;
CREATE INDEX OC_YKB.IX_TAB_TEST_FUNC_1 ON OC_YKB.TAB_TEST3
(SUBSTR(pan, 1, 6));
exec DBMS_STATS.GATHER_TABLE_STATS(ownname => 'OC_YKB' , tabname => 'TAB_TEST3', cascade => true);
Index works for this queries:
SELECT SUBSTR(PAN, 1, 6) FROM OC_YKB.TAB_TEST3 WHERE SUBSTR(PAN, 1, 6) = '539638';
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 7 (100)| |
|* 1 | INDEX RANGE SCAN| IX_TAB_TEST_FUNC_1 | 1449 | 10143 | 7 (0)| 00:00:01 |
SELECT *
FROM OC_YKB.TAB_TEST3
WHERE SUBSTR(PAN, 1, 6) =
( SELECT '539638' FROM OC_YKB.TAB_TEST3 WHERE SUBSTR(PAN, 1, 6) = '539638'
);
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 17 (100)| |
| 1 | TABLE ACCESS BY INDEX ROWID| TAB_TEST3 | 1449 | 42021 | 17 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | IX_TAB_TEST_FUNC_1 | 1450 | | 7 (0)| 00:00:01 |
|* 3 | INDEX RANGE SCAN | IX_TAB_TEST_FUNC_1 | 2 | 14 | 3 (0)| 00:00:01 |
SELECT *
FROM OC_YKB.TAB_TEST3
WHERE SUBSTR(PAN, 1, 6) in
( SELECT SUBSTR(PAN, 1, 6) FROM OC_YKB.TAB_TEST3 WHERE SUBSTR(PAN, 1, 6) = '539638'
);
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 24 (100)| |
|* 1 | HASH JOIN RIGHT SEMI | | 1449 | 52164 | 24 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | IX_TAB_TEST_FUNC_1 | 1449 | 10143 | 7 (0)| 00:00:01 |
| 3 | TABLE ACCESS BY INDEX ROWID| TAB_TEST3 | 1449 | 42021 | 17 (0)| 00:00:01 |
|* 4 | INDEX RANGE SCAN | IX_TAB_TEST_FUNC_1 | 1450 | | 7 (0)| 00:00:01 |
but does not work for this queries:
SELECT *
FROM OC_YKB.TAB_TEST3
WHERE SUBSTR(PAN, 1, 6) =
( SELECT SUBSTR(PAN, 1, 6) FROM OC_YKB.TAB_TEST3 WHERE SUBSTR(PAN, 1, 6) = '539638'
)
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 1914 (100)| |
|* 1 | TABLE ACCESS FULL| TAB_TEST3 | 292K| 8283K| 1911 (4)| 00:00:23 |
|* 2 | INDEX RANGE SCAN| IX_TAB_TEST_FUNC_1 | 2 | 14 | 3 (0)| 00:00:01 |
SELECT *
FROM OC_YKB.TAB_TEST3
WHERE SUBSTR(PAN, 1, 6) in
( SELECT '539638' FROM OC_YKB.TAB_TEST3 WHERE SUBSTR(PAN, 1, 6) = '539638'
);
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 1901 (100)| |
|* 1 | FILTER | | | | | |
| 2 | TABLE ACCESS FULL| TAB_TEST3 | 1755K| 48M| 1883 (3)| 00:00:23 |
|* 3 | FILTER | | | | | |
|* 4 | INDEX RANGE SCAN| IX_TAB_TEST_FUNC_1 | 2 | 14 | 3 (0)| 00:00:01 |
Can you explain why index does not work in second examples. Thanks
The index *can* work, but it is a costing decision by the optimizer. Here's the intial run
SQL>
SQL> create table TAB_TEST3 (guid number(16), pan varchar2(19 byte));
Table created.
SQL>
SQL> create sequence sq_test3 cache 1000;
Sequence created.
SQL>
SQL> INSERT /*+ APPEND */ INTO TAB_TEST3 select sq_test3.NEXTVAL, 536238||lpad(sq_test3.NEXTVAL,10,0) from dual connect by level <= 1000000;
1000000 rows created.
SQL> commit;
Commit complete.
SQL> INSERT /*+ APPEND */ INTO TAB_TEST3 select sq_test3.NEXTVAL, 516823||lpad(sq_test3.NEXTVAL,10,0) from dual connect by level <= 500000;
500000 rows created.
SQL> commit;
Commit complete.
SQL> INSERT /*+ APPEND */ INTO TAB_TEST3 select sq_test3.NEXTVAL, 550222||lpad(sq_test3.NEXTVAL,10,0) from dual connect by level <= 200000;
200000 rows created.
SQL> commit;
Commit complete.
SQL> INSERT /*+ APPEND */ INTO TAB_TEST3 select sq_test3.NEXTVAL, 550358||lpad(sq_test3.NEXTVAL,10,0) from dual connect by level <= 50000;
50000 rows created.
SQL> commit;
Commit complete.
SQL> INSERT /*+ APPEND */ INTO TAB_TEST3 select sq_test3.NEXTVAL, 558649||lpad(sq_test3.NEXTVAL,10,0) from dual connect by level <= 5000;
5000 rows created.
SQL> commit;
Commit complete.
SQL> INSERT /*+ APPEND */ INTO TAB_TEST3 select sq_test3.NEXTVAL, 539638||lpad(sq_test3.NEXTVAL,10,0) from dual connect by level <= 1;
1 row created.
SQL> commit;
Commit complete.
SQL>
SQL> CREATE INDEX IX_TAB_TEST_FUNC_1 ON TAB_TEST3
2 (SUBSTR(pan, 1, 6));
Index created.
SQL> exec DBMS_STATS.delete_TABLE_STATS(ownname => '' , tabname => 'TAB_TEST3');
PL/SQL procedure successfully completed.
SQL>
SQL> set autotrace traceonly explain
SQL> SELECT SUBSTR(PAN, 1, 6) FROM TAB_TEST3 WHERE SUBSTR(PAN, 1, 6) = '539638';
Execution Plan
----------------------------------------------------------
Plan hash value: 1490009622
---------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 292K| 1999K| 738 (1)| 00:00:09 |
|* 1 | INDEX RANGE SCAN| IX_TAB_TEST_FUNC_1 | 292K| 1999K| 738 (1)| 00:00:09 |
---------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access(SUBSTR("PAN",1,6)='539638')
SQL>
SQL> SELECT *
2 FROM TAB_TEST3
3 WHERE SUBSTR(PAN, 1, 6) =
4 ( SELECT '539638' FROM TAB_TEST3 WHERE SUBSTR(PAN, 1, 6) = '539638'
5 );
Execution Plan
----------------------------------------------------------
Plan hash value: 2195403411
--------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 292K| 8283K| 1849 (1)| 00:00:23 |
| 1 | TABLE ACCESS BY INDEX ROWID| TAB_TEST3 | 292K| 8283K| 1849 (1)| 00:00:23 |
|* 2 | INDEX RANGE SCAN | IX_TAB_TEST_FUNC_1 | 292K| | 738 (1)| 00:00:09 |
|* 3 | INDEX RANGE SCAN | IX_TAB_TEST_FUNC_1 | 3 | 21 | 3 (0)| 00:00:01 |
--------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access(SUBSTR("PAN",1,6)= (SELECT '539638' FROM "TAB_TEST3" "TAB_TEST3" WHERE
SUBSTR("PAN",1,6)='539638'))
3 - access(SUBSTR("PAN",1,6)='539638')
SQL>
SQL> SELECT *
2 FROM TAB_TEST3
3 WHERE SUBSTR(PAN, 1, 6) in
4 ( SELECT SUBSTR(PAN, 1, 6) FROM TAB_TEST3 WHERE SUBSTR(PAN, 1, 6) = '539638'
5 );
Execution Plan
----------------------------------------------------------
Plan hash value: 16876070
-----------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 292K| 10M| | 3421 (1)| 00:00:42 |
|* 1 | HASH JOIN RIGHT SEMI | | 292K| 10M| 5432K| 3421 (1)| 00:00:42 |
|* 2 | INDEX RANGE SCAN | IX_TAB_TEST_FUNC_1 | 292K| 1999K| | 738 (1)| 00:00:09 |
| 3 | TABLE ACCESS BY INDEX ROWID| TAB_TEST3 | 292K| 8283K| | 1849 (1)| 00:00:23 |
|* 4 | INDEX RANGE SCAN | IX_TAB_TEST_FUNC_1 | 292K| | | 738 (1)| 00:00:09 |
-----------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access(SUBSTR("PAN",1,6)=SUBSTR("PAN",1,6))
2 - access(SUBSTR("PAN",1,6)='539638')
4 - access(SUBSTR("PAN",1,6)='539638')
SQL>
SQL> SELECT *
2 FROM TAB_TEST3
3 WHERE SUBSTR(PAN, 1, 6) =
4 ( SELECT SUBSTR(PAN, 1, 6) FROM TAB_TEST3 WHERE SUBSTR(PAN, 1, 6) = '539638'
5 );
Execution Plan
----------------------------------------------------------
Plan hash value: 2195403411
--------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 292K| 8283K| 1849 (1)| 00:00:23 |
| 1 | TABLE ACCESS BY INDEX ROWID| TAB_TEST3 | 292K| 8283K| 1849 (1)| 00:00:23 |
|* 2 | INDEX RANGE SCAN | IX_TAB_TEST_FUNC_1 | 292K| | 738 (1)| 00:00:09 |
|* 3 | INDEX RANGE SCAN | IX_TAB_TEST_FUNC_1 | 3 | 21 | 3 (0)| 00:00:01 |
--------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access(SUBSTR("PAN",1,6)= (SELECT SUBSTR("PAN",1,6) FROM "TAB_TEST3" "TAB_TEST3"
WHERE SUBSTR("PAN",1,6)='539638'))
3 - access(SUBSTR("PAN",1,6)='539638')
SQL>
SQL> SELECT *
2 FROM TAB_TEST3
3 WHERE SUBSTR(PAN, 1, 6) in
4 ( SELECT '539638' FROM TAB_TEST3 WHERE SUBSTR(PAN, 1, 6) = '539638'
5 );
Execution Plan
----------------------------------------------------------
Plan hash value: 772303409
-----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 85G| 2310G| 1865 (1)| 00:00:23 |
|* 1 | FILTER | | | | | |
| 2 | TABLE ACCESS FULL| TAB_TEST3 | 1755K| 48M| 1847 (1)| 00:00:23 |
|* 3 | FILTER | | | | | |
|* 4 | INDEX RANGE SCAN| IX_TAB_TEST_FUNC_1 | 3 | 21 | 3 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter( EXISTS (SELECT 0 FROM "TAB_TEST3" "TAB_TEST3" WHERE :B1='539638'
AND SUBSTR("PAN",1,6)='539638'))
3 - filter(:B1='539638')
4 - access(SUBSTR("PAN",1,6)='539638')
SQL>
SQL> set autotrace off
And then I repeated the exercise with a histogram because we have skew in the data
SQL>
SQL> exec DBMS_STATS.GATHER_TABLE_STATS(ownname => '' , tabname => 'TAB_TEST3', method_opt=>'for all columns size auto');
PL/SQL procedure successfully completed.
SQL>
SQL> set autotrace traceonly explain
SQL> SELECT SUBSTR(PAN, 1, 6) FROM TAB_TEST3 WHERE SUBSTR(PAN, 1, 6) = '539638';
Execution Plan
----------------------------------------------------------
Plan hash value: 1490009622
---------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1886 | 13202 | 7 (0)| 00:00:01 |
|* 1 | INDEX RANGE SCAN| IX_TAB_TEST_FUNC_1 | 1886 | 13202 | 7 (0)| 00:00:01 |
---------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access(SUBSTR("PAN",1,6)='539638')
SQL>
SQL> SELECT *
2 FROM TAB_TEST3
3 WHERE SUBSTR(PAN, 1, 6) =
4 ( SELECT '539638' FROM TAB_TEST3 WHERE SUBSTR(PAN, 1, 6) = '539638'
5 );
Execution Plan
----------------------------------------------------------
Plan hash value: 2195403411
--------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1886 | 54694 | 15 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| TAB_TEST3 | 1886 | 54694 | 15 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | IX_TAB_TEST_FUNC_1 | 1886 | | 7 (0)| 00:00:01 |
|* 3 | INDEX RANGE SCAN | IX_TAB_TEST_FUNC_1 | 2 | 14 | 3 (0)| 00:00:01 |
--------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access(SUBSTR("PAN",1,6)= (SELECT '539638' FROM "TAB_TEST3" "TAB_TEST3" WHERE
SUBSTR("PAN",1,6)='539638'))
3 - access(SUBSTR("PAN",1,6)='539638')
SQL>
SQL> SELECT *
2 FROM TAB_TEST3
3 WHERE SUBSTR(PAN, 1, 6) in
4 ( SELECT SUBSTR(PAN, 1, 6) FROM TAB_TEST3 WHERE SUBSTR(PAN, 1, 6) = '539638'
5 );
Execution Plan
----------------------------------------------------------
Plan hash value: 16876070
---------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1886 | 67896 | 22 (0)| 00:00:01 |
|* 1 | HASH JOIN RIGHT SEMI | | 1886 | 67896 | 22 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | IX_TAB_TEST_FUNC_1 | 1886 | 13202 | 7 (0)| 00:00:01 |
| 3 | TABLE ACCESS BY INDEX ROWID| TAB_TEST3 | 1886 | 54694 | 15 (0)| 00:00:01 |
|* 4 | INDEX RANGE SCAN | IX_TAB_TEST_FUNC_1 | 1886 | | 7 (0)| 00:00:01 |
---------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access(SUBSTR("PAN",1,6)=SUBSTR("PAN",1,6))
2 - access(SUBSTR("PAN",1,6)='539638')
4 - access(SUBSTR("PAN",1,6)='539638')
SQL>
SQL> SELECT *
2 FROM TAB_TEST3
3 WHERE SUBSTR(PAN, 1, 6) =
4 ( SELECT SUBSTR(PAN, 1, 6) FROM TAB_TEST3 WHERE SUBSTR(PAN, 1, 6) = '539638'
5 );
Execution Plan
----------------------------------------------------------
Plan hash value: 2195403411
--------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 292K| 8283K| 1849 (1)| 00:00:23 |
| 1 | TABLE ACCESS BY INDEX ROWID| TAB_TEST3 | 292K| 8283K| 1849 (1)| 00:00:23 |
|* 2 | INDEX RANGE SCAN | IX_TAB_TEST_FUNC_1 | 292K| | 738 (1)| 00:00:09 |
|* 3 | INDEX RANGE SCAN | IX_TAB_TEST_FUNC_1 | 2 | 14 | 3 (0)| 00:00:01 |
--------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access(SUBSTR("PAN",1,6)= (SELECT SUBSTR("PAN",1,6) FROM "TAB_TEST3" "TAB_TEST3"
WHERE SUBSTR("PAN",1,6)='539638'))
3 - access(SUBSTR("PAN",1,6)='539638')
SQL>
SQL> SELECT *
2 FROM TAB_TEST3
3 WHERE SUBSTR(PAN, 1, 6) in
4 ( SELECT '539638' FROM TAB_TEST3 WHERE SUBSTR(PAN, 1, 6) = '539638'
5 );
Execution Plan
----------------------------------------------------------
Plan hash value: 772303409
-----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 551M| 14G| 1865 (1)| 00:00:23 |
|* 1 | FILTER | | | | | |
| 2 | TABLE ACCESS FULL| TAB_TEST3 | 1755K| 48M| 1847 (1)| 00:00:23 |
|* 3 | FILTER | | | | | |
|* 4 | INDEX RANGE SCAN| IX_TAB_TEST_FUNC_1 | 2 | 14 | 3 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter( EXISTS (SELECT 0 FROM "TAB_TEST3" "TAB_TEST3" WHERE :B1='539638'
AND SUBSTR("PAN",1,6)='539638'))
3 - filter(:B1='539638')
4 - access(SUBSTR("PAN",1,6)='539638')
SQL>
SQL> set autotrace off
So all bar the last one could potentially use the index. The last is simple a costing decision - if I force the index you can see the cost came out similar but slightly higher than the full scan.
SQL>
SQL> set autotrace traceonly explain
SQL> SELECT /*+ index(TAB_TEST3) */ *
2 FROM TAB_TEST3
3 WHERE SUBSTR(PAN, 1, 6) =
4 ( SELECT SUBSTR(PAN, 1, 6) FROM TAB_TEST3 WHERE SUBSTR(PAN, 1, 6) = '539638'
5 );
Execution Plan
----------------------------------------------------------
Plan hash value: 2195403411
--------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 292K| 8283K| 1849 (1)| 00:00:23 |
| 1 | TABLE ACCESS BY INDEX ROWID| TAB_TEST3 | 292K| 8283K| 1849 (1)| 00:00:23 |
|* 2 | INDEX RANGE SCAN | IX_TAB_TEST_FUNC_1 | 292K| | 738 (1)| 00:00:09 |
|* 3 | INDEX RANGE SCAN | IX_TAB_TEST_FUNC_1 | 2 | 14 | 3 (0)| 00:00:01 |
--------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access(SUBSTR("PAN",1,6)= (SELECT SUBSTR("PAN",1,6) FROM "TAB_TEST3" "TAB_TEST3"
WHERE SUBSTR("PAN",1,6)='539638'))
3 - access(SUBSTR("PAN",1,6)='539638')
SQL> set autotrace off
SQL>
SQL>