如何在生成列的表达式中使用带时区列的时间戳?

问题描述 投票:0回答:1

我有一个名为

timestampz
created
列,我想使用 Postgres 12+ 生成的列来创建月、日和年的生成列。

由于

timestampz
中有时区,因此
date_part(create, ..)
EXTRACT (month from created)
等表达式会失败并出现错误:

ERROR: generation expression is not immutable

我尝试转换为时间戳并修复时区,但两者仍然被视为可变生成表达式:

  1. GENERATED ALWAYS AS (date_part('month', created::timestamp))
  2. GENERATED ALWAYS AS (date_part('month', created::timestamp AT TIME ZONE 'UTC'))

但是,正如Erwin Brandstetter在他对这个答案的评论中所指出的,这是有效的:

ALTER TABLE tbl
ADD COLUMN created_year numeric GENERATED ALWAYS AS 
    (date_part('month', created AT TIME ZONE 'UTC')) STORED;

当然

EXTRACT(month from created AT TIME ZONE 'UTC'))
也可以。

这让我得出这样的结论:如果我想要 N 个时区,我需要 N 个生成的列。
为什么这个有效,而不是上面的#2?

::timestamp
数据类型转换时与服务器的配置参数有关吗?

postgresql immutability calculated-columns generated-columns
1个回答
0
投票

为什么这个有效,而不是上面的#2?

#1 和 #2 都不起作用,因为从

timestamptz
timestamp
的转换不是 immutable。这取决于当前的
timezone
设置。出于显而易见的原因,生成的列仅接受不可变表达式。注意:
timezone
不是“服务器设置”,它是每个单独会话的设置。默认值通常设置在
postgresql.conf
中,但客户可以(并且经常会)根据需要进行设置。

另一方面,

date_part('month', created AT TIME ZONE 'UTC
是不可变的。导出 UTC 时间(或任何给定时区偏移量的本地时间)始终会产生相同的结果,并且时区是此表达式中的给定常量。

不幸的是,数据类型的名称

timestamp with time zone

 有点误导。根本不存储给定的时区。它只是作为输入修饰符来计算相应的 UTC 时间,该时间存储在内部。 (并且作为输出装饰器,调整为请求客户端的 
timezone
。)参见:

  • 数据类型“timestamp with time zone”中的时区存储
基础知识:

  • 在 Rails 和 PostgreSQL 中完全忽略时区
  • PostgreSQL 中的计算/计算/虚拟/派生/生成列
© www.soinside.com 2019 - 2024. All rights reserved.