在 SQL 数据库中存储没有年份的日期的最佳方法是啥?

Posted

技术标签:

【中文标题】在 SQL 数据库中存储没有年份的日期的最佳方法是啥?【英文标题】:What is the best way to store a date without a year in SQL database?在 SQL 数据库中存储没有年份的日期的最佳方法是什么? 【发布时间】:2010-04-20 12:29:47 【问题描述】:

我正在构建一个应用程序,该应用程序具有应存储在数据库中的每日报价。每个报价都分配有一年中的一天,包括 2 月 29 日的一天。由于报价只关心日期而不是年份,我还应该使用 smalldatetime 类型吗?请发表意见,谢谢!!

【问题讨论】:

报价是否提到了实际日期?例如4 月 4 日,民权活动家和诺贝尔和平奖获得者马丁路德金遇刺身亡。 . . 我还不确定所有的内容,所以我假设他们可能...... 能否提供更多详细信息 - 您是在构建日历应用程序还是其他什么?你的模型中这个日/月属性的含义是什么?你将如何使用它?您可以两种方式存储;区别取决于对上述问题的回答。 【参考方案1】:

我最近遇到了这个问题,我最初的设计确实存储了日期,而我只是忽略了年份。但是,感觉不太对。我决定将其删除并使用单独的 Day/Month 列代替。它只是感觉更干净,更具可读性。

更新

自从我写了这个答案很久以来,事后我举起双手说 cmets 被天真地忽视了。通过将日/月存储为单独的字段,可能会存储无效数据,而如果您将它们存储为完整的DateTime,您实际上是免费获得该验证。

根据您的验证策略,这可能不是问题,但是,如果您依赖数据库验证,那么我建议您将其存储为 DATE 并简单地提取相关信息或使用触发器运行一些验证在插入之前。

【讨论】:

我不会说如果你必须在之后对数据进行任何一天的计算会更干净。 @Unreason:从数据库的角度来看,它更干净。存储完整日期 IMO 是错误的。您应该只存储您需要的信息。 @James:如果您没有在一天/一个月内进行适当的检查,那么您的解决方案只会显得更干净。通过使用日期域,您可以免费获得这些检查。至于只存储你需要的信息——我们在那里并没有太大的偏差(年份没有意义,它不是信息);这种情况类似于从十进制(4,4)类型中浪费一些位。好处是您可以快速且良好地进行域检查。 @Unreason:是的,但是,如果您将日期验证为20/04/2010,而实际上您真正指的日期是20/04/2011,那么您的内置检查不会为您提供他们只是验证 20/04 是 2010 年的有效日期,这对您没有用处,它与您想要的 real 年份无关。对我来说,无论如何都必须在应用程序端检查日期,然后才能将其持久化到数据库中。 @James 我认为 Unreason 更关心尝试存储 31/04 并让数据库获取该错误。通过使用日期时间列,您可以免费获得这种检查。虽然您通过进行客户端验证来解决这个问题,但它确实让您可以让数据库包含无效的数据。使用日期时间列,数据库无法存储无效数据。或者,您可以通过插入触发器来防止无效的日/月组合来伪造检查 - 但我宁愿数据库为我做数据库工作,而不是手动模拟数据库功能。【参考方案2】:

另一种选择(我认为其他人没有提供)是将月份和日期存储为单独的整数。因此,要查找今天的条目,您可以:

select quote from quoteTable where month = 4 and day = 20;

这将允许您在不使用日期(并忽略年份)的情况下获得特定日期的消息。

只是一个想法。

【讨论】:

@Todd...我相信他们有(见我的帖子)【参考方案3】:

这取决于您要如何处理这些日期。 如果您的数据库中的年份不是问题,那么您可以采用闰年并将其用于存储日期,在您的应用程序视图中忽略它。

【讨论】:

我不会这样做。您在数据库中存储了不正确的数据。这可能会导致意想不到的问题。 @jmgant:没有什么会导致意想不到的问题(!):P,但说真的 - 他存储的不是不正确的数据,而是无意义的数据;这几个位用于验证数据的域,这样您就不必像处理日期和月份字段以及此类字段的变体那样具有复杂的检查约束。 @Unreason:我认为这是不正确的。日期时间表示时间中的特定实例。使用该数据类型来存储每年重复的月份和日期,这是一种不同类型的数据,IMO 是错误的。此外,已经决定 2 月 29 日总是可以接受的,而且由于我们不包括时间或非西方日历,因此验证并不那么困难。 @jmgant,是我们为存储的位赋予语义。是的,这可能会产生误导。是的,如果有一个更合适的域会更好。但是,不,这不仅仅是错误的 - 只不过是说 decimal(2,1) 表示一条线上的点,并且只在其中存储域的一个已知子集(例如比率 x/2 或 x/4)只是错了(类比不是很好,但希望点确实得到了跨越)。将它们分成两列可能会更好,但如果您只对比率的十进制表示感兴趣,则不是。 :)【参考方案4】:

