每個函式都有一個易變性分類,可能性包括VOLATILE
、STABLE
或IMMUTABLE
。如果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
,因為它們的值在事務內不會改變。
在考慮計劃並立即執行的簡單互動式查詢時,STABLE
和IMMUTABLE
類別之間的差異很小:函式是在計劃期間執行一次還是在查詢執行啟動時執行一次,差別不大。但是,如果計劃被儲存並在以後重用,則存在巨大差異。當一個函式實際上不是IMMUTABLE
時將其標記為IMMUTABLE
可能會允許它在計劃期間被過早地摺疊成一個常量,導致在計劃的後續使用中重用過期的值。在使用預編譯語句或使用快取計劃的函式語言(如PL/pgSQL)時,這是一個危險。
對於用SQL或任何標準過程語言編寫的函式,易變性類別還決定了另一個重要屬性,即由呼叫函式的SQL命令所做的任何資料更改的可見性。一個VOLATILE
函式將看到這些更改,而STABLE
或IMMUTABLE
函式則不會。此行為是透過MVCC的快照機制實現的(參見第 13 章):STABLE
和IMMUTABLE
函式使用呼叫查詢開始時建立的快照,而VOLATILE
函式在它們執行的每個查詢開始時獲取一個新的快照。
用C語言編寫的函式可以隨意管理快照,但通常最好也讓C函式以這種方式工作。
由於這種快照機制,只包含SELECT
命令的函式可以安全地標記為STABLE
,即使它查詢的表可能正在被併發查詢修改。PostgreSQL將使用為呼叫查詢建立的快照執行STABLE
函式的所有命令,因此在整個查詢中它將看到資料庫的固定檢視。
IMMUTABLE
函式中的SELECT
命令也使用相同的快照機制。通常不建議在IMMUTABLE
函式中查詢資料庫表,因為如果表內容發生變化,不可變性就會被打破。然而,PostgreSQL不強制您這樣做。
一個常見的錯誤是將函式標記為IMMUTABLE
,而它的結果卻依賴於配置引數。例如,一個操作時間戳的函式很可能會根據TimeZone設定得出不同的結果。為了安全起見,這類函式應該被標記為STABLE
。
PostgreSQL要求STABLE
和IMMUTABLE
函式除了SELECT
命令之外不包含任何SQL命令,以防止資料修改。(這不是一個完全可靠的測試,因為這類函式仍然可以呼叫修改資料庫的VOLATILE
函式。如果你這樣做,你會發現STABLE
或IMMUTABLE
函式不會注意到被呼叫函式所應用的資料庫更改,因為它們對它的快照是隱藏的。)
如果您在文件中看到任何不正確、與您對特定功能的使用經驗不符或需要進一步解釋的內容,請使用此表單報告文件問題。