为什么Oracle数据库中SQL查询运行速度很慢?

bxfogqkk  于 2023-01-01  发布在  Oracle
关注(0)|答案(4)|浏览(177)

我有一个表,有大约1800万条记录。我必须在一次查询中读取整个表,因为所有记录都是在同一天创建的。所以,首先我定义了一个索引,我使用下面的命令对四列进行查询:

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分钟,这是非常非常慢的!
您能告诉我如何更改查询以获得更好的性能吗?如有任何帮助,我们将不胜感激。

tez616oj

tez616oj1#

根据您提供的信息,我们几乎无法提供任何建议。
除了修复DATE列的数据类型之外,因为将日期存储为字符串确实会使优化器感到困惑。
预期的设置取决于 * 您的数据 *,这里有一些提示。

DATE列是选择性的

如果 predicate date='20170628'(或更好的col_date = date'2017-06-28)只返回 * 很少的记录 *,那么您将从该列的索引中获益。

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 * 在 hash join semi 中重写 * 您的in (subquery),因此不需要手动重写查询。

C_NUM是选择性的

相反,如果 predicate c_num in (...返回很少的记录,则在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表上不会花费太多时间

-----------------------------------------------------------------------------------
| 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'))

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

lymnna71

lymnna712#

仅对date和c_num列创建索引,而不是(date,c_num,obc,bu);或者对date和c_num创建另一个索引,并将其称为idx 2

ruoxqz4g

ruoxqz4g3#

问题解决了。
我分别在列上创建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);

希望对其他人有用。

7gs2gvoe

7gs2gvoe4#

使用存在而不是在()中
其中t_date = '20170628'且存在(从c_g中选择'x',其中g_id = 1且关键字=测试表.c_编号)

相关问题