Postgresql无法创建to

it2023-06-13  72

PostgreSQL 函数在定义的时候有三个稳定性状态可选,分别简单说明一下:

immutable:非常稳定,任何时候调用,只要函数的参数不变结果就不变。如果参数为常量或者无参数,在生成执行计划时,直接将这类函数替换为常量。一个IMMUTABLE函数不能修改数据库并且使用相同的参数永远返回相同的结果。这种状态的函数允许优化器在一个查询用常量参数调用该函数时提前计算该函数。例如,一个 SELECT … WHERE x = 2 + 2这样的查询可以被简化为 SELECT … WHERE x = 4,因为整数加法操作符底层的函数被标记为IMMUTABLE。

stable:稳定,在一个事务中调用时,只要函数的参数不变结果就不变。STABLE函数无法修改数据库,并且在单个语句中所有行给定相同参数的情况下,返回相同的结果。此类别使优化程序可以将函数的多个调用优化为单个调用。尤其在索引扫描条件下使用包含此类函数的表达式是安全的。 (因为一次索引扫描只会计算一次比较值, 而不是为每一行都计算一次,所以在一个索引扫描条件中不能使用 VOLATILE函数。)

volatile: 不稳定。函数可以执行任何操作,包括修改数据库。在使用相同参数的连续调用中,它可以返回不同的结果。使用该状态函数的查询将在需要其值的每一行重新评估该函数。

hank=# create table tb1 (col_a timestamp without time zone); CREATE TABLE #建索引的时候报错,提示不是immutable状态的函数,因为postgresql只有immutable状态的函数或者操作符才可以创建索引 hank=# create index idx_tb1_time on tb1 (to_char(col_a,'yyyy-mm-dd')); ERROR: functions in index expression must be marked IMMUTABLE #查看函数稳定性 hank=# select proname,proargtypes,provolatile from pg_proc where proname='to_char'; proname | proargtypes | provolatile ---------+-------------+------------- to_char | 1184 25 | s to_char | 1700 25 | s to_char | 23 25 | s to_char | 20 25 | s to_char | 700 25 | s to_char | 701 25 | s to_char | 1186 25 | s to_char | 1114 25 | s hank=# select oid,typname from pg_type where oid in (20,25,23,700,701,1114,1184,1186,1700); oid | typname ------+------------- 20 | int8 23 | int4 25 | text 700 | float4 701 | float8 1114 | timestamp 1184 | timestamptz 1186 | interval 1700 | numeric (9 rows)

为什么函数表达式索引一定要用immutable状态的函数,因为执行函数得到的结果必须和建立函数索引时得到的结果是一致的, 如果结果不一致,那么走索引扫描和走全表扫描的结果就会不一致,结果就会出现不一致的问题。

可以自建函数解决以上的问题,不过这里需要时间字段类型不带time zone,而且LC_MESSAGES不变,因为LC_MESSAGES变化可能导致结果变化。

CREATE OR REPLACE FUNCTION immutable_to_char(createtime timestamp) RETURNS text AS $BODY$ select to_char($1, 'yyyy-mm-dd'); $BODY$ LANGUAGE sql IMMUTABLE; #再次创建索引成功 hank=# create index idx_tb1_time on tb1 (immutable_to_char(col_a)); CREATE INDEX

参考: https://github.com/digoal/blog/blob/master/201206/20120626_02.md https://www.postgresql.org/docs/current/xfunc-volatility.html

最新回复(0)