我想将列从
timestamp
(无时区)迁移到 timestamptz
类型。
我使用的是 Postgres 9.3.9。
我需要知道此操作是否会导致表重写(锁定表),因为我的表很大并且数据库处于活动状态。
我在 9.2 发行说明中找到了这个:
增加 varchar 或 varbit 列的长度限制,或完全删除该限制,不再需要重写表。同样,增加数字列允许的精度,或将列从受约束的数字更改为不受约束的数字,不再需要重写表。在涉及间隔、时间戳和时间戳类型的类似情况下,也可以避免表重写。
这听起来很有希望,但实际上并没有详细说明“类似案例”可能是什么。
如果此操作将锁定表,我希望获得有关如何在实时数据库上解决此问题而不中断服务的建议。
首先,您似乎混淆了锁和表重写。发行说明中的注释谈到了表重写 - 它总是在表上使用
ACCESS EXCLUSIVE
锁。但这里还有许多其他操作也需要锁。
您需要:
ALTER TABLE tbl ALTER ts_col TYPE timestamptz;
除非您想在转换中设置特定时区,而不是会话的当前时区:
ALTER TABLE tbl ALTER ts_col TYPE timestamptz USING ts_col AT TIME ZONE 'Europe/London';
在这种情况下,请务必使用时区名称,不是简单的偏移量或缩写。详情:
更改现有表的定义。有 下面描述了几个子表单。请注意所需的锁定级别 每个子表单可能有所不同。ALTER TABLE
锁被持有,除非 明确指出。ACCESS EXCLUSIVE
ALTER column_name TYPE data_type
需要这样一个ACCESS EXCLUSIVE
锁。虽然 timestamp
和 timestamptz
的内部存储 format相同,但内部 value 通常会因转换而更改(取决于会话的时区设置!)。 Postgres 必须为表中的每一行编写新版本,因此这也需要表重写。由于该操作需要
ACCESS EXCLUSIVE
锁,因此无需保留旧的行版本,并且转换后您将看不到死元组。
fiddle - 演示时区设置对转换的作用。我还添加了一个将
varchar
转换为 text
的示例,这不需要重写表 - 除非您使用更短的长度修饰符。
旧 sqlfiddle - 请注意输出强制转换为 text
(
ts_col::text
),以防止 sqlfiddle 中的 JDBC 层添加更多(不需要的!)表示魔法。
事务开始后尝试访问表的并发事务将等待,直到该事务完成。 您
可以尝试通过在后台准备一个新表、删除旧表并重命名新表来缩短锁定时间,但这将使并发事务失败并出现如下错误:
错误:无法打开与 OID 123456 的关系详情:
timestamp
timestamptz
像 varchar
或
numeric
timestamp
、time
和 interval
类型允许使用修饰符。例如,时间戳默认最多存储 6 位小数秒,但您可以修改:
timestamp(0)
不存储秒小数。从 varchar(10)
->
varchar(20)
的转换不需要重写表,因为源类型中的值也保证适合(二进制兼容)目标类型。对于 timestamp (0)
->
timestamp
或 timestamptz(3)
-> timestamptz(5)
也是如此。这就是手册在发行说明中引用的段落中提到的内容: 在涉及以下内容的类似情况下,也可以避免表重写
interval
、
和timestamp
类型。timestamptz