Sql 如何让Oracle对此查询有更好的执行计划?

Sql 如何让Oracle对此查询有更好的执行计划?,sql,oracle,query-optimization,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

我们有一些跟踪已处理事务的表。这些表有数百万行。我经常想查看最近的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);
现在查询速度非常慢。explain计划在B和C上显示了一个完整的表扫描。现在,如果我分别计算嵌套查询并用逗号分隔的ID列表替换它,查询速度会非常快。这对我来说似乎很明显——它只有100行要连接在一起,因此当然比首先将A和B连接在一起来回答查询要快

从概念上讲,我理解查询优化器试图找到一个好的执行计划,但在本例中,它似乎做得很糟糕。有没有办法强制DBMS首先执行嵌套查询?可能是通过暗示


谢谢

中的似乎没有任何作用。您尝试过将其删除吗

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;

中的似乎没有任何作用您尝试过将其删除吗

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;

您的方法可能观察到这样一个事实,即从trans中最多只选择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

您的方法可能观察到这样一个事实,即从trans中最多只选择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

您可以使用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;

您可以使用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;

您可以简单地过滤主trans表本身中的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;

您可以简单地过滤主trans表本身中的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;

IN子句没有将结果限制为仅最近的100个插入吗?没有理由在IN子句中使用嵌套select。它所做的只是确保id在大于maxid-100的id列表中。同样的事情也可以通过直接比较id和maxid-100来完成。我同意这样做会更容易。我的意思是它确实做了一些事情,而不是什么都不做。IN子句不把结果限制在最近的100次插入吗?没有理由在IN子句中使用嵌套的select。它所做的只是确保id在大于maxid-100的id列表中。同样的事情也可以通过直接比较id和maxid-100来完成。我同意这样做会更容易。我只是说它确实做了些什么,而不是什么都没有。呃。。。您希望将较低的值作为afet之间的第一个参数。但我不确定这是否有效——乐观主义者不一定会意识到只有大约100行TRAN将被返回。检查一下解释计划,嗯。。。您希望将较低的值作为afet之间的第一个参数。但我不确定这是否有效——乐观主义者不一定会意识到只有大约100行TRAN将被返回。不过,请检查解释计划。我忘了提到我不确定no_merge提示是否适用于in子句。您可能需要重新编写查询才能使用内联查询。您可以在没有提示的情况下禁用。在此处抛出+1,因为这就是我要寻找的答案。。但即使有提示,它仍在执行FTS,因此提示似乎没有真正改变任何东西。您可能需要在嵌套的select上使用提示强制对内部查询进行索引扫描。我忘了提到,我不确定no_merge提示是否适用于in子句。您可能需要重新编写查询才能使用内联查询。您可以在没有提示的情况下禁用。在此处抛出+1,因为这就是我要寻找的答案。。但是,即使有提示,它仍然在执行FTS,因此提示似乎没有真正改变任何东西。您可能需要在嵌套选择上使用提示强制对内部查询进行索引扫描。我让这种方法起作用了-查询执行得非常快。我认为关键是rownum,我让这种方法起作用了——查询执行得非常快。我认为关键是rownum