错误:索引表达式中的函数必须在Postgres中标记为IMMUTABLE

时间:2011-05-12 03:35:55

标签: sql postgresql

我想创建一个Multicolumn表达式索引,但是当我创建索引时,会输出以下消息:

--detail message 
wapgrowth=> create index CONCURRENTLY idx_test on  tmp_table using btree (skyid, to_char(create_time, 'YYYY-MM-DD'), actiontype );
ERROR:  functions in index expression must be marked IMMUTABLE


--table ddl
wapgrowth=> \d tmp_table
               Table "wapgrowth.tmp_table"
   Column    |            Type             |   Modifiers   
-------------+-----------------------------+---------------
 id          | integer                     | not null
 actiontype  | character varying(20)       | 
 apptype     | character varying(20)       | 
 score       | integer                     | 
 create_time | timestamp without time zone | default now()
 skyid       | integer                     | 
Indexes:

5 个答案:

答案 0 :(得分:19)

根据黑客邮件列表中的这个帖子:

http://www.mail-archive.com/pgsql-hackers@postgresql.org/msg86725.html

这是预期的行为,因为to_char取决于LC_MESSAGES设置

在你的情况下,这显然没有意义,因为你使用的格式永远不会依赖于语言环境,所以如果你确实需要在索引中使用文本表示,你可以创建自己的to_char()函数和将其标记为不可变:

CREATE OR REPLACE FUNCTION my_to_char(some_time timestamp) 
  RETURNS text
AS
$BODY$
    select to_char($1, 'yyyy-mm-dd');
$BODY$
LANGUAGE sql
IMMUTABLE;

如果必须将其用作索引中的文本(并且不能使用强制转换为Sam建议的日期),则需要创建自己的格式化函数,并将其标记为不可变。然后可以在索引中使用它。

但要使Postgres 使用索引,您还需要在SQL语句中调用my_to_char()。使用内置to_char()

时,它无法识别它

但我确实认为Sam在索引中使用直接日期的建议可能更好

答案 1 :(得分:2)

时间戳 - 无时区的

to_char不是不可变函数,因为转换取决于您的本地时区设置。这意味着索引不能移植到不同时区的另一台计算机,Postgres将不允许它。我认为如果你将create_time声明为带时区的时间,问题就会消失。

答案 2 :(得分:2)

不要使用to_char将时间戳格式化为YYYY-MM-DD,而是尝试将时间戳转换为日期类型,这将产生相同的效果:

create index CONCURRENTLY idx_test on  tmp_table using btree (skyid, cast(create_time as date), actiontype );

答案 3 :(得分:1)

在一天结束时,您似乎正在尝试索引create_time的“YYYY-MM-DD”表示。为什么不只是INDEX create_time?问题是to_char()是MUTABLE,因为locale环境变量可能会改变,这会改变to_char()的输出。

http://www.postgresql.org/docs/9.0/static/charset.html

如果您可以控制架构,则可以添加一个新列(例如create_date TEXT)和INDEX,然后设置一个处理插入的触发器。实际上,如果您创建了一种将TIMESTAMP WITHOUT TIME ZONE转换为TEXT的方法,那么您可以以恒定的方式进行INDEX。 a_horse_with_no_name的建议很好,因为我认为你不关心语言环境。

您正在运行的问题是所有DATE和TIME处理代码都遵循locale,这不是IMMUTABLE,因此您不能轻易地依赖这些数据类型的INDEX函数。

答案 4 :(得分:1)

这将详细解释:

https://www.postgresql.org/message-id/CAKFQuwbcMfesmNkm19mXFLXP14sP5BiPsR1GSkY1suKiM1rukg%40mail.gmail.com

基本上,时区取决于服务器,因此如果有人进行更改,结果可能会更改。但是,如果您锁定时区:

date(timezone('UTC', create_time)

它将起作用。