Question

I'm writting some stored functions in Oracle. One of these is a really basic function who take a string as parameter and return an another string. Here is my function:

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;

So it's not working.. The function seems to work well but is executed without any end.. The function is working time and time, I manually cancel the operation after 2 or 3 minutes (this query give normally instant result). After writing the query again and again I finally (and randomly) change the cu.custcode like custcodeParam into a cu.custcode = custcodeParam and it is working!!

So my question is why? Why I can't use a like comparator in a stored function? Why this makes no error but the function run indefinitly.

Thanks.

Était-ce utile?

La solution

Cursors are all treated identically in Oracle. A query in a function will be treated exactly the same as a query you enter manually through SQL*Plus.

However, what may differ in your example is how Oracle works with variables. The following two queries are fundamentally different to the optimizer:

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

and

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

In the first case the optimizer looks at the constant FOO% and can instantly tell that an index on code is perfectly suited to retrieve the rows rapidly via an index RANGE SCAN.

In the second case, the optimizer has to consider that :V_CODE is not constant. The purpose of the optimizer is to determine a plan for a query that will be shared by successive executions of the same query (because computing a plan is expensive).

The behaviour of the optimizer will depend upon your version of Oracle:

  • In old Oracle versions (9i and before), the value of the variable was ignored to build the plan. In effect Oracle had to build a plan that would be efficiently, whatever value was passed to it. In your case this likely would result in a full scan because Oracle had to take the least risky option and consider that FOO% was as likely a value as %FOO (the latter can't be accessed efficiently via an index range scan).
  • In 10g Oracle introduced bind peeking: now the optimizer can access the value of the variable and produce a suitable plan. The main problem is that in most cases a query can only have one plan, which means that the value of the first variable ever passed to the function will force the execution plan for all further executions. If the first value to be passed is %FOO, a FULL SCAN will likely be chosen.
  • In 11g, Oracle has "intelligent cursor sharing": a single query can share more than one plan, in the example above the value FOO% would use a RANGE SCAN while %FOO would probably use a FULL SCAN.

What version of Oracle are you using?


update

In 10g and before if this function is used often without wildcards you should rewrite it to acknowledge the optimizer behaviour:

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;
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top