Function-based Index and Or-Expansion
原文連線:
1. Create objects.
drop table t1 purge;
create table t1(c1 int, c2 int, c3 int);
insert into t1
select level, level, level
from dual
connect by level <= 100000;
create index t1_n1 on t1(c1+1); -- function-based index
create index t1_n2 on t1(c2+1); -- function-based index
create index t1_n3 on t1(c1); -- normal index
create index t1_n4 on t1(c2); -- normal index
exec dbms_stats.gather_table_stats(user, 't1');
2.With index combination enabled, Oracle builds the most efficient execution plan imagineable.
alter session set "_b_tree_bitmap_plans" = true;
explain plan for
select *
from t1
where c1+1 = 1 or c2+1 = 1
;
-------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|
-------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 2 | 48 | 2 (0)|
| 1 | TABLE ACCESS BY INDEX ROWID | T1 | 2 | 48 | 2 (0)|
| 2 | BITMAP CONVERSION TO ROWIDS | | | | |
| 3 | BITMAP OR | | | | |
| 4 | BITMAP CONVERSION FROM ROWIDS| | | | |
|* 5 | INDEX RANGE SCAN | T1_N1 | | | 1 (0)|
| 6 | BITMAP CONVERSION FROM ROWIDS| | | | |
|* 7 | INDEX RANGE SCAN | T1_N2 | | | 1 (0)|
-------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
5 - access("C1"+1=1)
7 - access("C2"+1=1)
3. What if the index combination got disabled?
alter session set "_b_tree_bitmap_plans" = false; -- In standard edition, this would be fixed behavior.
explain plan for
select *
from t1
where c1+1 = 1 or c2+1 = 1
;
---------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|
---------------------------------------------------------------
| 0 | SELECT STATEMENT | | 2 | 48 | 99 (6)|
|* 1 | TABLE ACCESS FULL| T1 | 2 | 48 | 99 (6)|
---------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("C1"+1=1 OR "C2"+1=1)
4. But with normal indexes, Oracle’s choice is Or-Expansion, which is quite natural and efficient.
explain plan for
select *
from t1
where c1 = 1 or c2 = 1
;
---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 2 | 30 | 4 (0)|
| 1 | CONCATENATION | | | | |
| 2 | TABLE ACCESS BY INDEX ROWID| T1 | 1 | 15 | 2 (0)|
|* 3 | INDEX RANGE SCAN | T1_N4 | 1 | | 1 (0)|
|* 4 | TABLE ACCESS BY INDEX ROWID| T1 | 1 | 15 | 2 (0)|
|* 5 | INDEX RANGE SCAN | T1_N3 | 1 | | 1 (0)|
---------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("C2"=1)
4 - filter(LNNVL("C2"=1))
5 - access("C1"=1)
5. Okay, maybe Oracle has some logic holes with function-based indexes. Would appropriate hints force the Or-Expansion with function-based indexes?
explain plan for
select
/*+
INDEX_RS_ASC(@"SEL$1_2" " "T1_N1")
INDEX_RS_ASC(@"SEL$1_1" " "T1_N2")
USE_CONCAT(@"SEL$1" 8) */
*
from t1
where c1+1 = 1 or c2+1 = 1
;
----------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|
----------------------------------------------------------------
| 0 | SELECT STATEMENT | | 2 | 48 | 195 (4)|
| 1 | CONCATENATION | | | | |
|* 2 | TABLE ACCESS FULL| T1 | 1 | 24 | 98 (5)|
|* 3 | TABLE ACCESS FULL| T1 | 1 | 24 | 98 (5)|
----------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("C2"+1=1)
3 - filter("C1"+1=1 AND LNNVL("C2"+1=1))
No.
The question is why this is happening. This is well documented here.
http://download.oracle.com/docs/cd/B14117_01/appdev.101/b10795/adfns_in.htm#1006464
Yes, unfortunately, Oracle is not able to use function-based indexes with or expansion. Hence in this case, full table scan is the only option remaining.
6. There are another trick we can try when the SQL text itself is not modifiable. Stored outline. But in this case, even stored outline cannot change the execution plan to use function-based indexes. Mission impossible.
7. The last trick. Oracle 10g supports the feature called advanced query rewriting, which enables us to change the SQL text on the fly. But it has many restrictions.
- Enterpnrise edition feature.
- Select stateme only.
- Bind variable not supported.
Fortunately, my simple and stupid test case can be resolved with this feature.
begin
sys.dbms_advanced_rewrite.declare_rewrite_equivalence (
name => 'rewrite1',
source_stmt =>
'select *
from t1
where c1+1 = 1 or c2+1 = 1',
destination_stmt =>
'select *
from t1
where c1 = 0 or c2 = 0',
validate => false,
rewrite_mode => 'text_match');
end;
/
alter session set query_rewrite_integrity = trusted;
explain plan for
select *
from t1
where c1+1 = 1 or c2+1 = 1
;
---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 2 | 30 | 4 (0)|
| 1 | CONCATENATION | | | | |
| 2 | TABLE ACCESS BY INDEX ROWID| T1 | 1 | 15 | 2 (0)|
|* 3 | INDEX RANGE SCAN | T1_N4 | 1 | | 1 (0)|
|* 4 | TABLE ACCESS BY INDEX ROWID| T1 | 1 | 15 | 2 (0)|
|* 5 | INDEX RANGE SCAN | T1_N3 | 1 | | 1 (0)|
---------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("C2"=0)
4 - filter(LNNVL("C2"=0))
5 - access("C1"=0)
But, this feature would not be easily adopted in the real life. Too many restrictions.
It was a sad story, which made me think about how to control the execution plan of the non-modifiable SQL.
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/22034023/viewspace-688793/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 關於函式索引(function-based index)函式索引FunctionIndex
- 基於函式的索引(function-based index,FBI)函式索引FunctionIndex
- 建立Function-Based IndexesFunctionIndex
- Function-Based Indexes (199)FunctionIndex
- Uses of Function-Based Indexes (200)FunctionIndex
- Optimization with Function-Based Indexes (201)FunctionIndex
- Dependencies of Function-Based Indexes (202)FunctionIndex
- Resolve Dependencies of Function-Based Indexes (205)FunctionIndex
- KEEP INDEX | DROP INDEXIndex
- rowid,index,INDEX FULL SCAN,INDEX FAST FULL SCAN|IndexAST
- INDEX UNIQUE SCAN,INDEX FULL SCAN和INDEX FAST FULL SCANIndexAST
- Clustered Index Scan and Clustered Index SeekIndex
- 高效的SQL(Function-based Indexes 函式、運算優化思路)SQLFunctionIndex函式優化
- oracle hint之full,index,index_asc,index_desc,index_combile示例OracleIndex
- IndexIndex
- Index的掃描方式:index full scan/index fast full scanIndexAST
- rebuild index 和 recreate index (重新建立index) 的區別RebuildIndex
- pk 、unique index 和 index 區別Index
- global index & local index的區別Index
- alter index rebuild與index_statsIndexRebuild
- B-index、bitmap-index、text-index使用場景詳解Index
- Index Full Scan vs Index Fast Full ScanIndexAST
- Using index condition Using indexIndex
- 【Oracle】global index & local index的區別OracleIndex
- Index Full Scans和Index Fast Full ScansIndexAST
- What is meant by Primary Index and Secondary IndexIndex
- Index Full Scan 與 Index Fast Full ScanIndexAST
- PostgreSQL:INDEXSQLIndex
- <MYSQL Index>MySqlIndex
- jQuery index()jQueryIndex
- index索引Index索引
- rebuild indexRebuildIndex
- index rebuildIndexRebuild
- Bitmap IndexIndex
- index range scan,index fast full scan,index skip scan發生的條件IndexAST
- create index/create index online區別Index
- Create index with open on-line index creationIndex
- 使用自定義函式創Function-Based Indexes時需要使用DETERMINISTIC Functions!函式FunctionIndex