每个函数都有一个易失性级别(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() 函数。
在那些简单的规划后马上执行的交互查询上,STABLE 和 IMMUTABLE 没有什么区别:函数是在规划开始时执行还是在查询开始时执行的差别并不大。但是如果规划被保存并且后来被重用,那差别可就大了。如果把一个函数标记为 IMMUTABLE 而它实际上又不是,那么就会导致在随后使用其规划的时候用上一个不完整的数值。如果在使用预先准备好语句或者使用一种缓冲规划的函数语言(比如 PL/pgSQL),那么后果可能很严重。
因为 MVCC 的快照行为(参阅章12),一个只包含 SELECT 命令的函数可以安全地标记为 STABLE ,即使它所选择的表可能会被其它并发查询修改也一样。PostgreSQL 将会在执行 STABLE 函数时为调用它的查询建立快照,因此它在该查询的生存期内都会看到一致的数据库视图。还要指出的是,current_timestamp
函数族都是稳定的,因为它们的数值在一个事务里是不改变的。
同样的快照行为也用于 IMMUTABLE 函数里面的 SELECT 命令。通常,在一个 IMMUTABLE 函数里选择一个数据库的表是不明智的,因为如果表的内容改变,那么这种不变性就将改变。不过,PostgreSQL 并不禁止你这样做。
一个常见的错误是把一个函数标记为 IMMUTABLE ,而实际上这个函数的结果依赖某个配置参数。比如,一个操作时间戳的函数可能有依赖于 timezone 设置的结果。为了安全考虑,这样的函数应该标记为 STABLE 。
【注意】在 PostgreSQL 8.0 之前,要求 STABLE 和 IMMUTABLE 函数不能修改数据库这个约束并未由系统强制。版本 8.0 通过要求这类函数不能包含 SELECT 之外的 SQL 命令来强制这个约束。不过,这么做并不是完全防弹的升级,因为这样的函数仍然可以调用那些可能修改数据库的 VOLATILE 函数。如果你这么做的话将会发现 STABLE 或 IMMUTABLE 并不会觉察到被它调用的函数对数据库所做的修改。