支持的版本: 当前 (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. 函数易变性类别 #

每个函数都有一个易变性分类,其可能性为VOLATILESTABLEIMMUTABLEVOLATILE是默认值,如果CREATE FUNCTION命令没有指定类别。易变性类别是对优化器关于函数行为的承诺。

  • 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命令的函数也可以安全地标记为STABLEPostgreSQL 将使用为调用查询建立的快照执行STABLE函数的所有命令,因此它将在整个查询中看到数据库的固定视图。

相同的快照行为也用于IMMUTABLE函数中的SELECT命令。通常不建议在IMMUTABLE函数中从数据库表中选择,因为如果表内容发生任何更改,不可变性将被破坏。但是,PostgreSQL 不会强制您不这样做。

一个常见的错误是将一个函数标记为IMMUTABLE,而其结果取决于配置参数。例如,一个处理时间戳的函数可能会有取决于TimeZone设置的结果。为了安全起见,应将此类函数标记为STABLE

注意

PostgreSQL 要求STABLEIMMUTABLE函数不能包含SELECT以外的SQL命令,以防止数据修改。(这不是一个完全万无一失的测试,因为此类函数仍然可以调用修改数据库的VOLATILE函数。如果这样做,您会发现STABLEIMMUTABLE函数不会注意到调用的函数应用的数据更改,因为这些更改对其快照是隐藏的。)

提交更正

如果您在文档中看到任何不正确、与您使用特定功能的经验不符或需要进一步澄清的地方,请使用此表格报告文档问题。