文字字符串有效,但变量需要永远

时间:2010-02-10 14:42:46

标签: oracle plsql

我有一个在我有固定值时有效的查询。即:

select
    count(*)
from
    address a
where
    a.primary_name like upper('cambourne court') and
    a.secondary_name like upper('flat 9');

然而,将upper('flat 9')替换为second_name:=upper('flat 9')变量,搜索现在返回'cambourne court'中的所有111个地址。

为什么会这样?

编辑:这是完整的address.sql文件(删除了评论)

declare
    address_details address%rowtype;
    current_loc varchar2(32);

    prime_name varchar2(255);
    prime_number varchar2(255);
    second_name varchar2(255);
    street_name varchar2(255);
    town_name varchar2(255);
    success boolean;

    the_count number;
begin

prime_name:=upper('&&primary_name');
prime_number:=upper('&&primary_number');
second_name:=upper('&&secondary_name');
street_name:=upper('&&street_name');
town_name:=upper('&&town_name');


success:=true;

-- error checking here (removed for brevity)


if success then
    current_loc := 'finding address';
    select
        count(*)
    into
        the_count
    from
        dependency d,
        address a,
        street s
    where
        d.dep_obj_id1 = 2 and
        d.dep_obj_id2 = 1 and   
        a.loc_id = d.dep_id1 and
        s.loc_id = d.dep_id2 and 
        a.primary_name like prime_name and
        a.secondary_name like second_name and
        s.name like street_name and
        s.town like town_name;

end if;

dbms_output.put_line('success: address found '||the_count); 


exception 
    when too_many_rows then 
        dbms_output.put_line('failure: too many rows while '||current_loc); 
    when no_data_found then 
        dbms_output.put_line('failure: no rows found while '||current_loc); 
    when others then
        dbms_output.put_line('failure: general error while '||current_loc); 

end; 
/

更新:我重新启动了SQL * Plus,似乎修复了这个中断。

用实际字符串替换prime_name和second_name意味着代码在不到一秒的时间内运行。使用变量意味着需要2分钟以上。

4 个答案:

答案 0 :(得分:3)

您的症状对应于具有与表中列相同名称的PL / SQL变量。

[编辑] 对于一个不正确的答案的upvote感到有些内疚,所以我试图重现并且没有得到你的结果:

SQL> select * from address
  2  ;

PRIMARY_NAME               SECONDARY_NAME
------------------------------ ------------------------------
CAMBOURNE COURT            FLAT 9
CAMBOURNE COURT            FLAT 10

SQL> declare
  2  second_name varchar2(30) := upper('flat 9');
  3  x pls_integer;
  4  cursor c is
  5  select
  6      count(*)
  7  from address a
  8  where
  9      a.primary_name like upper('cambourne court') and
 10      a.secondary_name like upper('flat 9')
 11  ;
 12  begin
 13  select count(*) into x
 14   from address a
 15  where
 16      a.primary_name like upper('cambourne court') and
 17      a.secondary_name like upper('flat 9');
 18  dbms_output.put_line('literal: '||x);
 19  select count(*) into x
 20   from address a
 21  where
 22      a.primary_name like upper('cambourne court') and
 23      a.secondary_name like second_name;
 24  dbms_output.put_line('variable: '||x);
 25  end;
 26  /
literal: 1
variable: 1

PL/SQL procedure successfully completed.

答案 1 :(得分:2)

111条记录表明second_name不包含您期望的值;你是如何捕获&&secondary_name的,你能否在省略验证部分之前和之后检查它实际具有的值?从结果看,它似乎包含'%'而不是'flat 9',但我认为你已经检查过了。

速度问题表明优化器正在以改变连接顺序和/或正在使用的索引的方式改变行为。默认情况下,每个street行都可以加入每个address行,每个like记录都有一个Cambourne Court,然后才进行依赖性检查,但它会根据它认为可以的索引而变化很大使用和任何可用的统计数据。区别在于文字,即使您使用explain plan,也没有通配符,因此它可能知道它可以使用primary_name和/或secondary_name上的索引;在变量版本中,它无法知道何时解析查询,因此必须假设更糟糕的情况,即'%'。如果它返回111个地址,它实际上可能会得到它。

如果没有{{1}},很难准确猜出发生了什么,但你可以尝试添加一些优化提示,至少尝试使连接顺序正确,甚至使用索引 - 尽管应该可能如果您的值可以以%开头,则不要留在原地。这可能会告诉你不同的做法。

答案 2 :(得分:1)

解释计划可能具有启发性。 运行后,从v $ sql中找到该statemnet的sql_id

select sql_text, sql_id from v$sql where lower(sql_text) like '%address%street%';

然后将其插入

select * from table(dbms_xplan.display_cursor('1mmy8g93um377'));

你应该在底部看到的是这样的,它会显示计划中是否存在任何奇怪之处(例如,使用其中一个表中的列,使用函数......)。

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - access("A"."LOC_ID"="D"."DEP_ID1" AND "S"."LOC_ID"="D"."DEP_ID2")
   4 - filter(("A"."PRIMARY_NAME" LIKE :B4 AND "A"."SECONDARY_NAME" LIKE 
              :B3))
   6 - filter(("S"."NAME" LIKE :B2 AND "S"."TOWN" LIKE :B1))
   7 - filter(("D"."DEP_OBJ_ID1"=2 AND "D"."DEP_OBJ_ID2"=1))

答案 3 :(得分:1)

亚历克斯指出了可能的原因。表被索引并且使用带有变量的“like”是索引停用的情况。优化器将具有没有通配符或占位符的常量的“like”表达式视为“=”,因此将考虑索引(如果存在)。

删除那些列上的索引,你会得到与常量或变量相同的不良性能。实际上不要这样做,只需自动跟踪并比较计划。

此致