为什么MySQL的最长时限是838:59:59?

时间:2016-08-31 22:23:30

标签: mysql sql

我自己也遇到了极限,但是尽管网上有很多喋喋不休的话,我从未见过为何对TIME数据类型的上限和下限的解释。官方参考http://dev.mysql.com/doc/refman/5.7/en/time.html

  

TIME值的范围可以从'-838:59:59'到'838:59:59'。小时部分可能太大,因为TIME类型不仅可以用来表示一天中的时间(必须小于24小时),还可以用于表示两个事件之间的经过时间或时间间隔(可能远大于24小时,甚至是负面的。)

但我不知道为什么小时部分被允许“如此之大”,但为什么它被切断了它的位置。关于几天,或者如果我试图想象可以将多少秒存储为整数的可能截止值似乎没有任何意义。那么范围呢?

3 个答案:

答案 0 :(得分:7)

DATETIME基于10的基数存储,请参阅Date and Time Data Type Representation

  

DATETIME:八个字节:一个四字节整数,日期打包为YYYY×10000 + MM×100 + DD和一个四字节整数,时间打包为HH×10000 + MM×100 + SS

出于方便和其他原因,(旧)时间格式以相同的方式编码,使用3个字节:

Hours * 10000 + Minutes * 100 + Seconds

这意味着:

3 bytes = 2^24 = 16.777.216
with sign: 2^23 = 8.388.608

使用编码,这代表了神奇的838小时。最多分钟和秒的8608秒(没有溢出),这导致最大有效时间838:59:59。关于这一点的一个好处是,那个时间的整数表示8385959很容易被人类读取。但是这种编码当然会留下空白,无效(未使用)的整数值(如8309999)。

自MySQL 5.6.4起,time格式将其encoding更改为

  1 bit sign    (1= non-negative, 0= negative)
  1 bit unused  (reserved for future extensions)
 10 bits hour   (0-838)
  6 bits minute (0-59) 
  6 bits second (0-59) 
---------------------
  24 bits = 3 bytes

即使它现在可以存储更多小时,但为了兼容性,它仍然只允许838小时。

答案 1 :(得分:7)

TIME值始终存储在MySQL的3个字节中。但格式在version 5.6.4上发生了变化。我怀疑这不是第一次改变。但另一个变化,如果有的话,发生在很久以前,并没有公开的证据。 GitHub上的MySQL源代码历史从5.5版开始(最早的提交是从2008年5月开始),但我正在寻找的变化发生在2001 - 2002年左右(MySQL 4于2003年推出)

当前格式,如文档中所述,使用6位秒(可能的值:063),6位(分钟),10位(小时)(可能的值:{{ 1}}到0),1位用于符号(添加已提到的间隔的负值)和1位未使用并标记为"保留用于将来的扩展"。

它针对时间组件(小时,分钟,秒)进行了优化,并且不会浪费太多空间。使用此格式,可以在1023-1023:59:59之间存储值。但是,MySQL将小时数限制为+1023:59:59,可能是为了向后兼容之前写的应用程序,当时我认为这是限制。

在5.6.4版之前,838值也存储在3个字节上,组件打包为TIME。此格式已针对使用时间戳进行了优化(因为它实际上是一个时间戳)。使用此格式,可以存储大约days * 24 * 3600 + hours * 3600 + minutes * 60 + seconds-2330小时范围内的值。虽然有大量值可用,但MySQL仍然将值限制为+2330-838小时。

MySQL 4上有bug #11655。可以使用嵌套的+838语句返回TIME范围之外的-838..+838值。它不是一个功能,而是一个错误,它已被修复。

将值限制在此范围内并主动更改在其外部生成SELECT值的任何代码段的唯一原因是向后兼容性。

我怀疑MySQL 3使用了不同的格式,由于数据的打包方式,将有效值限制在TIME小时范围内。

通过调查当前MySQL's source code,我发现了这个有趣的公式:

-838..+838

暂时忽略上面使用的 #define TIME_MAX_VALUE (TIME_MAX_HOUR*10000 + TIME_MAX_MINUTE*100 + TIME_MAX_SECOND) 部分,让我们只记住MAX和{{1} }是TIME_MAX_MINUTETIME_MAX_SECOND之间的数字。公式只是将小时,分钟和秒连接成一个整数。例如,值00变为59

这个公式提出了以下问题:假设170:29:45值存储在带有符号的3个字节上,那么可以用这种方式表示的最大正值是多少?

我们要查找的值为1702945,十进制表示法为TIME。由于最后四位数(0x7FFFFF)应该被读取为分钟(8388607)和秒(8607),并且它们的最大有效值为86,因此最大值可以使用上面的公式将符号存储在带有符号的3个字节上07。其中,598385959。当当!

猜猜是什么?上面列出的TIME代码片段是从以下内容中提取的:

+838:59:59

我确信这是MySQL 3用于在内部保留C值的方式。这种格式强加了范围的限制,后续版本的向后兼容性要求将限制传播到我们的日子。

答案 2 :(得分:1)

显然,如果没有得到数据库设计者的直接反馈,很难回答这些类型的问题。

但是有一些关于如何在内部存储不同数据类型的文档,并且在某种程度上,它可以帮助我们理解这一点。

例如,对于TIME数据类型,请注意根据documentation内部存储的内容:

  非分数部分的

TIME编码:

  1 bit sign    (1= non-negative, 0= negative)
  1 bit unused  (reserved for future extensions)
 10 bits hour   (0-838)
  6 bits minute (0-59) 
  6 bits second (0-59) 
---------------------
  24 bits = 3 bytes

因此,正如您所看到的,目标是使信息适合3个字节。并且,在这3个字节中,为hours保留了10个比特,这几乎决定了整个范围。

也就是说,10位确实允许值高达1023,因此我猜,从技术上讲,如果不对存储大小进行任何更改,则范围可能是-1023:59:591023:59:59。为什么他们没有这样做,他们选择838作为截止点,我不知道。