为什么 SQL 查询在 Oracle 数据库中 运行 非常慢?

Why SQL query is running very slow in Oracle Database?

我有一个 table 大约有 1800 万条记录。我必须在一个查询中读取整个 table,因为所有记录都是在一个日期创建的。 因此,首先我在四个列上定义了一个索引,我使用以下命令查询它们:

CREATE INDEX test_etl_index ON test_table(t_date,c_num,obc,bu);

创建索引后,我必须对 c_num,obc,bu 的每一列使用此查询。查询如下:

    SELECT t_date,
       c_num,
       pd,
       pds,
       uc,
       obc,
       t_id,
       da,
       ca,
       db,
       t_time,
       ibc,
       lc,
       lt,
       sts,
       wd,
       bu
FROM   test_table
WHERE  t_date = '20170628'
       AND c_num IN (SELECT KEY
                     FROM   c_g
                     WHERE  g_id = 1);

但是,对于每一列,查询大约需要 8 分钟,这非常非常慢!

能否请您指导我如何更改查询以获得更好的性能?任何帮助将不胜感激。

仅在日期和 c_num 列上创建索引,而不是 (date,c_num,obc,bu) 要么 在 date 和 c_num 上创建另一个索引并调用它 idx2

根据您提供的信息几乎无法提供任何建议。

除了 - 正如推荐的那样 - 修复 DATE 列的数据类型,因为将日期存储为字符串确实会使优化器感到困惑。

预期的设置取决于您的数据,这里有一些提示。

DATE 列是可选的

万一你的谓词date='20170628'(或者更好col_date = date'2017-06-28) returns 只有 很少的记录 您将从该列的索引中获益。

create index test_table_idx on test_table(col_date);

你可以期待一个执行计划如下

-------------------------------------------------------------------------------------------------------
| Id  | Operation                            | Name           | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                     |                |     4 |   472 |     5   (0)| 00:00:01 |
|*  1 |  HASH JOIN SEMI                      |                |     4 |   472 |     5   (0)| 00:00:01 |
|   2 |   TABLE ACCESS BY INDEX ROWID BATCHED| TEST_TABLE     |    10 |  1120 |     2   (0)| 00:00:01 |
|*  3 |    INDEX RANGE SCAN                  | TEST_TABLE_IDX |    10 |       |     1   (0)| 00:00:01 |
|*  4 |   TABLE ACCESS FULL                  | C_G            |     3 |    18 |     3   (0)| 00:00:01 |
-------------------------------------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   1 - access("C_NUM"="KEY")
   3 - access("COL_DATE"=TO_DATE(' 2002-01-01 00:00:00', 'syyyy-mm-dd hh24:mi:ss'))
   4 - filter("G_ID"=1)

请注意,Oracle rewrite 您的 in (subquery)hash join semi 中,因此不需要手动查询重写。

C_NUM 是选择性的

如果相反谓词c_num in (...returns记录很少,在c_num列上定义一个索引。

create index test_table_idx2 on test_table(c_num);

你可以期待一个执行计划如下

------------------------------------------------------------------------------------------------
| Id  | Operation                    | Name            | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |                 |     4 |   480 |    28   (4)| 00:00:01 |
|   1 |  NESTED LOOPS                |                 |     4 |   480 |    28   (4)| 00:00:01 |
|   2 |   NESTED LOOPS               |                 |    20 |   480 |    28   (4)| 00:00:01 |
|   3 |    SORT UNIQUE               |                 |     3 |    18 |     3   (0)| 00:00:01 |
|*  4 |     TABLE ACCESS FULL        | C_G             |     3 |    18 |     3   (0)| 00:00:01 |
|*  5 |    INDEX RANGE SCAN          | TEST_TABLE_IDX2 |    10 |       |     2   (0)| 00:00:01 |
|*  6 |   TABLE ACCESS BY INDEX ROWID| TEST_TABLE      |     1 |   114 |    12   (0)| 00:00:01 |
------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
 
   4 - filter("G_ID"=1)
   5 - access("C_NUM"="KEY")
   6 - filter("COL_DATE"=TO_DATE(' 2000-01-02 00:00:00', 'syyyy-mm-dd hh24:mi:ss'))

无选择性

如果以上都不成立,请忘记索引,您应该会看到一个 HASH JOIN SEMI,它在 18M 上应该不会花费那么多时间 table

-----------------------------------------------------------------------------------
| Id  | Operation            | Name       | Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------------
|   0 | SELECT STATEMENT     |            | 27273 |  3142K|  4516   (1)| 00:00:01 |
|*  1 |  HASH JOIN RIGHT SEMI|            | 27273 |  3142K|  4516   (1)| 00:00:01 |
|*  2 |   TABLE ACCESS FULL  | C_G        |     3 |    18 |     3   (0)| 00:00:01 |
|*  3 |   TABLE ACCESS FULL  | TEST_TABLE | 90909 |  9943K|  4512   (1)| 00:00:01 |
-----------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
 
   1 - access("C_NUM"="KEY")
   2 - filter("G_ID"=1)
   3 - filter("COL_DATE"=TO_DATE(' 2000-01-02 00:00:00', 'syyyy-mm-dd 
              hh24:mi:ss')) 

重点是学习如何获取查询的 ,如何阅读它以及如何理解瓶颈在哪里。

问题已解决。

我分别在列上创建 INDEX,如下所示:

 CREATE INDEX t_date_inx ON test_table(t_date);
 CREATE INDEX c_num_inx ON test_table(c_num);

然后运行查询。它 运行 更快。

 SELECT t_date,
   c_num,
   pd,
   pds,
   uc,
   obc,
   t_id,
   da,
   ca,
   db,
   t_time,
   ibc,
   lc,
   lt,
   sts,
   wd,
   bu
   FROM   test_table
   WHERE  t_date = '20170628'
       AND c_num IN (SELECT KEY
                 FROM   c_g
                 WHERE  g_id = 1);

希望对其他人有用。