【问题标题】:MySQL datatype to store month and year only [closed]mysql数据类型仅存储月份和年份[关闭]
【发布时间】:2012-02-26 09:59:03
【问题描述】:

我正在编写一个将 STUDENT 数据存储在 MySQL 关系数据库中的 php 应用程序。我正在尝试找到最好的方式/数据类型来存储一个月和一年而没有一天。我不知道我是否应该将其存储为 DATE 并以某种方式使用 php 将日期存储为第一天或使用我目前不熟悉的不同数据类型。理想情况下,我不想存储一天,因为这一天并不总是相同的,并且如果将来这一天发生变化,则需要更改 php 源代码。

只是为了获取更多背景信息,我正在存储一个学生的 INTENT_TO_GRAD。客户似乎只希望此信息作为报告的参考或视觉效果,而不是将其用于数据操作。换言之,此数据的唯一功能要求是显示在报告中。

【问题讨论】:

  • 您是否考虑将月份和年份作为 SMALLINT 存储在单独的字段中?

标签: mysql sql database-design


【解决方案1】:

从 5.7 开始,可以使用生成的列,这使您可以强制执行年月唯一性,同时还可以在另一个字段上使用日期函数,而不会因复制而破坏数据完整性:

CREATE TABLE `year_month` (
  `Year` int(11) NOT NULL,
  `Month` enum(
      'January',
      'February',
      'March',
      'April',
      'May',
      'June',
      'July',
      'August',
      'September',
      'October',
      'November',
      'December'
      ) NOT NULL,
  `GENERATED_YearMonth` date
      GENERATED ALWAYS AS
          ((makedate(`Year`,1) + interval (`Month` - 1) month))
      STORED,
  PRIMARY KEY (`Year`,`Month`),
  KEY `year_month_GENERATED_YearMonth_index` (`GENERATED_YearMonth`)
)
;

