每个函数都有一个易变性分类,可能性包括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
,可能会导致该函数在计划期间过早地折叠为常量,从而导致在后续使用该计划期间使用过时的值。这在使用预处理语句或使用缓存计划的函数语言(如 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
而不是 IMMUTABLE
。
PostgreSQL 要求 STABLE
和 IMMUTABLE
函数不包含 SELECT
以外的 SQL 命令,以防止数据修改。(这不是一个完全可靠的测试,因为此类函数仍然可以调用修改数据库的 VOLATILE
函数。如果您这样做,您会发现 STABLE
或 IMMUTABLE
函数没有注意到被调用函数应用的数据库更改,因为这些更改对它的快照是隐藏的。)
如果您在文档中看到任何不正确的内容,与您对特定功能的体验不符或需要进一步澄清,请使用 此表格 报告文档问题。