我应该在函数中使用异常而不是count(*)吗?

时间:2013-04-26 05:45:39

标签: sql performance oracle

我是Oracle的新手,我有两个高频率使用的功能。我想知道它们之间哪个更好。

这一个:

FUNCTION GET_MY_MONEY (myType IN NUMBER) RETURN NUMBER AS
    var_amount   NUMBER;
    var_result   NUMBER;
BEGIN
    var_result := 0;
    var_amount := 0;
    SELECT amount INTO var_amount FROM mytable WHERE type = myType AND sysdate >= date_from AND sysdate <= date_to;
    var_result := var_amount*1000;
    RETURN var_result;
EXCEPTION
    WHEN OTHERS THEN
        RETURN 0;
END;

或者这个:

FUNCTION GET_MY_MONEY (myType IN NUMBER) RETURN NUMBER AS
    var_count    NUMBER;
    var_amount   NUMBER;
    var_result   NUMBER;
BEGIN
    var_result := 0;
    var_count := 0;
    var_amount := 0;
    SELECT count(*) INTO var_count FROM mytable WHERE type = myType AND sysdate >= date_from AND sysdate <= date_to;
    IF (var_count > 0) THEN
        SELECT amount INTO var_amount FROM mytable WHERE type = myType AND sysdate >= date_from AND sysdate <= date_to;
        var_result := var_amount*1000;
        RETURN var_result;
    ELSE RETURN 0; END IF;
EXCEPTION
    WHEN OTHERS THEN
        RETURN 0;
END;

哪个性能更好?它们被调用后返回的速度更快?

提前致谢。

3 个答案:

答案 0 :(得分:4)

一般来说,这取决于。您多久调用一次函数并传入导致查询返回0行的myType值?

如果在99.9%的调用中,查询将返回正好1行,则第二种方法将运行查询以执行两次。虽然第二次调用可能不会导致函数的价格比第一次高两倍,因为您感兴趣的块几乎可以保证缓存,第二种方法几乎肯定会慢得多。

另一方面,如果大部分调用涉及不返回行的myType值,则第二种方法通常不必再次执行查询。并且第一种方法将导致处理异常的开销很大一部分时间几乎肯定会比第二次查询更昂贵。

在大多数情况下,基于返回0行的概率,更有效的解决方案将是显而易见的。大多数情况下,仅当调用者非常确信它们将传入的myType值有效时才调用该函数,因此第一种方法最终会更有效。随着导致找到0行的调用的比例增加,第二种方法变得更有效。该行的位置取决于许多因素,尤其是您的表,数据,硬件和Oracle版本。您需要运行基准测试,以确定您的特定代码的分界线是10%还是20%或90%。

答案 1 :(得分:2)

问题已被修改,但如果有多行,则SELECT amount INTO var_amount ...将失败。(可能您想选择sum(amount))。

第一种方法更好,因为:

  • 更容易理解
  • 你不扫描桌子两次。
  • 在第二个 你得到var_amount但你没有使用它。
  • 计数(*)没用,你只能select nvl(sum(amount),0) as amount

但是,您必须为日期变量(date_fromdate_to)指定一个值或将它们作为参数。 你可以返回一个表达式,即:RETURN var_count*1000;

答案 2 :(得分:1)

另外:

DECLARE
  v_retVal NUMBER:= 0;
  v_sal    NUMBER:= 0;
BEGIN
  SELECT nvl(sum(sal),0) as sal INTO v_sal 
    FROM scott.emp 
   WHERE deptno = 40; -- no data for deptno = 40 

   -- No exception needed, function will always return 0 or value --
   v_retVal:= (CASE WHEN v_sal = 0 THEN 0 ELSE v_sal*1000 END);

   dbms_output.put_line (v_retVal);

  -- RETURN v_retVal;
END;
/