2013-04-25 85 views
1

我在寫一些存儲在Oracle中的函數。其中之一是一個非常基本的函數,它將一個字符串作爲參數並返回另一個字符串。這裏是我的功能:爲什麼我不能在存儲函數中使用Like?

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比較器?爲什麼這沒有任何錯誤,但功能無限期地運行。

謝謝。

+0

是否有'custcode'指數?你在做什麼是有效的,但是也許Oracle正在爲函數中的「like」版本(過濾器在綁定變量上)選擇一個不同的執行計劃,以便在獨立運行時(它沒有綁定並且它可以決定訪問索引會更快)。但除非表格很大,否則我不會期望看到那麼大的差別。真的,你需要跟蹤兩者,看看發生了什麼。另外,你是否真的在帕爾姆傳球?如果不是,那''''是毫無意義的,如果你是,你是不是冒着多個會導致ORA-02112的比賽冒險? – 2013-04-25 07:16:37

+0

@AlexPoole我沒有通過參數傳遞通配符,所以我會讓平等,但我只是好奇這樣的區別。我看了兩個版本的執行計劃,但沒有區別。在custcode列上有一個索引,他在這兩種情況下都使用 – bAN 2013-04-25 07:25:53

回答

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不得不採取風險最小的選項,並認爲FOO%%FOO(後者無法通過索引範圍掃描有效訪問)的值相同。
  • 在10g Oracle中引入bind peeking:現在優化器可以訪問變量的值併產生一個合適的計劃。主要的問題是,在大多數情況下,查詢只能有一個計劃,這意味着第一個傳遞給該函數的變量的值將強制所有進一步執行的執行計劃。如果要傳遞的第一個值是%FOO,則可能會選擇FULL SCAN。
  • 在11g中,Oracle具有「intelligent cursor sharing」:單個查詢可以共享多個計劃,在上面的示例中,值FOO%將使用範圍掃描,而%FOO可能會使用FULL SCAN。

您使用的是哪個版本的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; 
+0

感謝您的迴應!我使用的是Oracle 10g,因此可以作爲解釋。優化器對適合的執行計劃進行全面掃描?我理解正確嗎? – bAN 2013-04-25 09:00:13

+0

我不認爲10g可以構建兩個不同的計劃(一個用於通配符'%',一個不用),所以我認爲當變量不需要LIKE運算符時使用不同的查詢將是明智的。我更新的答案)。 – 2013-04-25 09:07:55

相關問題