每个函数都有一个易变性分类,可能性包括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
函数不会注意到被调用函数所应用的数据库更改,因为它们对它的快照是隐藏的。)
如果您在文档中看到任何不正确、与您对特定功能的使用经验不符或需要进一步解释的内容,请使用此表单报告文档问题。