2014-01-28 26 views
6

考慮一個確定性的功能,如:確定性函數何時使用之前的計算值?

CREATE OR REPLACE FUNCTION SCHEMA.GET_NAME(ss_id nvarchar2 
        ) RETURN nvarchar2 DETERMINISTIC IS 
    tmpVar nvarchar2(500); 
    BEGIN 

     select name into tmpvar from logistics.organization_items 
     where id = ss_id ; 
     return tmpvar ; 

    END ss_name; 

使用蟾蜍我叫SCHEMA.GET_NAME(1)並返回A。然後,我將表中的值從A更改爲B,並調用SCHEMA.GET_NAME(1)返回B

這是一個很好的結果。但我怕根據this page in the documentation沒有被更新的值,這說的:

當Oracle數據庫在遇到這些情形之一的確定性函數,它會嘗試使用先前的計算結果可能時,而不是重新執行功能。如果隨後更改函數的語義,則必須手動重建所有從屬函數索引和實例化視圖。

在什麼情況下會的GET_NAME(1)返回一箇舊的緩存值(A而不是B)的值?

回答

6

如果您從表中選擇,那麼函數的結果是而不是確定性的。 A deterministic system是一個總是產生相同的輸出,給定相同的初始條件。

可以更改表中的信息,因此從表中選擇的函數不是確定性的。從PL/SQL Language Reference引用:

不要指定該子句定義使用包變量或以任何方式可能影響函數的返回結果是訪問數據庫的功能。如果數據庫選擇不重新執行該功能,則不會捕獲這樣做的結果。

換句話說,Oracle並不保證函數的結果是準確的(它們可能是)。如果你的桌子是靜態的,並且不可能改變,那麼它應該沒問題,但這不是我想要依賴的東西。要回答你的問題,不要以爲Oracle會在同一個事務/會話中返回除緩存值以外的任何內容。

如果您需要加快速度,有兩種方法。首先,請檢查您是否有ID的索引!

  1. 只需加入此表。如果你的功能只有這個,那麼就不需要該功能存在。

  2. 使用scalar sub-query caching(不一定可能,但值得嘗試)。

    select (select get_name(:id) from dual) 
        from your_table 
    

    Oracle將創建函數結果的內存散列,如結果緩存。如果你多次執行相同的功能,那麼Oracle將打擊緩存而不是功能。

+0

感謝您的完整答案。此外,我還喜歡現在多久刷新此功能的緩存?在執行查詢的範圍內?一段時間?關於釋放緩存的內存需求? – mehrandvd

+0

或者也許緩存僅在會話期間有效。所以對於新的會議,它會更新? – mehrandvd

+2

@mehrandvd - 我不知道這個特性的細節,但是我有一種感覺,就像其他大多數優化一樣:優化器感覺像是這樣。換句話說,有** NO WAY **來保證它被緩存/更新(有一些概率,主要是緩存,但沒有保證)。這與運行沒有ORDER BY的查詢類似 - 你得到優化器決定給你的命令,而不是你想要的。 –

1

Ben的回答概括起來很好,我只想補充一點,這樣你用你的函數內部DETERMINISTIC關鍵字是不正確的 - 同時考慮您從表中讀取的值,然後返回對用戶也一樣。

確定性函數應該用在您要在固定輸入上計算表達式的情況下,例如,當您需要返回子字符串或輸入字符串的大小寫。以編程方式,您知道對於相同的輸入,小寫函數將始終返回相同的值,因此您希望緩存結果(使用deterministic關鍵字)。

當您從表中讀取值時,Oracle無法知道列中的值沒有更改,因此它傾向於重新執行該函數而不依賴於緩存的結果(這很有意義)

0

你能爲你的函數添加一個時間戳參數嗎?然後將sysdate傳遞給你調用它的任何地方的函數。

通過這種方式,您可以有效地緩存結果,並且避免一般在給定事務中返回相同值時反覆運行該函數。

0

埃雷茲的評論是我正在尋找的答案。 在執行查詢或plsql-unit之前,可以使用此解決方案強制在重置函數的ret值(例如,更改包var)後再次執行該函數。 我使用這個: select ... from big_table_vw;

其中

從BIG_TABLE 其中last_mutated> = get_date創建視圖big_table_vw 作爲 選擇...(分析函數) ();

在我的情況下,big_table_vw包含防止Oracle將謂詞推入視圖的窗口函數。

+0

更正:我只是試圖多次調用該函數沒有時間戳,結果是該函數被調用一次爲每個sql語句。所以,不需要時間戳。 – Rik

0

這是一個長期回答的後續問題,但我只是想補充一點,Oracle確實提供了具有可變依賴項的函數的緩存機制。 RESULT_CACHEDETERMINISTIC的替代方案,允許Oracle在任何時候修改引用的對象時放棄緩存的函數結果。

通過這種方式,您可以緩存代價很低的更新對象的代價高昂的計算,並確信緩存的結果不會返回錯誤的結果。

下面是使用神話怪物一個例子:當像下方的情況下發生,任何現有的高速緩存被無效,然後一個新的高速緩存可以重建

CREATE TABLE MONSTER (
    MONSTER_NAME VARCHAR2(100) NOT NULL PRIMARY KEY 
); 

INSERT INTO MONSTER VALUES ('Chthulu'); 
INSERT INTO MONSTER VALUES ('Grendel'); 
INSERT INTO MONSTER VALUES ('Scylla'); 
INSERT INTO MONSTER VALUES ('Nue'); 
COMMIT; 

CREATE OR REPLACE PACKAGE MONSTER_PKG 
IS 
    FUNCTION IS_THIS_A_MONSTER(P_MONSTER_NAME IN VARCHAR2) 
    RETURN BOOLEAN RESULT_CACHE; 
END MONSTER_PKG; 
/

CREATE OR REPLACE PACKAGE BODY MONSTER_PKG 
IS 
    FUNCTION IS_THIS_A_MONSTER(P_MONSTER_NAME IN VARCHAR2) 
    RETURN BOOLEAN 
    RESULT_CACHE RELIES_ON (MONSTER) 
    IS 
    V_MONSTER_COUNT NUMBER(1, 0) := 0; 
    BEGIN 
     SELECT COUNT(*) 
     INTO V_MONSTER_COUNT 
     FROM MONSTER 
     WHERE MONSTER_NAME = P_MONSTER_NAME; 
     RETURN (V_MONSTER_COUNT > 0); 
    END; 
END MONSTER_PKG; 
/

BEGIN 
    DBMS_OUTPUT.PUT_LINE('Is Kraken initially a monster?'); 
    IF MONSTER_PKG.IS_THIS_A_MONSTER('Kraken') 
    THEN 
    DBMS_OUTPUT.PUT_LINE('Kraken is initially a monster'); 
    ELSE 
    DBMS_OUTPUT.PUT_LINE('Kraken is not initially a monster'); 
    END IF; 
    INSERT INTO MONSTER VALUES ('Kraken'); 
    COMMIT; 
    DBMS_OUTPUT.PUT_LINE('Is Kraken a monster after update?'); 
    IF MONSTER_PKG.IS_THIS_A_MONSTER('Kraken') 
    THEN 
    DBMS_OUTPUT.PUT_LINE('Kraken is now a monster'); 
    ELSE 
    DBMS_OUTPUT.PUT_LINE('Kraken is not now a monster'); 
    END IF; 
END; 
/

是Kraken最初是一個怪物?
海怪最初並不是怪物
海妖更新後是怪物嗎?
Kraken現在是一個怪物

相關問題