我有一个名为
timestampz
的 created
列,我想使用 Postgres 12+ 生成的列来创建月、日和年的生成列。
由于
timestampz
中有时区,因此 date_part(create, ..)
和 EXTRACT (month from created)
等表达式会失败并出现错误:
ERROR: generation expression is not immutable
我尝试转换为时间戳并修复时区,但两者仍然被视为可变生成表达式:
GENERATED ALWAYS AS (date_part('month', created::timestamp))
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
数据类型转换时与服务器的配置参数有关吗?
为什么这个有效,而不是上面的#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
。)参见:基础知识: