时间戳索引:索引表达式中的函数必须标记为IMMUTABLE

时间:2015-10-24 06:57:11

标签: sql postgresql

我想在我的表上为 start_time 创建一个索引,我的json列中的timestamptz(带时区的时间戳)字段名为 match

关注this questionthis article我知道由于时区和本地化的不同,您无法在timestamptz字段上创建索引。这两个都表明你可以在时间戳上创建索引(转换为文本),所以我尝试了以下函数:

CREATE OR REPLACE FUNCTION to_text(timestamptz) 
 RETURNS text AS $$
  SELECT to_char($1 at time zone 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.US') 
  $$
LANGUAGE sql
IMMUTABLE;

我认为我对时区和本地化没有任何问题。

CREATE INDEX i_match_start_time ON matches (to_text(((match->>'start_time')::timestamptz)));

返回以下内容:

ERROR: functions in index expression must be marked IMMUTABLE

我还尝试过返回时间戳的函数:

SELECT ($1 at time zone 'UTC') 

返回unix时间的函数(尝试double并转换为十进制):

SELECT EXTRACT(EPOCH FROM $1)

其中每个都返回相同的错误。

我需要在 start_time 上编制索引,因为几乎所有对此表的选择查询都将按 start_time 排序。

感谢您的帮助。

1 个答案:

答案 0 :(得分:2)

首先我认为这可能是CREATE INDEX逻辑中的错误。但关键是从texttimestamptz本身的演员阵容也不是IMMUTABLE。它取决于datestyle等易变设置。

在您的特定情况下,有一种解决方法甚至比您尝试的更好。将演员阵容移动到函数中:

CREATE OR REPLACE FUNCTION to_text(text) 
  RETURNS text AS
$func$
SELECT to_char($1::timestamptz AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.US') 
$func$ LANGUAGE sql IMMUTABLE;

同样有效,但现在CREATE INDEX不会禁止:

CREATE INDEX bar ON foo(to_text(j->>'start_time'));

显然,您必须相应地调整函数调用:从表达式中删除强制转换::timestamptz。确保您使用相同的设置无处不在,否则索引可能会导致错误的结果。

更好

使用to_timestamp()而不是强制转换的实际不可变表达式(如果您的输入模式允许):

CREATE OR REPLACE FUNCTION to_text(text) 
  RETURNS text AS
$func$
SELECT to_char(to_timestamp($1, 'YYYY-MM-DD"T"HH24:MI:SS.US')  -- adapt to your pattern
            AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS.US') 
$func$ LANGUAGE sql IMMUTABLE;

但请注意(引用我的测试中的错误消息):

  to_date

不支持

“TZ”/“tz”/“OF”格式模式