下面的例子如何添加索引?
How to add indexes to the following examples?
SELECT UNIT_PRICE
FROM ORDER_DETAIL
WHERE QUANTITY IN (100, 200, 300) OR
DISCOUNT = 0.01;
我的查询:
EXPLAIN PLAN FOR SELECT UNIT_PRICE FROM ORDER_DETAIL WHERE QUANTITY IN (100, 200, 300) OR DISCOUNT = 0.01;`
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);
CREATE INDEX OD_IDX_QD ON ORDER_DETAIL(QUANTITY, DISCOUNT);
EXPLAIN PLAN FOR SELECT UNIT_PRICE FROM ORDER_DETAIL WHERE QUANTITY IN (100, 200, 300) OR DISCOUNT = 0.01;
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);
DROP INDEX OD_IDX_QD;
这种情况下如何正确添加索引?
您的查询包含 OR,这就是您无法进行索引范围扫描的原因。
您可以为 DISCOUNT 再创建一个索引并添加提示 OR_EXPAND,在这种情况下,您将获得带有 2 个索引范围扫描(或扩展转换)的 UNION ALL
(甲骨文 12.2+:https://blogs.oracle.com/optimizer/optimizer-transformations:-or-expansion)
CREATE INDEX OD_IDX_D ON ORDER_DETAIL(DISCOUNT);
EXPLAIN PLAN FOR
SELECT/*+ or_expand */ UNIT_PRICE FROM ORDER_DETAIL WHERE QUANTITY IN (100, 200, 300) OR DISCOUNT = 0.01;
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);
-- output
Plan hash value: 4033578183
----------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 4 | 52 | 5 (0)| 00:00:01 |
| 1 | VIEW | VW_ORE_1606201E | 4 | 52 | 5 (0)| 00:00:01 |
| 2 | UNION-ALL | | | | | |
| 3 | TABLE ACCESS BY INDEX ROWID BATCHED | ORDER_DETAIL | 1 | 26 | 2 (0)| 00:00:01 |
|* 4 | INDEX RANGE SCAN | OD_IDX_D | 1 | | 1 (0)| 00:00:01 |
| 5 | INLIST ITERATOR | | | | | |
| 6 | TABLE ACCESS BY INDEX ROWID BATCHED| ORDER_DETAIL | 3 | 90 | 3 (0)| 00:00:01 |
|* 7 | INDEX RANGE SCAN | OD_IDX_QD | 3 | | 2 (0)| 00:00:01 |
----------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
4 - access("DISCOUNT"=0.01)
7 - access("QUANTITY"=100 OR "QUANTITY"=200 OR "QUANTITY"=300)
filter(LNNVL("DISCOUNT"=0.01))
或者如果你的oracle版本很旧,你可以使用hint use_concat:
CREATE INDEX OD_IDX_D ON ORDER_DETAIL(DISCOUNT);
EXPLAIN PLAN FOR
SELECT/*+ use_concat */ UNIT_PRICE
FROM ORDER_DETAIL OD
WHERE QUANTITY IN (100, 200, 300) OR DISCOUNT = 0.01;
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);
Plan hash value: 819751077
------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 4 | 120 | 5 (0)| 00:00:01 |
| 1 | CONCATENATION | | | | | |
| 2 | TABLE ACCESS BY INDEX ROWID BATCHED | ORDER_DETAIL | 1 | 30 | 2 (0)| 00:00:01 |
|* 3 | INDEX RANGE SCAN | OD_IDX_D | 1 | | 1 (0)| 00:00:01 |
| 4 | INLIST ITERATOR | | | | | |
| 5 | TABLE ACCESS BY INDEX ROWID BATCHED| ORDER_DETAIL | 3 | 90 | 3 (0)| 00:00:01 |
|* 6 | INDEX RANGE SCAN | OD_IDX_QD | 3 | | 2 (0)| 00:00:01 |
------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("DISCOUNT"=0.01)
6 - access("QUANTITY"=100 OR "QUANTITY"=200 OR "QUANTITY"=300)
filter(LNNVL("DISCOUNT"=0.01))
更新:评论不方便回答你的补充问题,所以我在这里回答:
因为您的查询包含 OR:
WHERE QUANTITY IN (100, 200, 300) OR DISCOUNT = 0.01;
如果你分析你的查询,你会发现它与
select UNIT_PRICE
from (
SELECT rowid, UNIT_PRICE FROM ORDER_DETAIL WHERE QUANTITY IN (100, 200, 300)
union
SELECT rowid, UNIT_PRICE FROM ORDER_DETAIL WHERE DISCOUNT = 0.01
)
union all 的第一部分可以使用您的索引,因为它从该列开始
但不是第二部分,因为您没有为 index.In 的第一列提供范围,这种情况下 oracle 可以使用 INDEX SKIP SCAN,但在这种情况下无效。
所以你需要另一个索引。
SELECT UNIT_PRICE
FROM ORDER_DETAIL
WHERE QUANTITY IN (100, 200, 300) OR
DISCOUNT = 0.01;
我的查询:
EXPLAIN PLAN FOR SELECT UNIT_PRICE FROM ORDER_DETAIL WHERE QUANTITY IN (100, 200, 300) OR DISCOUNT = 0.01;`
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);
CREATE INDEX OD_IDX_QD ON ORDER_DETAIL(QUANTITY, DISCOUNT);
EXPLAIN PLAN FOR SELECT UNIT_PRICE FROM ORDER_DETAIL WHERE QUANTITY IN (100, 200, 300) OR DISCOUNT = 0.01;
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);
DROP INDEX OD_IDX_QD;
这种情况下如何正确添加索引?
您的查询包含 OR,这就是您无法进行索引范围扫描的原因。 您可以为 DISCOUNT 再创建一个索引并添加提示 OR_EXPAND,在这种情况下,您将获得带有 2 个索引范围扫描(或扩展转换)的 UNION ALL
(甲骨文 12.2+:https://blogs.oracle.com/optimizer/optimizer-transformations:-or-expansion)
CREATE INDEX OD_IDX_D ON ORDER_DETAIL(DISCOUNT);
EXPLAIN PLAN FOR
SELECT/*+ or_expand */ UNIT_PRICE FROM ORDER_DETAIL WHERE QUANTITY IN (100, 200, 300) OR DISCOUNT = 0.01;
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);
-- output
Plan hash value: 4033578183
----------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 4 | 52 | 5 (0)| 00:00:01 |
| 1 | VIEW | VW_ORE_1606201E | 4 | 52 | 5 (0)| 00:00:01 |
| 2 | UNION-ALL | | | | | |
| 3 | TABLE ACCESS BY INDEX ROWID BATCHED | ORDER_DETAIL | 1 | 26 | 2 (0)| 00:00:01 |
|* 4 | INDEX RANGE SCAN | OD_IDX_D | 1 | | 1 (0)| 00:00:01 |
| 5 | INLIST ITERATOR | | | | | |
| 6 | TABLE ACCESS BY INDEX ROWID BATCHED| ORDER_DETAIL | 3 | 90 | 3 (0)| 00:00:01 |
|* 7 | INDEX RANGE SCAN | OD_IDX_QD | 3 | | 2 (0)| 00:00:01 |
----------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
4 - access("DISCOUNT"=0.01)
7 - access("QUANTITY"=100 OR "QUANTITY"=200 OR "QUANTITY"=300)
filter(LNNVL("DISCOUNT"=0.01))
或者如果你的oracle版本很旧,你可以使用hint use_concat:
CREATE INDEX OD_IDX_D ON ORDER_DETAIL(DISCOUNT);
EXPLAIN PLAN FOR
SELECT/*+ use_concat */ UNIT_PRICE
FROM ORDER_DETAIL OD
WHERE QUANTITY IN (100, 200, 300) OR DISCOUNT = 0.01;
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);
Plan hash value: 819751077
------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 4 | 120 | 5 (0)| 00:00:01 |
| 1 | CONCATENATION | | | | | |
| 2 | TABLE ACCESS BY INDEX ROWID BATCHED | ORDER_DETAIL | 1 | 30 | 2 (0)| 00:00:01 |
|* 3 | INDEX RANGE SCAN | OD_IDX_D | 1 | | 1 (0)| 00:00:01 |
| 4 | INLIST ITERATOR | | | | | |
| 5 | TABLE ACCESS BY INDEX ROWID BATCHED| ORDER_DETAIL | 3 | 90 | 3 (0)| 00:00:01 |
|* 6 | INDEX RANGE SCAN | OD_IDX_QD | 3 | | 2 (0)| 00:00:01 |
------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("DISCOUNT"=0.01)
6 - access("QUANTITY"=100 OR "QUANTITY"=200 OR "QUANTITY"=300)
filter(LNNVL("DISCOUNT"=0.01))
更新:评论不方便回答你的补充问题,所以我在这里回答:
因为您的查询包含 OR:
WHERE QUANTITY IN (100, 200, 300) OR DISCOUNT = 0.01;
如果你分析你的查询,你会发现它与
select UNIT_PRICE
from (
SELECT rowid, UNIT_PRICE FROM ORDER_DETAIL WHERE QUANTITY IN (100, 200, 300)
union
SELECT rowid, UNIT_PRICE FROM ORDER_DETAIL WHERE DISCOUNT = 0.01
)
union all 的第一部分可以使用您的索引,因为它从该列开始 但不是第二部分,因为您没有为 index.In 的第一列提供范围,这种情况下 oracle 可以使用 INDEX SKIP SCAN,但在这种情况下无效。 所以你需要另一个索引。