我正在从 mysql DB 切换到使用 oracle 19c 我需要创建/调整索引以支持范围条件: 我有这样的疑问:
select * from mytable where mydate>? and mytype=?
我在 mytavble(mydate,mytype) 上有一个索引 根据 MySQL,一旦达到第一个范围条件,查询中的任何后续条件将不会使用任何其他列。
为什么 mysql 不能也使用第二个索引列 mytype? 甲骨文也是如此吗?
Oracle 将打开一个
INDEX RANGE SCAN
,但会扫描 mydate
高于绑定变量的所有条目,并跳过(过滤)具有不同 mytype
的条目。
如果每个
mydate
都有大量数量的mytype
,这将非常无效。
你可以在执行计划中看到它
EXPLAIN PLAN SET STATEMENT_ID = 'jara1' into plan_table FOR
select * from mytable where mydate>:1 and mytype=:2;
SELECT * FROM table(DBMS_XPLAN.DISPLAY('plan_table', 'jara1','ALL'));
-----------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 500 | 982K| 61 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID BATCHED| MYTABLE | 500 | 982K| 61 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | IDX | 90 | | 30 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("MYDATE">:1 AND "MYTYPE"=TO_NUMBER(:2) AND "MYDATE" IS NOT NULL)
filter("MYTYPE"=TO_NUMBER(:2))
显然更好的查询索引是列顺序相反的索引 - 首先是具有相等谓词的列
create index idx2 on mytable (mytype,mydate);
-----------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 500 | 982K| 93 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID BATCHED| MYTABLE | 500 | 982K| 93 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | IDX2 | 90 | | 3 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("MYTYPE"=TO_NUMBER(:2) AND "MYDATE">:1 AND "MYDATE" IS NOT NULL)
您会看到没有过滤器,仅选择了适当的行。与
3
相比,预计成本也更低30
。
我不能代表 MySQL,但怀疑这会是相同的行为。