如何让Oracle为此查询提供更好的执行计划?

时间:2013-09-17 18:03:35

标签: sql oracle query-optimization

我们有一些表可以跟踪已处理的交易。这些表有数百万行。通常我想查看最近的X事务,所以我有一个这样的查询从几个表中提取我想要的信息:

select a.id, b.field_one, c.field_two
from trans a, table_two b, table_three c
where a.id = b.id
and a.id = c.id
and a.id in
  (select id from trans where id > (select max(id) from trans) - 100);

现在查询速度很慢。解释计划显示了对B和C的全表扫描。现在,如果我单独评估嵌套查询并将其替换为逗号分隔ID列表,则查询速度非常快。这对我来说显而易见 - 它只有100行连接在一起,所以当然它会比通过首先加入A和B来回答查询更快。

从概念上讲,我理解查询优化器正试图找到一个好的执行计划,但在这种情况下,它似乎做得很糟糕。有没有办法强制DBMS首先执行嵌套查询?可能通过使用提示?

由于

5 个答案:

答案 0 :(得分:2)

您的方法可能会阻止从反式中选择最多100行的事实。

试试这个:

with cte_last_trans as (
  select id
  from   (select   id
          from     trans
          where    id > (select max(id)-100 from trans)
          order by id desc)
  where  rownum <= 100)
select a.id,
       b.field_one,
       c.field_two
from   cte_last_trans a,
       table_two      b,
       table_three    c
where  a.id = b.id
and    a.id = c.id

顺便问一下,您是否考虑过并非所有id值都存在的可能性?如果要返回100行,请使用:

with cte_last_trans as (
  select id
  from   (select   id
          from     trans
          order by id desc)
  where  rownum <= 100)
select a.id,
       b.field_one,
       c.field_two
from   cte_last_trans a,
       table_two      b,
       table_three    c
where  a.id = b.id
and    a.id = c.id

答案 1 :(得分:2)

您可以使用NO_MERGE提示,它将强制Oracle首先执行内部查询,而不是尝试合并这两个查询。这是一个例子:

SELECT /*+NO_MERGE(seattle_dept)*/ e1.last_name, seattle_dept.department_name 
  FROM employees e1, 
    (SELECT location_id, department_id, department_name 
       FROM departments 
      WHERE location_id = 1700) seattle_dept 
  WHERE e1.department_id = seattle_dept.department_id;



select /*+ no_merge(inner) */ a.id, b.field_one, c.field_two
  from trans a, 
       table_two b, 
       table_three c, 
       (select id from trans where id > (select max(id) from trans) - 100) inner
 where a.id = b.id
   and a.id = c.id
   and a.id = inner.id;

答案 2 :(得分:0)

  select a.id, b.field_one, c.field_two
 from trans a, table_two b, table_three c
 where a.id = b.id
 and a.id = c.id
 and a.id between (select max(id)-100 from trans) and (select max(id) from trans)

答案 3 :(得分:0)

如果您尝试删除in,似乎没有做任何事情吗?

select a.id, b.field_one, c.field_two
from trans a, table_two b, table_three c
where a.id = b.id
and a.id = c.id
and a.id > (select max(id) from trans) - 100;

答案 4 :(得分:0)

您可以简单地过滤主要转换表中的100条记录,而不是一次又一次地加入它。

select a.id, b.field_one, c.field_two
from 
(select id from (select id, row_number() over(order by id desc) rn 
from trans) where rn <=100) a, 
table_two b, table_three c
where a.id = b.id
and a.id = c.id;