ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
# 35.6\. 函数易失性范畴 每个函数都有一个_易失性_级别`VOLATILE`,`STABLE`或者 `IMMUTABLE`。 如果[CREATE FUNCTION](#calibre_link-4)命令没有明确声明范畴的话, `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快照行为实现(参阅[Chapter 13](#calibre_link-444)): `STABLE`和`IMMUTABLE`函数使用快照 确立为调用查询的开始, 而`VOLATILE`函数每个查询执行开始时获得一个新的快照。 > **Note:** 用C写的函数管理快照然而是他们想要的,但是使C函数这样进行工作往往是一个好主意。 因为快照行为, 一个只包含`SELECT`命令的函数可以安全地标记为`STABLE`, 即使它所选择的表可能会被其它并发查询修改也一样。 PostgreSQL将会在执行`STABLE` 函数时为调用它的查询建立快照, 因此它在该查询的生存期内都会看到一致的数据库视图。 同样的快照行为也用于`IMMUTABLE`函数里面的`SELECT`命令。 通常,在一个`IMMUTABLE`函数里选择一个数据库的表是不明智的, 因为如果表的内容改变,那么这种不变性就将改变。不过, PostgreSQL并不禁止你这样做。 一个常见的错误是把一个函数标记为`IMMUTABLE`, 而实际上这个函数的结果依赖某个配置参数。比如, 一个操作时间戳的函数可能有依赖于[TimeZone](#calibre_link-793)设置的结果。 为了安全考虑,这样的函数应该标记为`STABLE`。 > **Note:** 在PostgreSQL之前,要求`STABLE`和 `IMMUTABLE` 函数不能修改数据库这个约束并未由系统强制。版本8.0 通过要求这类函数不能包含`SELECT`之外的 SQL 命令来强制这个约束。 不过,这么做并不是完全防弹的升级, 因为这样的函数仍然可以调用那些可能修改数据库的`VOLATILE`函数。 如果你这么做的话将会发现`STABLE`或者 `IMMUTABLE` 并不会觉察到被它调用的函数对数据库所做的修改。