我想在我的表上为 start_time 创建一个索引,我的json列中的timestamptz(带时区的时间戳)字段名为 match 。
关注this question和this 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 排序。
感谢您的帮助。
答案 0 :(得分:2)
首先我认为这可能是CREATE INDEX
逻辑中的错误。但关键是从text
到timestamptz
本身的演员阵容也不是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”格式模式