Pad*_*han 5 postgresql alter-table timezone
似乎将列从 更改为timestamp without time zone
会timestamp with time zone
根据语句时的当前会话时区转换现有数据alter
。
请参阅此示例,每个语句后显示输出
\ncreate table tztest (col1 timestamp without time zone);\n\nset timezone = 'UTC';\ninsert into tztest (col1) values ('2023-02-01 10:10:10');\nselect col1 as t1, extract(epoch FROM col1) from tztest;\n-- \xe2\x86\x92 2023-02-01 10:10:10 1675246210\n\nset timezone = 'America/New_York';\nalter table tztest alter column col1 type timestamp with time zone;\nselect col1 as t2, extract(epoch FROM col1) from tztest;\n-- \xe2\x86\x92 2023-02-01 10:10:10-05 1675264210\n\nset timezone = 'UTC';\nselect col1 as t3, extract(epoch FROM col1) from tztest;\n-- \xe2\x86\x92 2023-02-01 15:10:10+00 1675264210\n
Run Code Online (Sandbox Code Playgroud)\n命令后纪元值发生变化alter
。
\n我期望 PG 会假设该timestamp
值是 UTC,并且在将类型更改为timestamp with time zone
(给我t2
of05:10:10
和t3
of 10:10:10
)时不调整它。
然而,PG 似乎假设 时的会话时区alter
,并将其转换为 UTC。
我的理解是否正确,这是预期的行为吗?
\n在一个大表上,更改将更新每一行,这是我们关心并且当然想理解的事情。
\n是的,你的理解是正确的。如果您没有明确设置,请勿依赖后备(当前会话的时区设置)。
如果您的时间戳应被解释为 UTC 时间戳,请明确说明。最好timezone
针对这种特殊情况设置会话并使用ALTER TABLE
简单的SET DATA TYPE
. 只有这种情况才可以走捷径,无需重写表(见下文):
SET timezone = 'UTC';
ALTER TABLE tztest ALTER COLUMN col1 TYPE timestamptz;
Run Code Online (Sandbox Code Playgroud)
您还可以AT TIME ZONE
在USING
条款中申请SET DATA TYPE
:
ALTER TABLE tztest ALTER COLUMN col1 TYPE timestamptz
USING col1 AT TIME ZONE 'UTC';
Run Code Online (Sandbox Code Playgroud)
无论哪种方式,纪元值都保持不变,因为 Postgrestimestamptz
在内部存储为 UTC 时间戳。Postgres 必须重建任何涉及的索引col1
。
看:
在一个大表上,更改将更新每一行......
除了指出的异常之外,ALTER TABLE
重写整个表和所有索引(在持续时间内采取独占锁!)。将类型更改为二进制强制类型(如text
--> varchar
)可以避免这种情况。手册:
作为例外,当更改现有列的类型时,如果子句
USING
不更改列内容,并且旧类型可以二进制强制转换为新类型,或者是新类型上的不受约束的域,则不需要表重写。
对于表达式 with ,Postgres 没有意识到这一点AT TIME ZONE
。
但是SET DATA TYPE
,就像 Jeff 评论的那样,将会话时区设置为 UTC 的简单方法就符合条件。Postgres 12的发行说明:
当会话时区为 UTC 时,允许
ALTER TABLE ... SET DATA TYPE
在 之间进行更改timestamp
并 避免表重写 (Noah Misch)timestamptz
在 UTC 时区,这两种数据类型是二进制兼容的。
旧版本没有那么智能。比较:
小提琴-- Postgres 11
小提琴-- Postgres 12
另请注意,重写不是UPDATE
,它会产生死元组并使表膨胀,但不会以其较弱的锁干扰并发读取。这会在原始状态下完全重写整个表,但在持续时间内采用排他锁。
有关的: