SQL嵌套循环成本太高

SQL嵌套循环成本太高,sql,oracle,performance,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'; 它的计划是: ------------

我有这样一个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秒,时间太长了。我是一个新手,不知道如何提高它的性能。实际上,所有表都有大约3300-5400万行,所有id列都有索引。还收集了表的统计数据,我无法使用并行提示。 我可以做哪些优化?

对于此查询:

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)
上建立索引,表中有3500万条记录。桌子是分区的吗。?如果查询确保了分区修剪,我想您的查询没有问题,我认为错误的执行计划最初是生成的,现在仍在缓存中。您可以用不同的方式覆盖查询,可能会得到更好的计划(例如使用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 

我认为问题在于基数估计。由于多个从-maybuly-master到detail“type”表的左连接,存在返回行的错误假设。较差的基数估计可能导致较差的计划选择。我建议尝试一下Mike提出的孤立选择,并比较时间安排。我不确定smart CTE在Oracle中的性能如何,因此我推荐使用独立语句,即使您必须使用临时表或内存表。使用id值单独选择每个表,并将结果放入临时表中。然后在这些临时表上执行最后的选择。大约2-5,这取决于某些IDID选项,以查看表是否可以被压缩并收集统计信息并检查是否可以生成SQL监视器报告。这将提供有价值的运行时信息,并限制猜测的数量。谢谢,但这个id实际上是varchar类型的,因为其中一些id中有字母12345'只是C、B、A、D中的一个主题id唯一和索引列的示例?是的,它为所有表的所有id列建立了索引参见Gordon Linoff的答案。日期必须在你的
w
索引中。我认为提供的信息不足以做出明确的断言。在下面的评论中,您声明将返回“大约2-5”条记录(行?),而您帖子中的计划仅显示一行。这与适当的优化有关。另外:解释计划实际上并没有运行计划,这在将“时间”列与您所说的“15-18秒”进行比较时很明显。请考虑自动跟踪。如果您想要更准确的信息,您可以浏览以下内容: