3

我正在计划一个分布式应用程序系统,它将与不同类型的 RDBMS 进行通信。要求之一是对所有 RDBMS 类型的 DateTimes 进行一致处理。所有 DateTime 值必须以毫秒为精度,包括 TimeZone 信息并存储在单个列中。

由于不同的 RDBMS 处理日期和时间的方式不同,我担心在这种情况下我不能依赖它们的原生列类型,所以我必须想出一个不同的解决方案。(如果我在这里错了,欢迎你给我指路。)

无论是什么解决方案,理想情况下都应该允许在 SQL 级别上轻松排序和比较。其他方面,例如可读性和使用 SQL 日期时间函数的能力,并不重要,因为这些都将由网关服务处理。

我正在考虑将我的 DateTime 值存储在无符号 largeint 列类型(8 个字节)中。我还不确定是否所有有问题的 RDBMS(MSSQL、Oracle、DB2、PostgreSQL、MySQL,也许还有其他一些)实际上/拥有/这样的类型,但在这一点上,我只是假设他们有。

至于存储格式...例如,2009-01-01T12:00:00.999+01:00 可以存储类似于?20090101120000999??,在 8 个字节以下。

我能够以这种方式存储的最小日期时间为 0001-01-01T00:00:00.000+xx:xx,最大值为 8000-12-31T23:59:59.999+xx:xx,这给了我一个跨度绰绰有余。

由于最大无符号 largeint 值是 18446744073709551615,这给我留下了以下 3 位数字(由 A 和 BB 标记)来存储时区信息:AxxxxxxxxxxxxxxxxxBB。

考虑到 0001..8000 的最大年份跨度,A 可以是 0 或 1,BB 可以是 00 到 99 之间的任何值。

现在的问题是:

  • 您如何看待我提出的解决方案?它有优点还是只是愚蠢?

  • 如果没有更好的方法,您如何建议将剩余的三个数字最好地用于时区信息?

4

2 回答 2

2

要求之一是对所有 RDBMS 类型的 DateTimes 进行一致处理。

请注意,日期时间处理能力不同的数据库系统中存在很大差异。这范围从几乎不支持(SQLite)到优秀(Postgres)。一些如 Oracle 的遗留数据类型可能会混淆情况,因此请仔细研究而不做任何假设。

与其建立一个笼统地说我们必须支持“任何或所有数据库”的要求,不如说得更具体一些。准确研究哪些数据库可能真正适合在现实世界中部署。“任何或所有数据库”的要求是幼稚且不切实际的,因为数据库的许多功能各不相同——日期时间处理只是多数据库支持问题的开始。

SQL 标准几乎没有涉及日期时间的主题,广泛地定义了几种类型,几乎没有讨论日期时间工作的细微差别和复杂性。

另请注意,大多数编程平台对日期时间处理的支持极差。请注意,Java 凭借其出色设计的java.time类在该领域处于行业领先地位。该框架从 Java 的Joda-Time项目演变而来,该项目作为NodaTime移植到 .Net 平台。

所有 DateTime 值必须以毫秒为精度,

很好,你已经指定了那个重要的细节。了解各种系统将日期时间值解析为整秒、毫秒、微秒、纳秒或其他值。

包括时区信息并存储在单个列中。

精确定义时区。

了解从 UTC 偏移和时区之间的区别:第一个是小时-分钟-秒加或减数,第二个格式名称,Continent/Region是过去、现在和未来的历史更改特定区域的人们使用的偏移量。

CST、PST、IST 等 2-4 个字母的缩写不是正式的时区名称,没有标准化,甚至不是唯一的(避免使用它们)。

由于不同的 RDBMS 处理日期和时间的方式不同,我担心在这种情况下我不能依赖它们的原生列类型,所以我必须想出一个不同的解决方案。

SQL 标准确实定义了一些主要数据库支持的类型。

  • TIMESTAMP WITH TIME ZONE代表一个时刻,时间线上的一个特定点。我隐约记得听说过一个实际存储传入时区的数据库。但是大多数,例如 Postgres,使用传入值上指示的时区来调整为 UTC,然后存储该 UTC 值,最后丢弃时区信息。检索后,您将返回一个 UTC 值。当心工具和中间件具有令人困惑的反特性,即在检索后和显示给用户之前应用默认时区。
  • TIMESTAMP WITHOUT TIME ZONE表示带有时间的日期,但故意缺少时区或偏移量的上下文。没有区域/偏移量,这样的值不代表片刻。您可以应用时区来确定大约 26-27 小时范围内的时刻,即全球时区范围。