如果您需要保留日期和月份数据,您不妨使用SmallDateTime 并简单地忽略年份部分(或将其设置为全面相同的值,例如 2000 年是闰年,所以闰日是允许的)。

您仍然可以使用正确的日期和时间函数和正确的数据类型,如果您使用 VARCHAR 字段,您最终将转换为它。

【讨论】:

我不会使用 1900 作为年份值:如果这样做,您将无法存储 2 月 29 日。请改用 2000。【参考方案5】:

我不禁感到,如果日历是由软件工程师发明的,闰日将是 12 月 32 日而不是 2 月 29 日。这样你就可以简单地使用从 1 月 1 日开始的 smallint 偏移量.

您仍然可以使用从 3 月 1 日开始的 smallint 偏移量,其中 3 月 1 日为 0,3 月 2 日为 1,2 月 29 日为 365。但是它涉及对您想要的数字进行某种自定义转换,并且可能无法排序如你所愿。

鉴于您可以将 Day 和 Month 存储为占用相同空间的两个 tinyint,我不确定这是否是一个好计划,但为了完整起见,我想提一下。

【讨论】:

【参考方案6】:

由于没有像 Oracle 这样的 Interval 类型,因此您可以选择其中一种。

使用日期时间时存储年份/ smalldatetime,它会花费 你没有多余的东西来存储它,只是 选择不显示。 采用带日期的 DW 类型方法 表并使用 PK/FK 链接到它 使用非基于日期的类型,例如 smallint 或 varchar,虽然这 可能会导致一些困难 让查询保持可搜索性 并避免扫描。

【讨论】:

【参考方案7】:

一个连续的数字怎么样。您可以每次随机选择引号并在选择时标记另一个布尔字段。您可以在年底重置布尔字段。

这还允许您随着时间的推移向数据库中添加更多报价,而无需删除已有的报价。

【讨论】:

【参考方案8】:

您仍然可以在数据库中使用 datetime 列并使用 DatePart() SQL 函数来检索一年中的哪一天。

SELECT datepart(dy,myDateColumn) FROM myTable

【讨论】:

【参考方案9】:

我会避免为此使用日期时间。从某种意义上说,您将存储不正确的数据。例如,您将 4/20/2010 存储为 4/20/2012 或您选择的任何年份。即使年份对您的申请而言并不重要,但这种方法可能会导致一些意想不到的问题。

例如,如果您在其中找到了错误年份的日期怎么办?你的计算是错误的。

由于没有本地类型来支持您正在做的事情,我会将值存储为 varchar 并在用户定义的函数中进行任何必要的计算。

【讨论】:

【参考方案10】:

0305 是 3 月 5 日的 varchar 怎么样。

【讨论】:

【参考方案11】:

您还可以考虑使用单个 int 来存储一年中的某一天。

在人类可读的格式和日期之间进行转换可能会有点痛苦。另一方面,很容易将日期分配给引号并选择它们。

SELECT quote FROM QuoteTable 
WHERE dayOfYear = DATEPART(dy, GETDATE())

【讨论】:

【参考方案12】:
CONVERT(VARCHAR(5),GETDATE(),101)

【讨论】:

虽然这段代码 sn-p 可以解决问题,including an explanation 确实有助于提高您的帖子质量。请记住,您正在为将来的读者回答问题,而这些人可能不知道您的代码建议的原因。你可以edit你的答案包括解释。 除此之外,@Kyll 将日期存储为字符串是很糟糕的建议。这整个问题也叫“过早的优化”。

以上是关于在 SQL 数据库中存储没有年份的日期的最佳方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

检索日期中给定年份的所有 ORM 记录

检查日期是不是在 sql 中的日期范围之间(不检查年份)

SQL Server 2008 仅在月份和年份之间选择数据

在 SQL Server 存储过程中验证日期参数

SQL Server 2005获取任何年份中任何月份的第一个和最后一个日期

为 ASP.NET 编写月份和年份下拉列表的最佳方法是啥?