如何将时间戳字段转换为 int8?或者只是删除专栏并创建一个新的?

How to convert a timestamp field to int8? Or just drop the column and make a new one?

我有一个 PostgreSQL table,其中有一列类型为 timestamp。我不久前就收录了这个专栏,以防将来我想用它做点什么。我现在希望将其转换为 int8 并将其用作纪元时间列。 table 的所有行目前都将此列设置为 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.

注意:有问题的 table 中有超过一百万行。

首先,objective 是未定义的,没有弄清楚 int8 将代表什么。自纪元以来的秒数?毫秒?微秒? (在您的特定情况下,所有 NULL 值都无关紧要,但下一个 reader 可能会被误导。)

接下来,在 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() returns the UNIX epoch (which can be converted back to timestamptz with to_timestamp())。所以仅仅转换内部值是不行的。

对于您的特定情况(所有值 NULL),使用 text 作为垫脚石更简单。每种类型都可以从 text 转换为 text(只要值兼容)。

ALTER TABLE public.new_ambient_data
   ALTER COLUMN sensor_date TYPE int8 USING sensor_date::text::int8;

是的,就地转换列可能比删除并重新创建它更便宜。虽然该列全部为 NULL,但无论哪种方式,操作都非常便宜,因为没有实际的元组数据,NULL 位图中只有一点。这两种方式都不会触发 table 重写。

新添加的列始终位于列列表的末尾,而转换后的列保留在原位。就看你想要什么了。

最后,完全不要这样做。数据类型 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_partdate_trunc(以及从 v14 开始的 date_bin!)截断值并提取单个组件

在内部,时间戳无论如何都存储为数字,因此您不会损失性能。

具体的转换在欧文的回答中给出,这里不再赘述。