存储过程中的查询调整

时间:2019-04-16 15:15:05

标签: oracle stored-procedures oracle11g sql-tuning

我需要调整下面提到的这个查询,以避免每次都与存档表合并,因为即使在大多数情况下,数据将出现在第一个表本身中,下面的查询也可能不必要地扫描了存档表。 / p>

    const yourMessage: TokenMessage = {
        token: '',
        // ...
    };

    function iReturnMessage(): TokenMessage {
        return {
            token: ''
            // ...
        };
    }

2 个答案:

答案 0 :(得分:1)

您可以对主表添加not exists检查作为归档分支的一部分:

select o.status_code,o.order_id
from order o
where o.order_id=p_order_id 
union all
select a.status_code,a.order_id
from order_archive a
where a.order_id=p_order_id
and not exists (
  select *
  from order o
  where o.order_id=p_order_id 
);

这现在当然意味着它将查看两个分支中的主表;但它可能仍然更快,具体取决于您的数据,索引,统计信息等。


另一个选项可以防止查看存档表(如果ID存在于主表中),首先对主表进行简单计数,然后有条件地打开ref游标以针对两个可能的查询之一每个表一张;即在PL / SQL中而不是在SQL中进行分支,从而完全消除了联合。像这样:

procedure status(p_order_id in varchar2, p_stat out sys_refcursor) is
  l_count pls_integer;
begin
  select count(*) into l_count
  from order o
  where o.order_id=p_order_id;

  if l_count > 0 then
    -- exists in main table so only query that
    open p_stat for
      select o.status_code,o.order_id
      from order o
      where o.order_id=p_order_id;
  else
    -- does not exist in main table so only query archive
    open p_stat for
      select a.status_code,a.order_id
      from order_archive a
      where a.order_id=p_order_id;
  end if;
end status;
/

如果ID在主表中不是唯一的,则可以将and rownum = 1添加到初始查询中,以便在找到任何匹配的行后立即停止(因此l_count最多为1)而不是获得准确的计数,因为您实际上并不在乎所找到的实际数字。

  

我们可以在订单表的数据集上使用提取和循环吗?如果找到,则仅用于存档表

并非如此,因为获取将占用结果的第一行。这是一个非常人为的例子:

var rc refcursor;

declare
  x varchar2(1);
begin
  open :rc for
    select 'A' from dual where 1 = 0
    union all
    select 'B' from dual where 1 = 0;
  fetch :rc into x;
  if :rc%notfound then
    open :rc for select 'C' from dual;
  end if;
end;
/

PL/SQL procedure successfully completed.

print rc

'
-
C

通过这种设置,“ main”查询找不到任何行(由于1 = 0检查);提取游标后,游标是notfound,因此它将重新打开游标以进行“存档”查询,并且调用方会按预期看到该游标。

但是如果第一个查询确实返回行:

declare
  x varchar2(1);
begin
  open :rc for
    select 'A' from dual where 1=1
    union all
    select 'B' from dual where 1=1;
  fetch :rc into x;
  if :rc%notfound then
    open :rc for select 'C' from dual;
  end if;
end;
/

PL/SQL procedure successfully completed.

print rc

'
-
B

然后在获取游标之后是found,因此它不会为存档查询重新打开游标,并且现有的ref游标将传递回调用方。但是游标的第一行已经被提取到x变量中并且丢失了,因此调用者不再能看到该行。 print仅显示带有'B'的一行,而缺少应该包含'A'的另一行。

要使呼叫者仍然看到两行,则必须使用“ main”查询重新打开光标:

declare
  x varchar2(1);
begin
  open :rc for
    select 'A' from dual where 1=1
    union all
    select 'B' from dual where 1=1;
  fetch :rc into x;
  if :rc%found then
    open :rc for
      select 'A' from dual where 1=1
      union all
      select 'B' from dual where 1=1;
  else
    open :rc for select 'C' from dual;
  end if;
end;
/

PL/SQL procedure successfully completed.

print rc

'
-
A
B

从逻辑上讲,我想这实际上与我在上面进行的初始计数相同,但是不清楚。

答案 1 :(得分:0)

第一步很容易使用from my_module import x, y, z, zz, zzz ,因为UNION ALL保证了两个表的UNION结果。

使用distinct将从第一个表中获取数据,并且只有从其中获取了所有行之后,才可以访问第二个表。

仅当两个表中的行都分别与UNION ALLUNION相同并且您需要对其进行分解时,才需要

order_id。这不是典型的情况。