标准中还有其他类型,例如仅日期 ( DATE) 和仅时间 ( TIME)。

请参阅我为 Java 制作的此表,但在此上下文中,SQL 标准类型的列是相关的。请注意,这TIME WITH TIME ZONE在逻辑上没有意义,不应使用。

Java(传统和现代)和标准 SQL 中的日期时间类型表

如果您缩小了候选数据库列表,请研究他们的文档以了解它们是否具有类似于您感兴趣的标准类型的类型,以及该类型的名称是什么(并不总是标准名称)。

我正在考虑将我的 DateTime 值存储在无符号 largeint 列类型(8 个字节)中。

64 位值可能不太合适。例如,java.time类使用一对数字,自 UTC 1970 第一时刻的纪元参考以来的整秒数,再加上小数秒中纳秒计数的另一个数字。

如果它们在您的候选数据库列表中相似,那么最好使用数据库的数据时间数据类型。使用从纪元开始计数本质上是模棱两可的,这使得识别错误数据变得困难。

存储您自己的从纪元开始计数是可能的。如果您必须这样做,请确保整个团队都了解选择了哪个时代参考。至少有几十个已在各种计算系统中使用。假设正在使用特定的纪元参考,请注意工作人员。

定义您自己的日期时间跟踪的另一种方法是使用标准ISO 8601格式的文本。此类字符串将按字母顺序按时间顺序排序。该排序的一个例外是可选的,但通常Z在末尾用于指示与 UTC 的偏移量为零(发音为“Zulu”)。

我能够以这种方式存储的最小日期时间为 0001-01-01T00:00:00.000+xx:xx,

考虑到 0001..8000 的最大年份跨度

你真的保存了基督时代的价值观吗?这个软件真的会在 8000 年左右执行交易吗?

这是负责任的利益相关者应该定义他们真正需求的领域。例如,对于许多业务系统,您可能只需要产品发布那年的数据,并且只需要一两个世纪的未来。

不同数据库之间的最小值/最大值范围差异很大。如果您选择在每个数据库系统中使用内置数据类型,请调查其限制。例如,有些可能只到 2038 年,即常见的Y2038 问题

总结一下我的建议:

  • 了解您的日期时间需求:最小/最大范围、分辨率和各种类型(时刻与非时刻、仅日期等)。
  • 了解您可能用于部署的数据库。
    • 如果您需要经典RDMS中的企业级可靠性,您的候选列表可能只有几个:PostgresMicrosoft SQL ServerOracle,也许还有IBM Db2
    • 使支持的数据库列表尽可能短。您同意支持的每个数据库都是一个巨大的承诺,无论是现在还是将来。
    • 确保您选择的数据库具有可用于您选择的编程语言的数据库驱动程序。例如用于Java的JDBC
  • 如果可能,请使用数据库提供的内置数据类型。
  • 确保您和您的团队了解日期时间处理。根据我的经验,许多人不这样做,因为 (a) 该主题很少被教授,并且 (b) 许多程序员和管理员错误地认为他们对日期时间的日常直观理解足以完成编程工作。(正如他们所说,无知是幸福的。)
  • 确定除日期时间处理之外的其他功能领域,并比较哪些数据库支持这些领域。
于 2020-06-04T00:00:12.573 回答
1

我建议您以自 1970 年以来的毫秒数(Java 风格)存储日期时间信息。这是存储日期时间信息的标准方式,此外它在空间方面比您的建议更有效。因为在您的建议中,有些数字是“浪费的”,即月份数字只能存储 00-12(而不是 00-99)等等。您没有指定您的开发语言是什么,但我相信您可以找到许多将日期转换为毫秒的代码片段。如果您在 .NET 中进行开发,他们有类似的刻度概念。(您也可以使用此信息)

关于时区,我将添加另一列来仅存储 TimeZone 指示。

请记住,您选择的任何格式都应保持两个日期之间的一致性,即如果 D1 > D2 则 format(D1)>format(D2) ,这样您就可以查询数据库自某个日期以来的更改,或查询两个日期之间的更改

于 2009-01-09T11:51:04.177 回答