ОШИБКА: функции в выражении индекса должны быть отмечены IMMUTABLE в Postgres
Я хочу создать индекс выражения 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:
Ответы
Ответ 1
Согласно этой теме в списке рассылки хакеров:
http://www.mail-archive.com/[email protected]/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;
Если вы должны использовать его как текст в индексе (и не можете использовать приведение к дате, как предложил Сэм), вам нужно будет создать свою собственную функцию форматирования, которую вы можете отметить как неизменяемую. Это можно затем использовать в индексе.
Но для того, чтобы Postgres использовали индекс, вам нужно будет вызвать my_to_char()
в ваших операторах SQL. Он не узнает его при использовании встроенного to_char()
Но я думаю, что предложение Сэма, использующее прямую дату в индексе, вероятно, лучше
Ответ 2
Вместо того, чтобы использовать to_char для форматирования вашей метки времени в виде ГГГГ-ММ-ДД, попробуйте применить свою временную метку к типу даты, который будет иметь тот же эффект:
create index CONCURRENTLY idx_test on tmp_table using btree (skyid, cast(create_time as date), actiontype );
Ответ 3
to_char
временной метки без времени не является неизменной функцией, потому что преобразование зависит от локальной настройки часового пояса. Это означает, что индекс не будет переноситься на другой компьютер в другом часовом поясе, и Postgres не позволит этого. Я думаю, проблема исчезнет, если вы объявите create_time как время с часовым поясом.
Ответ 4
В конце дня похоже, что вы пытаетесь индексировать представление "YYYY-MM-DD" для create_time. Почему не просто INDEX create_time? Проблема заключается в том, что to_char() является MUTABLE, потому что переменная среды локали может меняться, что изменяет вывод to_char().
http://www.postgresql.org/docs/9.0/static/charset.html
Если у вас есть контроль над схемой, вы можете добавить новый столбец (например, create_date TEXT) и INDEX, а затем настроить триггер, который обрабатывает вставки. Фактически, если вы создали способ конвертировать ваш TIMESTAMP WITHOUT TIME ZONE в TEXT, вы можете индексировать его постоянным образом. Предложение a_horse_with_no_name было хорошим, так как я не думаю, что вы заботитесь о локали.
Проблема, с которой вы работаете, - это все коды обработки DATE и TIME, которые соответствуют языку, который не является IMMUTABLE, поэтому вы не можете легко использовать функции INDEX, которые полагаются на эти типы данных.