我想创建一个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:
答案 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)
这将详细解释:
基本上,时区取决于服务器,因此如果有人进行更改,结果可能会更改。但是,如果您锁定时区:
date(timezone('UTC', create_time)
它将起作用。