【讨论】:

    【解决方案2】:

    另一种解决方案是从您的 DATETIME/DATE 源构建生成的列。

    ALTER TABLE stats 
    ADD COLUMN year SMALLINT GENERATED ALWAYS AS (YEAR(stat_date)) NOT NULL,
    ADD COLUMN month smallint GENERATED ALWAYS AS (MONTH(stat_date)) NOT NULL;
    

    背景

    我遇到了类似的问题。阅读此线程后,我决定存储不完整的日期(零)。它适用于旧版本的 MySQL,但新版本会产生“日期不正确”错误。如前所述,您可以使用NO_ZERO_IN_DATE 设置将错误变为警告。但是该设置本身已被弃用。因此,将来只能通过禁用严格模式来支持日期中的零,从而消除其他类型的错误

    由于 NO_ZERO_IN_DATE 已被弃用,它将在未来的 MySQL 版本中作为单独的模式名称被删除,其影响包含在严格 SQL 模式的影响中。

    我的要求是建立一个表的月视图。我不得不在一个月内添加一个索引。因此,使用 YEAR()MONTH() 即时计算 moth 不是一种选择。

    生成的列达到了它们的目的。 yearmonth 列不是主索引的一部分,由于VIRTUAL(不是STORED)生成的列,我什至可以节省空间。

    链接

    【讨论】:

      【解决方案3】:

      您可以使用VARCHAR 数据类型仅存储月份和年份。

      对于那些使用datepicker的人:-

      1)在mysql中设置VARCHAR数据类型来存储月份和年份。

      2)如果您使用 jquery 验证并且有 jquery 插件日期选择器,那么您必须执行以下代码。

      例如:-

      <script>
      
      $(document).ready(function (){
      
      $('#monthyear').datepicker({
        autoclose:true,
        format:'mm-yy'
      });
      
      //Your form validation code here//
      
      });
      </script>
      

      【讨论】:

        【解决方案4】:
        SELECT * FROM Users
        WHERE 
            MONTH(DateTime) = '07'
        AND 
            YEAR(DateTime) = '2015'
        AND 
            DAY(DateTime) = '26'
        

        您可以像上面那样进行过滤,但如果您想单独存储年/月和日,在您应用索引及其非常大的数据之前它是无用的。

        【讨论】:

          【解决方案5】:

          何必呢?只需将其存储为一个完整的日期(也许总是使用第一个作为日期),如果您只需要其中的一部分,请使用数据库函数 MONTH()YEAR()。这使得使用该字段更加容易,因为您仍然可以进行范围查询等。

          【讨论】:

          • 我同意将其存储为日期。稍后客户很可能想要输入学生的实际毕业日期。
          • 如果将它们分开存储也可以进行范围查询,并且比使用 MONTH() 和 YEAR() 函数更快。如果您确实将它们存储在一起,请务必使用第一天,因为总是有一个,您可以使用像 BETWEEN '2012-04-01' AND '2012-12-01' 这样的日期来搜索范围。
          • @MarcusAdams,我同意性能部分,我不明白“如果你确实将它们存储在一起,一定要使用第一天,因为总是有一个”。 6 月 31 日毕业的学生我认识的不多。
          • 我想如果我将它存储为一个完整的日期,使用第一个作为日期,我可以选择使用 SUBSTR() 或 LEFT() 仅显示前 7 个字符以仅显示 YYYY -MM 部分通过删除 -DD。
          • +1 用于使用DATE,但使用EXTRACT(YEAR_MONTH FROM mydate) 进行比较,DATE_FORMAT(mydate, '%Y-%M') 用于显示。不要忘记另一个方便的date functions
          【解决方案6】:

          在 MySQL 手册中说您可以存储部分日期

          http://dev.mysql.com/doc/refman/5.1/en/date-and-time-functions.html#function_date-format

          月份和日期说明符的范围从零开始,因为 事实上,MySQL 允许存储不完整的日期,例如 '2014-00-00'。

          这意味着只存储年份和月份,您可以使用DATE 列并用00 代替日期。例如2013-12-00

          相关:Store incomplete date in MySQL date field

          【讨论】:

          • 不幸的是,在 MySQL 5.7+ 上似乎默认不允许这种行为:dev.mysql.com/doc/refman/5.7/en/…
          • @CodeCommander 很好,幸运的是,您可以修改链接中提到的 sql_mode SET GLOBAL sql_mode = 'ONLY_FULL_GROUP_BY, STRICT_TRANS_TABLES, ERROR_FOR_DIVISION_BY_ZERO, NO_AUTO_CREATE_USER, NO_ENGINE_SUBSTITUTION';
          【解决方案7】:

          考虑您将如何使用这些数据。如果您必须创建任何报告,哪种方式可以让您更轻松地检索和处理数据?

          而且您不必使用日期类型字段。您可以只有一个 Year 字段和一个 Month 字段,它们都是整数。然后,当您确实需要对其进行任何需要日期的表达式时,将它们放在一起并转换为日期就很容易了。

          将日期存储为日期为 1 的日期并忽略它是完全可以的,也相当正常。最后,这不是一个很重要的决定(相对而言),所以我会选择你最喜欢的一个并完成它。

          【讨论】:

          • 您可以将年份和月份作为整数存储在不同的列中。但是 MySQL 让它稍微痛苦一些,因为它不支持CHECK (month between 1 and 12);你必须添加一个表和一个外键约束来保证实际月份。
          • @Catcall 嗯,无论如何我都不会添加支票所以对我来说不是问题:)
          • 我可能是错的,但你不能只使用 CHECK (month in ('1','2','3',...))...或者只是拥有应用程序检查
          • MySQL 不支持 CHECK 约束。
          • 无论你是否使用 MySql,应用程序真的应该在它到达数据库之前强制执行这个约束。
          【解决方案8】:

          我会将两者存储为两个单独的列作为整数。它将成为验证蛋糕,并允许快速轻松地进行排序和分组。

          【讨论】:

          • 糟糕的解决方案。如果您想获取 3 个月前的数据怎么办?你需要做一些奇怪的事情,比如 (Year x 12 + Month - 1)
          • @Peter 最初的问题是他们只想将数据用于显示。我不明白你的数学是如何应用的。
          • @Peter 如果我将yearmonthday 存储为单独的列,id 仍然能够在后端和前端对它们进行排序和过滤。 id 只需检索所有值并将它们组合或从中创建一个日期对象
          • @Anthony 现在是 2019 年 2 月 17 日。请为过去 24 天的记录构建查询。恶梦。 “id 只需检索所有值”或者您可以构建查询select .. from ... where date &gt; '...' done,这是 db 的主要目的
          • @Peter 但我永远不需要在我的应用程序中选择最后 24 天:/
          猜你喜欢
          • 2014-08-07
          • 1970-01-01
          • 2012-12-07
          • 2019-04-25
          • 2012-02-24
          • 2016-08-30
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多