为什么 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);
希望对其他人有用。
我有一个 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);
希望对其他人有用。