【问题标题】:Create date from day, month, year fields in MySQL从 MySQL 中的日、月、年字段创建日期
【发布时间】:2011-04-26 23:30:58
【问题描述】:

我目前正在开发一个显示文档的应用程序,并允许成员通过许多不同的参数搜索这些文档,其中一个参数是日期范围。

我遇到的问题是数据库架构不是我自己开发的,数据库的创建者创建了一个“日期”表,其中包含“日”、“月”、“年”的字段。

我想知道如何从表中选择特定的日、月、年并在 SQL 中创建一个日期对象,以便我可以比较用户使用 BETWEEN 输入的日期。

下面是日期表的结构:

CREATE TABLE IF NOT EXISTS `date` (
  `deposition_id` varchar(11) NOT NULL default '',
  `day` int(2) default NULL,
  `month` int(2) default NULL,
  `year` int(4) default NULL,
  PRIMARY KEY  (`deposition_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

【问题讨论】:

  • 天啊。人们为什么要对他们的数据库这样做? :-(
  • 我实际上最终更改了数据库表以创建一个日期时间字段,然后连接年、月、日字段并根据 deposit_id 更新行。似乎是一种享受。
  • @Spudley 遗憾的是,这并不少见……甚至 'crunchbase' api 也以这种方式返回日期……这很糟糕……:-(
  • @Spudley 因为 MySQL 的日期时间和时间戳类型,以及您无法选择退出的时区转换这些年来仍然是垃圾。
  • @MichaelFulton 你可能是对的,他们有问题。但是,如果您告诉我使用日期时间字段或时间戳比问题中描述的解决方案更糟糕,那么恐怕我将不得不恭敬地不同意。

标签: sql mysql database datetime


【解决方案1】:

当您有年、月和日的整数值时,您可以通过组合 MAKEDATE() 和 DATE_ADD() 来创建 DATETIME。固定日期为 1 的 MAKEDATE() 将为您提供给定年份的第一天的 DATETIME,然后您可以使用 DATE_ADD() 添加月份和日期:

mysql> SELECT MAKEDATE(2013, 1);
+-------------------+
| MAKEDATE(2013, 1) |
+-------------------+
| 2013-01-01        |
+-------------------+

mysql> SELECT DATE_ADD(MAKEDATE(2013, 1), INTERVAL (3)-1 MONTH);
+---------------------------------------------------+
| DATE_ADD(MAKEDATE(2013, 1), INTERVAL (3)-1 MONTH) |
+---------------------------------------------------+
| 2013-03-01                                        |
+---------------------------------------------------+

mysql> SELECT DATE_ADD(DATE_ADD(MAKEDATE(2013, 1), INTERVAL (3)-1 MONTH), INTERVAL (11)-1 DAY);
| DATE_ADD(DATE_ADD(MAKEDATE(2013, 1), INTERVAL (3)-1 MONTH), INTERVAL (11)-1 DAY) |
+----------------------------------------------------------------------------------+
| 2013-03-11                                                                       |
+----------------------------------------------------------------------------------+

所以回答OP的问题:

SELECT * FROM `date`
WHERE DATE_ADD(DATE_ADD(MAKEDATE(year, 1), INTERVAL (month)-1 MONTH), INTERVAL (day)-1 DAY)
BETWEEN '2013-01-01' AND '2014-01-01';

【讨论】:

  • 肯定比创建和使用字符串更优雅。
  • 很好的解决方案——为什么 MySQL 没有这个原生的?似乎这比一年有 100 天或任何天的用例更有可能
  • 我更喜欢你的解决方案。唯一的问题是 makedate(99,1) 返回 1999 年 1 月 1 日。有没有办法让它返回 01/01/0099?
  • 我在使用 MAKEDATE 时遇到了问题,因为 2 月的天数(大部分时间)发生了变化。当您想使用 DAYOFYEAR 根据另一个日期的月份和日期(星期几)创建日期以便将一年中的那一天与不同的年份一起传递给 MAKEDATE 时,您有时会在前一天或后一天获得一个日期上一年。但是我想这个答案的最后一部分没有这个问题..
【解决方案2】:

你可以使用STR_TO_DATE()函数。

【讨论】:

  • STR_TO_DATE(CONCAT(date_year,'-',LPAD(date_month,2,'00'),'-',LPAD(date_day,2,'00')) , '%Y-%m-%d')
  • 不需要LPAD 数字。 STR_TO_DATE 为您做到这一点:STR_TO_DATE(CONCAT(date_year,'-',date_month,'-',date_day), '%Y-%m-%d')
  • 不确定为什么 SO 将单引号更改为 html 实体。
  • @pbarney 我想补充一点,STR_TO_DATE('1-1-1', '%Y-%m-%d') 的计算结果是 2001 年 1 月 1 日,至少在我测试它的 MySQL 数据库上是这样。如果您不喜欢这种行为,则必须LPAD 年份组件,这样您就有STR_TO_DATE('0001-1-1', '%Y-%m-%d'),它返回第一年一月的第一天。我不知道是否有任何文化配置或您需要的任何东西可以改一下,避免不得不LPAD当年,但是我没找到。
【解决方案3】:

最简单的方法是:

DATE(CONCAT_WS('-', year, month, day))

正如@pbarney 之前指出的那样,LPAD 不是必需的。如果您要与另一个日期对象进行比较,则不必用 DATE 包装它,因为 MySQL 会自动转换它:

some_date_field > CONCAT_WS('-', year, month, day)

【讨论】:

    【解决方案4】:

    要从中构建可排序的日期字符串,您需要CONCAT 将这些位连接在一起,并需要LPAD 以确保月份和日期字段的长度为两位数。像这样的:

    CONCAT(`year`,'-',LPAD(`month`,2,'00'),'-',LPAD(`day`,2,'00'))
    

    一旦你有了它,你应该可以使用BETWEEN,因为它们将采用可排序的格式。但是,如果您仍需要将它们转换为实际的日期时间字段,则可以将整个内容包装在 UNIX_TIMESTAMP() 中以获取时间戳值。

    所以你最终会得到这样的结果:

    SELECT UNIX_TIMESTAMP(CONCAT(`year`,'-',LPAD(`month`,2,'00'),'-',LPAD(`day`,2,'00'))) as u_date
    WHERE u_date BETWEEN timestamp_1 and timestamp_2
    

    但是,请注意,与字段最初只是一个简单的时间戳相比,这会大大慢一些。而且您绝对应该确保在年、月和日字段上有一个索引。

    【讨论】:

      【解决方案5】:

      扩展this answer,这是我的看法:

      DELIMITER $$
      
      CREATE FUNCTION fn_year_month_to_date(var_year INTEGER,
          var_month enum('01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12')
          )
      RETURNS DATE
      BEGIN
          RETURN (MAKEDATE(var_year, 1) + INTERVAL (var_month - 1) MONTH);
      END $$
      
      DELIMITER ;
      
      SELECT fn_year_month_to_date(2020, 12)
      ;
      

      【讨论】:

        【解决方案6】:

        尝试使用 CONCAT() 并将其设为一个字段并进行比较。

        不确定您是否可以将其作为连接后的日期进行比较。

        你可以比较为整数。

        将年月日连接起来,制作一个像 20101017 这样的整数并进行比较。

        希望:)

        【讨论】:

          猜你喜欢
          • 2015-10-10
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-05-06
          • 1970-01-01
          • 1970-01-01
          • 2016-07-27
          • 1970-01-01
          相关资源
          最近更新 更多