繁体   English   中英

Oracle Optimizer尴尬地不喜欢使用索引

[英]Oracle Optimizer Awkwardly Doesn't Prefer to Use Index

我正在将表与自身连接在一起,但是尽管我希望此操作使用索引,但似乎没有。 该表( MY_TABLE )上有100万条记录,而我运行的查询正在对大约1万条记录执行(因此它低于整个表的%1。)

测试用例:

explain plan for
SELECT *
  FROM SCHM.MY_TABLE A1, SCHM.MY_TABLE A2
 WHERE     (A1.K_ID = '123abc')
       AND A1.HDT = A2.HDT
       AND A2.C_DATE BETWEEN A1.SYSDATE - 0.0004 
                           AND A1.SYSDATE + 0.0004 
       AND A1.GKID = A2.GKID;


Plan hash value: 1210306805

----------------------------------------------------------------------------------------------------------
| Id  | Operation                             | Name             | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                      |                  |     3 |    81 |    28   (0)| 00:00:01 |
|*  1 |  FILTER                               |                  |       |       |            |          |
|*  2 |   HASH JOIN                           |                  |     3 |    81 |    28   (0)| 00:00:01 |
|   3 |    TABLE ACCESS BY INDEX ROWID BATCHED| MY_TABLE          |     3 |    45 |     7   (0)| 00:00:01 |
|*  4 |     INDEX RANGE SCAN                  | IX_MY_TABLE_C_DATE |     3 |       |     4   (0)| 00:00:01 |
|   5 |    TABLE ACCESS BY INDEX ROWID BATCHED| MY_TABLE          |    17 |   204 |    21   (0)| 00:00:01 |
|*  6 |     INDEX RANGE SCAN                  | IX_MY_TABLE_K_ID |    17 |       |     4   (0)| 00:00:01 |
----------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter(SYSDATE@!+0.00004>=SYSDATE@!-0.00004)
   2 - access("A1"."HDT"="A2"."HDT" AND 
              "A1"."GKID"="A2"."GKID")
   4 - access("A2"."C_DATE">=SYSDATE@!-0.00004 AND 
              "A2"."C_DATE"<=SYSDATE@!+0.00004)
   6 - access("A1"."K_ID"=U'123abc')

在上面的语句中,可以看出使用了C_DATE的索引。

但是,在下面的语句中,未使用C_DATE上的索引。 因此,查询运行速度非常慢。

真实案例:

explain plan for
SELECT *
  FROM SCHM.MY_TABLE A1, SCHM.MY_TABLE A2
 WHERE     (A1.K_ID = '123abc')
       AND A1.HDT = A2.HDT
       AND A2.C_DATE BETWEEN A1.C_DATE - 0.0004 
                           AND A1.C_DATE + 0.0004 
       AND A1.GKID = A2.GKID;


Plan hash value: 1063167343

----------------------------------------------------------------------------------------------------------
| Id  | Operation                             | Name             | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                      |                  |  4187K|   998M|  6549K  (1)| 00:04:16 |
|*  1 |  HASH JOIN                            |                  |  4187K|   998M|  6549K  (1)| 00:04:16 |
|   2 |   JOIN FILTER CREATE                  | :BF0000          |    17 |  2125 |    21   (0)| 00:00:01 |
|   3 |    TABLE ACCESS BY INDEX ROWID BATCHED| MY_TABLE          |    17 |  2125 |    21   (0)| 00:00:01 |
|*  4 |     INDEX RANGE SCAN                  | IX_MY_TABLE_K_ID |    17 |       |     4   (0)| 00:00:01 |
|   5 |   JOIN FILTER USE                     | :BF0000          |  1429M|   166G|  6546K  (1)| 00:04:16 |
|*  6 |    TABLE ACCESS STORAGE FULL          | MY_TABLE          |  1429M|   166G|  6546K  (1)| 00:04:16 |
----------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - access(A1.HDT=A2.HDT AND 
              A1.GKID=A2.GKID)
       filter(A2.C_DATE>=INTERNAL_FUNCTION(A1.C_DATE)-0.00004 AND 
              A2.C_DATE<=INTERNAL_FUNCTION(A1.C_DATE)+0.00004)
   4 - access(A1.K_ID=U'123abc')
   6 - storage(SYS_OP_BLOOM_FILTER(:BF0000,A2.HDT,A2.HDT))
       filter(SYS_OP_BLOOM_FILTER(:BF0000,A2.HDT,A2.HDT))

如果我使用提示/*+index(A2,IX_MY_TABLE_C_DATE )*/ ,那么一切都很好,将使用索引,并且查询可以根据需要快速运行。

实际情况下的查询无法更改,因为它是由应用程序创建的。

Index Information:
K_ID, not unique, position 1
HDT, not unique, position 1
C_DATE, not unique, position 1
ID Unique and Primary Key, position 1

为了使查询在实际情况下可以使用索引,我必须更改什么?

您的自联接中有三个联接条件(HDT,GKID,C_DATE)和1个非联接条件(K_ID)。 因此,对于我来说,如果DBMS从匹配K_ID的记录开始,然后查找所有匹配的其他记录,这似乎很自然。

对于这种情况,我建议以下索引:

create index idx1 on my_table(k_id, hdt, gkid, c_date);
create index idx2 on my_table(hdt, gkid, c_date);

如果每个k_id记录很少,那么我可以确定Oracle将使用索引。 如果有很多,Oracle可能仍将第二个用于哈希联接。

好吧,第二个查询的速度较慢,因为它与第一个查询完全不同。 表之间有一个额外的联接:

AND A2.C_DATE BETWEEN A1.C_DATE - 0.0004 
                  AND A1.C_DATE + 0.0004

一百万行要付出代价。

第一个查询没有此连接条件,并且两个表都是:

  • 首先过滤。 使用索引可以快速实现:只有3行和17行。
  • 加入他们第二。 加入3行和17行无需花费任何时间。

第二个查询需要执行:

  • 首先是一个巨大的(哈希)联接,可能会返回100K +行。
  • 稍后过滤。

这慢得多。

我建议添加以下索引,然后重试:

create index ix_1 (k_id);
create index ix_2 (hdt, gkid, c_date);

只是添加,只要您有情况

  1. 有一个慢的SQL,
  2. 已经确定了可以使其更快的提示
  3. 无法更改SQL,因为源不可用

那么这对于SQL Plan Baselines是一个完美的案例。 这些使您可以针对现有SQL锁定“良好”计划,而无需接触SQL语句本身。

描述SPM的整个系列都在下面的链接中,但是“第4部分”的链接详细介绍了您要实现的目标。

https://blogs.oracle.com/optimizer/sql-plan-management-part-1-of-4-creating-sql-plan-baselines https://blogs.oracle.com/optimizer/sql-plan-management -part-2-of-4-spm-aware-optimizer https://blogs.oracle.com/optimizer/sql-plan-management-part-3-of-4:-evolving-sql-plan-baselines https: //blogs.oracle.com/optimizer/sql-plan-management-part-4-of-4:-user-interfaces-and-other-features

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM