如何解决如何将时间戳字段转换为 int8?或者只是删除列并创建一个新列?
我有一个包含 timestamp
类型列的 Postgresql 表。不久前我已经包含了这个专栏,以防我将来想用它来做某事。我现在希望将其转换为 int8
并将其用作纪元时间列。目前表的所有行都将此列设置为 null
。当我尝试使用以下方法更改行时:
ALTER TABLE public.new_ambient_data
ALTER COLUMN sensor_date TYPE int8 USING sensor_date::int8;
我收到错误:
ERROR: cannot cast type timestamp without time zone to bigint
是删除列并创建一个我想要的新数据类型更好,还是这里有一个更好的 sql 脚本来将空的 timestamp
列转换为 int8
。
注意:有问题的表中有超过一百万行。
解决方法
首先,目标是未定义的,没有明确 int8
将代表什么。自纪元以来的几秒钟?毫秒?微秒? (在您的特定情况下,所有 NULL 值都无关紧要,但下一位读者可能会被误导。)
接下来,在 Postgres 中没有为 timestamp
--> bigint
定义演员表(基本上出于同样的原因)。 USING
子句需要一个有效的表达式。
假设您想要微秒,因为这会保留 Postgres 时间戳的原始微秒分辨率,这样就可以了:
ALTER TABLE public.new_ambient_data
ALTER COLUMN sensor_date TYPE int8 USING (extract(epoch FROM sensor_date)*1000000)::int8;
值得注意的是,时间戳的 Postgres 纪元从 2000-01-01 00:00:00 UTC 开始,与 UNIX 纪元从 1970-01-01 00:00:00 UTC 开始不同。但是 extract()
返回 UNIX 纪元(可以使用 to_timestamp()
将其转换回 timestamptz
)。所以仅仅转换内部值是不行的。
对于您的特定情况(所有值均为 NULL),使用 text
作为垫脚石更简单。每种类型都可以在 text
之间进行转换(只要值兼容)。
ALTER TABLE public.new_ambient_data
ALTER COLUMN sensor_date TYPE int8 USING sensor_date::text::int8;
是的,就地转换列可能比删除并重新创建列更便宜。虽然列都是 NULL,但无论哪种方式,操作都非常便宜,因为没有实际的元组数据,只有 NULL 位图中的一点。两种方式都不会触发表重写。
新添加的列始终位于列列表的末尾,而转换后的列则保留在原位。取决于你想要什么。
最后,不要这样做。数据类型timestamp
(或timestamptz
)通常在多种方面优于将时间信息存储为通用bigint
。在 Laurenz 的回答中查看详细信息!
见:
- Ignoring time zones altogether in Rails and PostgreSQL
- How to get the date and time from timestamp in PostgreSQL select query?
- How to round off milliseconds value from timestamp(0) in PostgreSQL?
首先,我想劝阻你不要这样做。使用 timestamp
列比使用数字列要好得多:
-
人类更容易理解这些值
-
你可以使用日期算法,所以你不会丢失任何东西:
timestamp - timestamp → interval
timestamp + interval → timestamp
interval / double precision → interval
这使您的查询更具可读性
-
如果您使用
timestamp with time zone
,您可以让 PostgreSQL 为您处理时区转换 -
有用的函数,如
date_part
和date_trunc
(以及从 v14 开始的date_bin
!)截断值并提取单个组件
在内部,时间戳无论如何都存储为数字,因此您不会损失性能。
实际转换在 Erwin 的回答中给出,所以我不会在这里重复。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。