如何将时间戳字段转换为 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_part
和 date_trunc
(以及从 v14 开始的 date_bin
!)截断值并提取单个组件
在内部,时间戳无论如何都存储为数字,因此您不会损失性能。
具体的转换在欧文的回答中给出,这里不再赘述。
我有一个 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_part
和date_trunc
(以及从 v14 开始的date_bin
!)截断值并提取单个组件
在内部,时间戳无论如何都存储为数字,因此您不会损失性能。
具体的转换在欧文的回答中给出,这里不再赘述。