2013-04-26 66 views
1

我是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; 

這是獲得更好的性能?當它們被調用時返回速度更快?

在此先感謝。

+1

一號做法是更好的,當記錄的數量都非常高。因爲count(*)是一個沉重的操作 – 2013-04-26 05:55:25

+1

我會用第一種方法。我認爲你對使用的變量感到困惑。 var_count沒有在第一個聲明,你沒有在第二個中使用var_amount。 – Noel 2013-04-26 05:57:12

+0

這是我在拼寫錯誤。感謝您的指出。 – PhatHV 2013-04-26 06:01:34

回答

4

一般情況下,這取決於。您多久會調用一次該函數並傳遞一個myType值,導致查詢返回0行?

如果在99.9%的調用中,查詢將返回恰好1行,那麼第二種方法將運行查詢以執行兩次。雖然第二次調用可能不會導致函數比第一次更昂貴一倍,因爲您感興趣的塊幾乎可以保證被緩存,但第二種方法幾乎肯定會慢得多。另一方面,如果大部分調用會涉及一個不返回行的myType值,則第二種方法通常不需要再次執行該查詢。第一種方法會導致處理異常的開銷大部分時間幾乎肯定會比第二個查詢更昂貴。

在大多數情況下,基於返回0行的概率,更有效的解決方案將顯而易見。大多數情況下,函數僅在調用者確信他們傳入的myType值有效時才被調用,因此第一種方法最終效率更高。由於導致0行被找到的調用比例增加,第二種方法變得更有效率。那條線將取決於許多因素,其中最重要的因素是您的數據表,您的數據,硬件和Oracle版本。您需要進行基準測試,以確定您的特定代碼的分界線是10%還是20%或90%。

+0

在我的情況下,可能超過80%的查詢將返回0行。但是剩下的20%仍然會花費2倍的時間來掃描桌子(這足以應付大量考慮)。你能解釋爲什麼'處理異常的開銷'更昂貴嗎?我是新手,當Oracle處理異常時,我真的不知道幕後會發生什麼。 – PhatHV 2013-04-26 06:26:00

+3

+1用於分析,但我認爲異常開銷等於零,而在函數的一小部分調用(我不確定,這只是一個賭注)中第二次調用sql引擎時,異常開銷等於零。最好的辦法是選擇nvl(sum(amount),0)。 – 2013-04-26 06:46:01

2

這個問題被編輯了,但如果有多行,SELECT amount INTO var_amount ...將會失敗(可能你想選擇sum(amount))。

第一種方法更好,因爲:

  • 是更爲理解
  • 你不掃描表的兩倍。
  • 在第二個 你得到var_amount但你不使用它。
  • 的COUNT(*)是沒有用的,你可以select nvl(sum(amount),0) as amount

但是,您必須指定一個值的日期變量(date_fromdate_to)或把它們作爲參數。 你可以返回一個表達式,即:RETURN var_count*1000;

+0

是的,特別是在選擇SUM()的情況下,在這種情況下根本不需要捕獲異常。 – 2013-04-26 06:32:23

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; 
/
相關問題