SQL嵌套循环的成本太高

时间:2018-12-09 14:15:37

标签: sql oracle performance

我有一个这样的SQL查询:

select w.name, c.address, b.salary, a.product, d.contract_amount
from w
left join c c.id = w.id
left join b b.id = w.id
left join a a.id = w.id and a.date > sysdate-30
left join d d.id = w.id
where w.id = '12345';

这是计划:

-----------------------------------------------------------------------------------
| Id  | Operation                     | Name  | Rows  | Bytes | Cost   | Time     |
-----------------------------------------------------------------------------------
|   0 | SELECT STATEMENT              |       |     1 |   849 |18896868| 00:01:14 |
|   1 |  NESTED LOOPS OUTER           |       |     1 |   849 |18896868| 00:01:14 |
|   2 |   NESTED LOOPS OUTER          |       |     1 |   849 |18896868| 00:01:14 |
|   3 |    NESTED LOOPS OUTER         |       |     1 |   670 |18896868| 00:01:14 |
|   4 |     NESTED LOOPS OUTER        |       |     1 |   596 |18896868| 00:01:14 |
|   5 |  TABLE ACCESS STORAGE FULL    |   w   |     1 |   415 |     20 | 00:00:01 |
|   6 |  TABLE ACCESS BY INDEX ROWID  |   c   |     1 |    22 |      3 | 00:00:01 |
|   7 |   INDEX UNIQUE SCAN           |c_id_nd|     1 |       |        | 00:00:01 |
|   8 |  TABLE ACCESS BY INDEX ROWID  |   b   |     1 |    66 |      2 | 00:00:01 |
|   9 |   INDEX UNIQUE SCAN           |b_id_nd|     1 |       |        | 00:00:01 |
|  10 |  TABLE ACCESS BY INDEX ROWID  |   a   |     1 |    11 |      3 | 00:00:01 |
|  11 |   INDEX UNIQUE                |a_id_nd|     1 |       |        | 00:00:01 |
|  12 |  TABLE ACCESS BY INDEX ROWID  |   d   |     1 |    25 |      1 | 00:00:01 |
|  13 |   INDEX UNIQUE                |d_id_nd|     1 |       |        | 00:00:01 |
-----------------------------------------------------------------------------------

现在它的工作大约需要15-18秒,而且太长了。我是调优的新手,我不知道如何提高其性能。实际上,所有表都有大约33-54百万行,所有id列都有索引。还统计了表的统计信息,我不能使用并行提示。 我该怎么做?

4 个答案:

答案 0 :(得分:1)

对于此查询:

select w.name, c.address, b.salary, a.product, d.contract_amount
from w left join
     c
     on c.id = w.id left join
     b
     on b.id = w.id left join
     a
     on a.id = w.id and a.date > sysdate-30 left join
     d
     on d.id = w.id
where w.id = '12345';

您要在w(id)c(id), b(id), a(id, date)d(id)上建立索引。

答案 1 :(得分:0)

表中有3500万条记录。表是否已分区?如果是这样,查询将确保分区修剪

答案 2 :(得分:0)

我想您的查询没有问题,我认为最初生成的执行计划很糟糕,它仍然位于缓存中。您可以用其他方式覆盖查询,可能会得到更好的计划(例如CTE)。您也可以尝试在加入前过滤ID。尝试这样的东西

with 
 W as (select id, name from w where w.id = '12345')
,C as (select id, address from C where c.id = '12345')
,B as (select id, salary from B where b.id = '12345')
,A as (select id, product from A where a.id = '12345' and a.date > sysdate - 30)
,D as (select id, contract_amount from D where d.id = '12345')

select w.name, c.address, b.salary, a.product, d.contract_amount
from w
left join c on c.id = w.id
left join b on b.id = w.id
left join a on a.id = w.id
left join d on d.id = w.id

或者这个:

with 
 W1 as (select w.id, w.name from w where w.id = '12345')
,W2 as (select w1.* , c.address from W1 left outer join C on w1.id = c.id)
,W3 as (select w2.*, b.salary from W2 left outer join B on w2.id = b.id)
,W4 as (select w3.*, a.product from W3 left outer join A on w3.id = a.id and a.date > sysdate - 30)
Select w4.*, d.contract_amount from W4 left outer join D on w4.id = d.id 

答案 3 :(得分:0)

我认为问题出在基数估计器上。由于从主表到详细信息“类型”表的多个左联接,因此错误地假定了行返回。基数估计不正确可能会导致计划选择不正确。我建议尝试使用Mike建议的隔离选择并比较计时。我不确定智能CTE在Oracle中的表现如何,因此即使您必须使用临时表或内存表,我也建议您使用绝对隔离的语句。使用您的id值单独选择每个表,然后将结果放入临时表中。然后在这些临时表上执行最终选择。