38.7. 功能波动类别

每个函数都有一个* volatility *类别,可能性为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确实不是 true 的时候将其标记为IMMUTABLE,则可能会使它在计划期间过早地折叠为常数,从而导致过时的值在计划的后续使用中被重新使用。当使用准备好的语句或使用缓存计划的函数语言(例如 PL/pgSQL)时,这是一种危险。

对于用 SQL 或任何标准过程语言编写的函数,还有第二个重要属性,由波动性类别确定,即,调用该函数的 SQL 命令所做的任何数据更改的可见性。 VOLATILE函数将看到此类更改,而STABLEIMMUTABLE函数则不会。此行为是通过 MVCC 的快照行为实现的(请参见Chapter 13):STABLEIMMUTABLE函数使用在调用查询开始时构建的快照,而VOLATILE函数在它们执行的每个查询的开始处获取一个新的快照。

Note

用 C 编写的函数可以根据需要 Management 快照,但是使 C 函数也这样工作通常是一个好主意。

由于存在这种快照行为,即使仅从SELECT命令执行的功能中选择并发查询进行修改的表,也可以将其安全标记为STABLE。 PostgreSQL 将使用为调用查询构建的快照执行STABLE函数的所有命令,因此它将在整个查询中看到数据库的固定视图。

IMMUTABLE函数中的SELECT命令使用相同的快照行为。通常根本不选择IMMUTABLE函数中的数据库表,因为如果表内容发生更改,则不变性将被破坏。但是,PostgreSQL 不会强制您不要这样做。

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

Note

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