【问题标题】:MySQL and datetimeMySQL 和日期时间
【发布时间】:2015-05-29 00:09:31
【问题描述】:

如果我有一个包含 DATETIME 列的表,我可以插入格式如下的日期:

2015-03-25 10:10:10
2015-03-25 10:10
2015-03-25 10
2015-03-25 

它将用零填充余数。但是我不能使用

2015-03 
2015

因为它会给出“不正确的日期时间值”错误。但是,可以在[..] WHERE timestamp < '2015-03' 之类的 SELECT 中使用最后两个 ..

如果在日期时间中省略,MySQL 是否可以用 01-01 填充剩余的日期时间,或者我必须自己手动完成?

即我想在 INSERT 语句中使用 '2015-03',或者做类似SELECT DATE_FORMAT('2015-03', '%Y%m%dT%H%i%S')

【问题讨论】:

    标签: mysql datetime


    【解决方案1】:

    Date and Time Literals中所述:

    MySQL 可识别以下格式的 DATE 值:

    • 作为'YYYY-MM-DD''YY-MM-DD' 格式的字符串。允许使用“宽松”语法:任何标点符号都可以用作日期部分之间的分隔符。例如,'2012-12-31''2012/12/31''2012^12^31''2012@12@31' 是等价的。

    • 作为'YYYYMMDD''YYMMDD' 格式的不带分隔符的字符串,前提是该字符串作为日期有意义。例如,'20070523''070523' 被解释为'2007-05-23',但'071332' 是非法的(它有无意义的月份和日期部分)并变成'0000-00-00'

    • 作为YYYYMMDDYYMMDD 格式的数字,前提是该数字作为日期有意义。例如,19830905830905 被解释为'1983-09-05'

    MySQL 可识别以下格式的 DATETIMETIMESTAMP 值:

    • 作为'YYYY-MM-DD HH:MM:SS''YY-MM-DD HH:MM:SS' 格式的字符串。这里也允许使用“宽松”的语法:任何标点符号都可以用作日期部分或时间部分之间的分隔符。例如,'2012-12-31 11:30:45''2012^12^31 11+30+45''2012/12/31 11*30*45''2012@12@31 11^30^45' 是等价的。

      日期和时间部分与小数秒部分之间唯一可识别的分隔符是小数点。

      日期和时间部分可以用T 分隔,而不是空格。例如,'2012-12-31 11:30:45''2012-12-31T11:30:45' 是等价的。

    • 作为'YYYYMMDDHHMMSS''YYMMDDHHMMSS' 格式的不带分隔符的字符串,前提是该字符串作为日期有意义。例如,'20070523091528''070523091528' 被解释为'2007-05-23 09:15:28',但'071122129015' 是非法的(它有一个无意义的小部分)并变成'0000-00-00 00:00:00'

    • 作为YYYYMMDDHHMMSSYYMMDDHHMMSS 格式的数字,前提是该数字作为日期有意义。例如,19830905132800830905132800 被解释为'1983-09-05 13:28:00'

    值得注意的是,MySQL 不支持您希望使用的不完整格式。

    MySQL碰巧接受您尝试过的一些不完整的格式(显然是用零填充)是未记录的行为,很可能不是由开发人员。它不能(并且不应该)值得依赖,尤其是因为可能存在导致行为中断的边缘情况;或者因为在未来的版本中可能会在没有警告的情况下更改行为。

    如果绝对有必要向 MySQL 提供这种不完整的时间文字(不应该这样做,因为您的数据访问层应该知道它正在处理的值的类型并提供它们到 MySQL 的支持格式),您可以使用其 STR_TO_DATE() 函数相应地解析它们:

    未指定的日期或时间部分的值为 0,因此 str 中未完全指定的值会产生部分或全部部分设置为 0 的结果:

    mysql> SELECT STR_TO_DATE('abc','abc');
            -> '0000-00-00'
    mysql> SELECT STR_TO_DATE('9','%m');
            -> '0000-09-00'
    mysql> SELECT STR_TO_DATE('9','%s');
            -> '00:00:09'

    日期值部分的范围检查如Section 11.3.1, “The DATE, DATETIME, and TIMESTAMP Types” 中所述。这意味着,例如,允许“零”日期或部分值为 0 的日期,除非 SQL 模式设置为不允许此类值。

    因此,例如,您可以使用:

    STR_TO_DATE('2015-03', '%Y-%m');
    

    【讨论】:

    • 附录:在问题中给出的WHERE 子句示例中“支持”不完整格式的原因是因为这些文字仅被解析为字符串;并根据数据库连接的排序按字典顺序进行比较。虽然它不是时间类型的真正比较(并且由于不能使用时间列上的索引而不能进行 sargable),但是(由于 MySQL 的日期时间格式的布局)这种字典顺序会产生与真正的时间类型相同的结果.
    • 谢谢。我希望“使用”这些时间戳的唯一原因是因为 MySQL 一开始就没有(全部)错误。我希望不必编写将正确的 ISO8601(如“2015-03”)转换为 MySQL DATETIME 格式的东西,但是唉。
    • @Lieuwe:执行此操作的代码通常已经存在于一个数据访问层中——所以除非你正在编写自己的代码(鉴于已经有大量优秀的库,不建议这样做提供这样的功能),你真的不应该摆弄这样低级的实现细节。
    • 我使用 C++,我收到一个“通常”符合 ISO8601 的日期时间字符串,该字符串需要存储在数据库中。我可以清楚地使用 strptime、std::get_time 或 sscanf 之类的东西来尝试将字符串解析为时间结构,然后再将其重新组装成 MySQL 可以理解的日期时间字符串,但这不是重点。如果我尝试在数字数据库字段中插入非数字的内容,或者在枚举字段中插入无效字符,MySQL 只会返回错误。我曾希望它对 ISO8601 时间戳做同样的事情,而我不知道该字符串的确切格式。
    【解决方案2】:

    试试 unix_timestamp()

    SELECT like [..] WHERE unix_timestamp(timestamp) < '2015-03'
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-06-16
      • 2012-04-28
      • 2017-03-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多