为什么我不能在存储函数中使用Like?

时间:2013-04-25 06:58:54

标签: oracle stored-functions

我正在Oracle中编写一些存储的函数。其中一个是一个非常基本的函数,它将字符串作为参数并返回另一个字符串。这是我的功能:

CREATE OR REPLACE 
FUNCTION get_mail_custcode (
    custcodeParam IN customer_table.custcode%TYPE)
    RETURN VARCHAR2
IS
    mail_rc   contact_table.email%TYPE;
BEGIN
    SELECT   cc.email
      INTO   mail_rc
      FROM   contact_table cc, customer_table cu
     WHERE       cu.customer_id = cc.customer_id
             AND cu.custcode like custcodeParam ;

    RETURN mail_rc ;
END;

所以它不起作用..该功能似乎运行良好但是没有任何结束执行..该功能是工作时间和时间,我在2或3分钟后手动取消操作(此查询通常即时结果)。 在反复写入查询后,我最终(并随机)将cu.custcode like custcodeParam更改为cu.custcode = custcodeParam并且它正在运行!!

所以我的问题是为什么?为什么我不能在存储函数中使用like比较器?为什么这不会导致错误,但函数运行无限期。

感谢。

1 个答案:

答案 0 :(得分:3)

游标在Oracle中的处理方式完全相同。函数中的查询将与您通过SQL * Plus手动输入的查询完全相同。

但是,您的示例中可能有所不同的是Oracle如何使用变量。以下两个查询与优化器根本不同:

SELECT * FROM tab WHERE code LIKE 'FOO%';

variable v_code VARCHAR2(4)
EXEC :v_code := 'FOO%';
SELECT * FROM tab WHERE code LIKE :v_code;

在第一种情况下,优化器会查看常量FOO%,并且可以立即告诉code上的索引非常适合通过索引RANGE SCAN快速检索行。

在第二种情况下,优化器必须考虑:V_CODE不是常量。优化器的目的是确定将由同一查询的连续执行共享的查询计划(因为计算计划很昂贵)。

优化程序的行为取决于您的Oracle版本:

  • 在旧的Oracle版本(9i及之前版本)中,忽略变量的值以构建计划。实际上,Oracle必须构建一个有效的计划,无论传递给它的价值如何。在您的情况下,这可能会导致完整扫描,因为Oracle必须采用风险最小的选项并认为FOO%可能是%FOO的值(后者无法通过索引范围扫描)。
  • 在10g中,Oracle引入了bind peeking:现在,优化器可以访问变量的值并生成合适的计划。主要问题是,在大多数情况下,查询只能有一个计划,这意味着传递给函数的第一个变量的值将强制执行所有进一步执行的计划。如果要传递的第一个值是%FOO,则可能会选择全面扫描。
  • 在11g中,Oracle有“intelligent cursor sharing”:单个查询可以共享多个计划,在上面的示例中,值FOO%将使用RANGE SCAN,而%FOO可能使用全扫描。

您使用的是哪个版本的Oracle?


更新

在10g和之前,如果经常使用没有通配符的函数,你应该重写它以确认优化器的行为:

BEGIN
   IF instr(custcodeParam, '%') > 0 OR instr(custcodeParam, '_') > 0 THEN
      SELECT cc.email
        INTO mail_rc
        FROM contact_table cc, customer_table cu
       WHERE cu.customer_id = cc.customer_id
         AND cu.custcode LIKE custcodeParam;
   ELSE
      SELECT cc.email
        INTO mail_rc
        FROM contact_table cc, customer_table cu
       WHERE cu.customer_id = cc.customer_id
         AND cu.custcode = custcodeParam;
   END IF;

   RETURN mail_rc;
END;