支援版本: 目前 (17) / 16 / 15 / 14 / 13
開發版本: devel
不支援的版本: 12 / 11 / 10 / 9.6 / 9.5 / 9.4 / 9.3 / 9.2 / 9.1 / 9.0 / 8.4 / 8.3 / 8.2 / 8.1 / 8.0

36.7. 函數穩定性分類 #

每個函數都有一個穩定性分類,可能的選項為 VOLATILESTABLEIMMUTABLE。如果 CREATE FUNCTION 指令未指定類別,則預設值為 VOLATILE。穩定性類別是對最佳化器關於函數行為的承諾。

  • VOLATILE 函數可以執行任何操作,包括修改資料庫。它可以在連續呼叫中使用相同的引數傳回不同的結果。最佳化器不會對這類函數的行為做出任何假設。使用不穩定函數的查詢將在需要其值的每一列重新評估該函數。

  • STABLE 函數無法修改資料庫,並且保證在單一陳述式中,對於所有列的相同引數,傳回相同的結果。此類別允許最佳化器將函數的多個呼叫最佳化為單一呼叫。特別是,在索引掃描條件中使用包含此類函數的運算式是安全的。(由於索引掃描只會評估比較值一次,而不是在每一列評估一次,因此在索引掃描條件中使用 VOLATILE 函數是無效的。)

  • IMMUTABLE 函數無法修改資料庫,並且保證永遠對於相同的引數傳回相同的結果。此類別允許最佳化器在查詢使用常數引數呼叫函數時預先評估該函數。例如,類似 SELECT ... WHERE x = 2 + 2 的查詢可以在看到時簡化為 SELECT ... WHERE x = 4,因為整數加法運算子底層的函數標記為 IMMUTABLE

為了獲得最佳的最佳化結果,您應該使用對函數有效的最嚴格的穩定性類別來標記函數。

任何具有副作用的函數必須標記為 VOLATILE,這樣對它的呼叫就不能被最佳化掉。即使是沒有副作用的函數,如果其值可以在單一查詢中變更,也需要標記為 VOLATILE;一些範例包括 random()currval()timeofday()

另一個重要的範例是 current_timestamp 函數系列符合 STABLE 的資格,因為它們的值在交易中不會變更。

在考慮規劃並立即執行的簡單互動式查詢時,STABLEIMMUTABLE 類別之間的差異相對較小:函數在規劃期間執行一次還是在查詢執行啟動期間執行一次並不重要。但是,如果儲存計劃並在稍後重複使用,則會有很大的差異。如果將函數標記為 IMMUTABLE 但實際上並非如此,可能會導致它在規劃期間過早地摺疊成常數,導致在後續使用計劃期間重複使用過時的值。在使用預備陳述式或使用快取計劃的函數語言(例如 PL/pgSQL)時,這是一個危險。

對於以 SQL 或任何標準程序語言編寫的函數,穩定性類別決定了第二個重要的屬性,即呼叫函數的 SQL 指令所做的任何資料變更的可見性。VOLATILE 函數將會看到這些變更,而 STABLEIMMUTABLE 函數則不會。此行為是使用 MVCC 的快照行為實作的(請參閱第 13 章):STABLEIMMUTABLE 函數使用在呼叫查詢開始時建立的快照,而 VOLATILE 函數在每次執行查詢時取得一個新的快照。

注意

用 C 語言編寫的函數可以隨意管理快照,但通常最好讓 C 函數也以這種方式工作。

由於這種快照行為,即使包含僅有 SELECT 指令的函數,也可以安全地標記為 STABLE,即使它從可能正在被並行查詢修改的表格中進行選擇。PostgreSQL 將使用為呼叫查詢建立的快照執行 STABLE 函數的所有指令,因此它將在整個查詢中看到資料庫的固定視圖。

對於 SELECT 指令,在 IMMUTABLE 函式中會使用相同的快照行為。一般來說,在 IMMUTABLE 函式中從資料庫表格進行選取是不明智的,因為如果表格內容發生變更,不變性將會被破壞。然而,PostgreSQL 並未強制禁止您這麼做。

常見的錯誤是將函式標記為 IMMUTABLE,但其結果取決於設定參數。例如,處理時間戳記的函式,其結果可能取決於 TimeZone 設定。為了安全起見,此類函式應改為標記為 STABLE

注意

PostgreSQL 要求 STABLEIMMUTABLE 函式除了 SELECT 之外,不得包含其他 SQL 指令,以防止資料修改。(這並非完全萬無一失的測試,因為此類函式仍然可以呼叫修改資料庫的 VOLATILE 函式。如果您這樣做,您會發現 STABLEIMMUTABLE 函式不會注意到被呼叫函式所套用的資料庫變更,因為這些變更對其快照是隱藏的。)

提交更正

如果您在文件中發現任何不正確、與您使用特定功能的經驗不符,或需要進一步釐清的地方,請使用此表單來回報文件問題。