错误:索引表达式中的函数必须在 Postgres 中标记为 IMMUTABLE
我想创建多列表达式索引,但是当我创建索引时,输出以下消息:
--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:
I want to create a Multicolumn expression index, but when I create the index, the following message is output:
--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:
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
根据黑客邮件列表中的此线程:
http://www .mail-archive.com/[email protected]/msg86725.html
这是预期的行为,因为
to_char
取决于 LC_MESSAGES设置这种情况显然没有意义,因为您使用的格式永远不会依赖于区域设置,因此如果您确实需要在索引中使用文本表示形式,您可以创建自己的 to_char() 函数并将其标记为不可变:
如果您必须将其用作索引中的文本(并且不能像 Sam 建议的那样使用日期转换),您将需要创建自己的格式化函数,您可以将其标记为不可变。然后可以在索引中使用它。
但要使 Postgres 使用索引,您还需要在 SQL 语句中调用
my_to_char()
。当您使用内置的to_char()
时,它不会识别它,但我确实认为 Sam 的建议在索引中使用直接日期可能更好
According to this thread in the hackers mailing list:
http://www.mail-archive.com/[email protected]/msg86725.html
this is intended behaviour as
to_char
depends on the LC_MESSAGES settingIn your case this apparently doesn't make sense as the format you are using will never depend on the locale, so if you do need to use the text representation in the index, you can create your own to_char() function and mark it as immutable:
If you have to use it as a text in the index (and cannot use the cast to a date as Sam suggested) you will need to create your own formatting function that you can mark as immutable. That can then be used in the index.
But to make Postgres use the index you will need to call
my_to_char()
in your SQL statements as well. It will not recognize it when you use the built-into_char()
But I do think Sam's suggestion using a straight date in the index is probably better
这更详细地解释了:
https://www.postgresql.org/message-id/CAKFQuwbcMfesmNkm19mXFLXP14sP5BiPsR1GSkY1suKiM1rukg%40mail.gmail.com
基本上时区取决于服务器,因此如果有人更改它,结果可能会改变。但如果你锁定时区:
date(timezone('UTC', create_time))
它将起作用。
This explains more in detail:
https://www.postgresql.org/message-id/CAKFQuwbcMfesmNkm19mXFLXP14sP5BiPsR1GSkY1suKiM1rukg%40mail.gmail.com
basically the timezone depends on the server, and thus the result might change if somebody changes it. But if you lock the timezone:
date(timezone('UTC', create_time))
it will work.
timestamp-without-timezone 的
to_char
不是一个不可变的函数,因为转换取决于您本地的时区设置。这意味着索引无法移植到不同时区的另一台计算机,Postgres 也不允许这样做。我认为如果您将 create_time 声明为带时区的时间,问题就会消失。to_char
of a timestamp-without-timezone is not an immutable function, because the conversion depends on your local time-zone setting. That means the index wouldn't be portable to another computer in a different time zone, and Postgres won't allow it. I think the problem will go away if you declare create_time as a time with timezone.不要使用 to_char 将时间戳格式化为 YYYY-MM-DD,而是尝试将时间戳转换为日期类型,这将具有相同的效果:
Instead of using to_char to format your timestamp as YYYY-MM-DD, try casting your timestamp to a date type, which will have the same effect:
归根结底,您似乎正在尝试对 create_time 的“YYYY-MM-DD”表示形式建立索引。为什么不只使用 INDEX create_time 呢?问题是 to_char() 是可变的,因为区域设置环境变量可能会更改,这会更改 to_char() 的输出。
http://www.postgresql.org/docs/current/static/charset。 html
如果您可以控制模式,您可以添加一个新列(例如 create_date TEXT)并对其进行索引,然后设置一个处理插入的触发器。事实上,如果您创建了一种将 TIMESTAMP without TIME ZONE 转换为 TEXT 的方法,您可以以恒定的方式对其进行索引。 a_horse_with_no_name 的建议是一个很好的建议,因为我认为你不关心语言环境。
您遇到的问题是所有 DATE 和 TIME 处理代码都遵循区域设置,这不是 IMMUTABLE,因此您无法轻松地使用依赖于这些数据类型的 INDEX 函数。
At the end of the day it looks like you're trying to index a "YYYY-MM-DD" representation of create_time. Why not just INDEX create_time? The problem is to_char() is MUTABLE because the locale environment variable could change, which changes the output of to_char().
http://www.postgresql.org/docs/current/static/charset.html
If you have control over the schema, you could add a new column (e.g. create_date TEXT) and INDEX that, then setup a trigger that handles the inserts. In fact, if you created a way of converting your TIMESTAMP WITHOUT TIME ZONE to TEXT, you could INDEX that in a constant way. a_horse_with_no_name's suggestion was a good one since I don't think you care about locale.
The problem you're running in to is all DATE and TIME handling code obeys locale, which isn't IMMUTABLE, therefore you can't easily INDEX functions that rely on those data types.
对于 timestamptz 字段,它看起来像这样:
for the timestamptz field, it looks like this: