2025年9月25日: PostgreSQL 18 发布!
支持的版本: 当前 (18) / 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时将其标记为IMMUTABLE可能会允许它在计划期间被过早地折叠成一个常量,导致在计划的后续使用中重用过期的值。在使用预编译语句或使用缓存计划的函数语言(如PL/pgSQL)时,这是一个危险。

对于用SQL或任何标准过程语言编写的函数,易变性类别还决定了另一个重要属性,即由调用函数的SQL命令所做的任何数据更改的可见性。一个VOLATILE函数将看到这些更改,而STABLEIMMUTABLE函数则不会。此行为是通过MVCC的快照机制实现的(参见第 13 章):STABLEIMMUTABLE函数使用调用查询开始时建立的快照,而VOLATILE函数在它们执行的每个查询开始时获取一个新的快照。

注意

用C语言编写的函数可以随意管理快照,但通常最好也让C函数以这种方式工作。

由于这种快照机制,只包含SELECT命令的函数可以安全地标记为STABLE,即使它查询的表可能正在被并发查询修改。PostgreSQL将使用为调用查询建立的快照执行STABLE函数的所有命令,因此在整个查询中它将看到数据库的固定视图。

IMMUTABLE函数中的SELECT命令也使用相同的快照机制。通常不建议在IMMUTABLE函数中查询数据库表,因为如果表内容发生变化,不可变性就会被打破。然而,PostgreSQL不强制您这样做。

一个常见的错误是将函数标记为IMMUTABLE,而它的结果却依赖于配置参数。例如,一个操作时间戳的函数很可能会根据TimeZone设置得出不同的结果。为了安全起见,这类函数应该被标记为STABLE

注意

PostgreSQL要求STABLEIMMUTABLE函数除了SELECT命令之外不包含任何SQL命令,以防止数据修改。(这不是一个完全可靠的测试,因为这类函数仍然可以调用修改数据库的VOLATILE函数。如果你这样做,你会发现STABLEIMMUTABLE函数不会注意到被调用函数所应用的数据库更改,因为它们对它的快照是隐藏的。)

提交更正

如果您在文档中看到任何不正确、与您对特定功能的使用经验不符或需要进一步解释的内容,请使用此表单报告文档问题。