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

9ceoxa92  于 4个月前  发布在  其他
关注(0)|答案(1)|浏览(66)

我想在我的表上为 start_time 创建一个索引,这是我的json列中名为 match 的timestamptz(带时区的时间戳)字段。
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)));


这将返回以下内容:
错误:索引表达式中的函数必须标记为IMMUTABLE
我也尝试了返回时间戳的函数:

SELECT ($1 at time zone 'UTC')


以及返回unix时间的函数(尝试double并转换为decimal):

SELECT EXTRACT(EPOCH FROM $1)


每个都返回相同的错误。
我需要在 start_time 上建立索引,因为实际上对这个表的所有选择查询都将按 start_time 排序。

utugiqy6

utugiqy61#

JSON嵌套timestamptz

一个timestamptz(json列中的timestamp with time zone)字段
JSON中没有 timestamptz字段” 这回事。JSON只理解:objectarraystringnumberbooleannull
我假设这是正确的处理与ISO 8601 timestamptz * 字符串 * 在外地排他性。否则,有更多的问题要解决。
相关博客:

IMMUTABLE

texttimestamptz的转换本身并不是IMMUTABLE,它取决于像datestyle这样的volatile设置。
在您的特定情况下,我们可以将强制转换移动到函数中作为解决方案:

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

字符串
由于只有ISO 8601日期,并且没有依赖于本地化设置的模板模式,这实际上是不可变的。

更好的索引

但是,现在没有必要强制转换为text并返回。直接索引返回的timestamptz值!(您最初假设这是不可能的,这不成立!)

CREATE OR REPLACE FUNCTION to_tstz_immutable(text) 
  RETURNS timestamptz
  LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE
RETURN $1::timestamptz;

-- then simply:
CREATE INDEX matches_start_time_idx ON matches (to_tstz_immutable(match->>'start_time'));


使用一个“SQL标准”函数(随Postgres 14添加)。参见:

在Postgres 9.6或更高版本中添加PARALLEL SAFE。请参阅:

  • PARALLEL标签用于带有SELECT和SELECT的函数

更好

实际上,对该表的所有选择查询都将按 start_time 排序。
根据上面的函数添加一个生成的列(Postgres 12添加的),并在其上添加一个普通索引:

ALTER TABLE matches
ADD COLUMN start_time timestamptz GENERATED ALWAYS AS (to_tstz_immutable(match->>'start_time')) STORED

CREATE INDEX matches_start_time_idx ON matches (start_time);


甚至更简单的日常处理,和更有效.增加8字节支付时使用了很多.见:

  • PostgreSQL中的计算/计算/虚拟/派生/生成列
    或者将时间戳从JSON文档中移到它自己的列中开始开始,这样最好。

相关问题