如何为以下示例添加索引?

Posted

技术标签:

【中文标题】如何为以下示例添加索引?【英文标题】:How to add indexes to the following examples? 【发布时间】:2020-07-06 03:53:13 【问题描述】:
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;

这种情况下如何正确添加索引?

【问题讨论】:

您好,根据 sn-p 您已成功创建索引。如果您的问题是为什么在此特定情况下未使用索引,则可能是由于许多因素。 (例如:如果大多数记录在选择查询中返回,那么优化器会正确评估使用索引可能会更昂贵) 一般来说还有一点是索引 快速查询(这取决于许多因素,对象统计信息、缓存计划、查询的选择性、倾斜数据等) 是的,我的问题是为什么在这个特定的实例中不使用这个索引。我的桌子是空的,所以这是什么原因。 如果表是空的,直接导航到表而不是使用索引会更容易 【参考方案1】:

您的查询包含 OR,这就是您无法进行索引范围扫描的原因。 您可以为 DISCOUNT 再创建一个索引并添加提示 OR_EXPAND,在这种情况下,您将获得具有 2 次索引范围扫描(或扩展转换)的 UNION ALL

(Oracle 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 版本太旧,你可以使用提示 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))

更新:cmets不方便回答你的其他问题,所以我在这里回答:

因为您的查询包含 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
)

联合的第一部分都可以使用您的索引,因为它从该列开始 但不是第二部分,因为您没有为索引的第一列提供范围。在这种情况下,oracle 可以使用 INDEX SKIP SCAN,但在这种情况下无效。 所以你需要另一个索引。

【讨论】:

我之前的索引是基于QUANTITYDISCOUNT,我不明白为DISCOUNT创建索引。 因为您的查询包含 OR:SELECT UNIT_PRICE FROM ORDER_DETAIL WHERE QUANTITY IN (100, 200, 300) OR DISCOUNT = 0.01;` 谢谢我需要一些时间来完全理解它。分别为“QUANTITY”和“DISCOUNT”添加索引怎么样? 我刚刚在我的帖子中添加了扩展答案,因为 cmets 不允许长答案。 >如何将索引分别添加到“QUANTITY”和“DISCOUNT”取决于...是的 - 如果您没有使用 AND 查询谓词:例如 ... AND QUANTITY =...中的 "我以前的索引是基于QUANTITY和DISCOUNT的"..在多列上创建索引时,索引的选择性是按索引中列的顺序排列的,而不是在每一列上分别地。并且不管 any 索引如何,优化器仍然可以很好地确定索引的选择性不足以证明额外的索引读取与简单地扫描表是合理的。

以上是关于如何为以下示例添加索引?的主要内容,如果未能解决你的问题,请参考以下文章

如何为 CitusDB 的 cstore_fdw 添加索引?

使用索引时如何为 UV 添加 vec2 以进行纹理映射

如何为 MySQL 表添加索引?

Mysql如何为表字段添加索引?

如何为映射类型添加索引签名?

如何为 UITableView 索引事件添加操作 - 对于每个字母