下面的例子如何添加索引?

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,但在这种情况下无效。 所以你需要另一个索引。