mysql虚拟列不一致wrt from_unixtime()

t2a7ltrp  于 5个月前  发布在  Mysql
关注(0)|答案(2)|浏览(48)

使用from_unixtime()得到一个带有虚拟列的表:

CREATE TABLE aa (
 id int NOT NULL AUTO_INCREMENT,
 epochmillis bigint,
 session_dt DATETIME(3) generated always as (from_unixtime(epochmillis/1000)),
 -- utc DATETIME(3) generated always as (convert_tz(from_unixtime(epochmillis/1000), @@time_zone, 'UTC')),
PRIMARY KEY (`id`));

字符串
我不能定义一个使用变量的表达式(注解掉了),但是from_unixtime()在内部使用了@@time_zone!这不公平!哈哈。基本上,这个表根据会话时区返回session_dt,这或多或少是错误的,这取决于你对要求会话时区的看法,但是和我试图使用的变量一样“违规”。
好吧,也许你已经猜到我在做什么了:试图从一个epochtimemillis列中获得一个稳定的UTC日期时间(3),该列远远超过2038(我认为最多到3001)(对于少数日期操作,在日期时间上可能会更容易)。
我只是想两全其美(微不足道的纪元时间,方便的无区域日期时间)。特别是,我想避免典型的将日期时间保存在作者的时区中,并在不同的读者的时区中恢复它。通过强制保存为纪元时间,并使用生成的日期时间列提供给予UTC,将没有办法避免存储正确的时刻。
需要说明的是:我希望我能阻止from_unixtime()选择会话的时区并在语句上指定时区。所有对convert_tz的尝试都是不可取的,只是试图撤消隐藏的选择。
我能做些什么来实现我的目标?
参考(从接受的答案中建议修复):https://sqlize.online/sql/mariadb/b22c2b06b9a00b9635fe65d782c22fc0/

gmxoilav

gmxoilav1#

您可以通过添加将epoch毫秒转换为UTC时间:

session_dt DATETIME(3) generated always as ('1970-01-01' + interval epochmillis*1000 microsecond),

字符串
也就是说,你真的应该首先存储session_dt,而不是使用生成的列。没有理由将日期存储为整数。如果你需要epoch毫秒作为查询结果,你可以从session_dt获取它:

floor(timestampdiff(microsecond,'1970-01-01',session_dt)/1000)


请注意,utc_timestamp(3)可以用来用当前的utc时间填充session_dt。你应该避免使用current_timestamp、from_unixtime、unix_timestamp等受会话时区影响的函数。你可能需要弄清楚如何让你的客户端期望日期时间是utc,但无论如何这都是值得的。

bakd9h0s

bakd9h0s2#

转换时区是一个非常棘手的问题。一个问题是,如果你存储的是未来的日期,因为不可能知道未来的政策是否会改变时区,但你只能根据当前的政策转换时区。请参阅https://codeopinion.com/just-store-utc-not-so-fast-handling-time-zones-is-complicated/了解更多信息。
唯一能在数据库中明确这一点的方法是将本地时间和UTC时间都存储为真实的列(即不将UTC时间存储为生成的列),同时存储本地时间的时区以及上次将本地时间转换为UTC时间时时区数据库的版本。

CREATE TABLE aa (
 id int NOT NULL AUTO_INCREMENT,
 epochmillis bigint,
 session_dt_local DATETIME(3),
 session_dt_utc DATETIME(3),
 local_tz_name VARCHAR(64),    -- e.g. "America/Toronto"
 tzdb_version VARCHAR(16),     -- e.g. "2022a"
 PRIMARY KEY (`id`)
);

字符串
这样,如果时区数据库在将来某个时候更改了该地区的时区数据库,那么当时维护该数据库的人就有足够的信息根据新的时区定义重新计算UTC时间。
您可以将session_dt_local作为生成列填充,因为它可以在不引用timezone变量的情况下完成。
但是你不能把UTC转换成一个生成的列。你必须使用一个触发器。因为你无论如何都要写一个触发器,你也可以在一个地方同时计算本地和UTC日期时间。但是这取决于你。

相关问题