问题 如何在DB和ORM中设计出生日期,以便混合已知和未知的日期部分


请注意,我的问题与SO问题类似 1668172


这是一个设计问题,以前肯定必须为其他人弹出,但我找不到适合我情况的答案。我想在我的应用程序中记录出生日期,其中包含几个“级别”的信息:

  • NULL 价值,即DoB未知
  • 1950-??-?? 只知道DoB年份值,日期/月份不是
  • ????-11-23 只需一个月,一天,或两者的组合,但没有一年
  • 1950-11-23 完整的DoB是众所周知的

我正在为我的应用程序使用的技术如下:

  • Asp.NET 4(C#),可能与MVC
  • 一些ORM解决方案,可能是Linq-to-sql或NHibernate的
  • MSSQL Server 2008,起初只是Express版

到目前为止,我想到了SQL位的可能性:

  • 1)使用一个可空的varchar柱,例如 1950-11-23,用'X'代替unkowns,例如 XXXX-11-23 要么 1950-XX-XX
  • 2)使用三个可空的int列,例如 195011,和 23
  • 3)使用年份的INT列,以及完整已知DoB的日期时间列

对于这个问题的C#结尾,我只涉及这两个选项:

  • A)使用字符串属性来表示DoB,仅用于视图目的。
  • B)为DoB使用自定义(?)结构或类,具有三个可空整数
  • C)使用可空的DateTime和可以为空的整数

解决方案似乎形成匹配对 1A2B 要么 3C。当然1A不是一个很好的解决方案,但它确实设置了基线。

任何提示和链接都非常感谢。好吧,如果他们有关系,无论如何:)


编辑,关于答案:我将一个答案标记为已被接受,因为我认为这对我有用。不过,如果你在这里遇到同样的问题,也值得一看其他答案。


2617
2018-06-21 21:00


起源

+1。有趣的问题。 - RichardOD
发现另一个提出类似问题的问题: stackoverflow.com/questions/1668172/... - Nathan Tregillus


答案:


SQL端

我对这个主题的最新想法是使用不确定或具有不同特异性的日期范围。给出两列:

DobFromDate (inclusive)
DobToDate (exclusive)

以下是它如何与您的场景一起使用:

Specificity   DobFromDate   DobToDate
-----------   -----------   ----------
YMD            2006-05-05   2006-05-06
YM             2006-05-01   2006-06-01
Y              2006-01-01   2007-01-01
Unknown        0000-01-01   9999-12-31
-> MD, M, D not supported with this scheme

请注意,没有理由不能将它一直带到小时,分钟,秒,毫秒等等。

然后在查询特定日期出生的人时:

DECLARE @BornOnDay date = '2006-05-16'

-- Include lower specificity:
SELECT *
FROM TheTable
WHERE
   DobFromDate <= @BornOnDay
   AND @BornOnDay < DobToDate;

-- Exclude lower specificity:
SELECT *
FROM TheTable
WHERE
   DobFromDate = @BornOnDay
   AND DobToDate = DateAdd(Day, 1, @BornOnDay);

这对我来说具有可维护性,易用性和表现力的最佳组合。它不会处理更重要的值(例如,你知道月份和日期而不是年份)的精确度损失,但如果可以解决这个问题,那么我认为它是一个胜利者。

如果您将按日期查询,那么一般来说,更好的解决方案(在我看来)将是以某种方式将项目保存为服务器上的日期的解决方案。

此外,请注意,如果您正在寻找日期范围而不是一天,使用我的解决方案,您仍然只需要两个条件,而不是四个:

DECLARE
   @FromBornOnDay date = '2006-05-16',
   @ToBornOnDay date = '2006-05-23';

-- Include lower specificity:
SELECT *
FROM TheTable
WHERE
   DobFromDate < @ToBornOnDay
   AND @FromBornOnDay < DobToDate;

C#侧

我会使用一个自定义类,其中包含对其进行适当的日期数学和日期比较所需的所有方法。您知道如何使用未知日期的业务要求,并且可以对类中的逻辑进行编码。如果您在特定日期之前需要某些东西,您是否只使用已知或未知的物品?什么会 ToString()返回?在我看来,这些是最好用课程解决的。


3
2018-06-21 21:18



有趣的解决方案。从来没有想过这个。我将把它归档以供进一步用于需要逐步设置日期精度的项目! - Nathan Tregillus
哇,很棒的建议。对于已知MD,D,M的情况的(提及)例外情况,这将允许我在数据库方面很好地表示事物,并且正如您所提到的,C#端可以随意添加它。首先要晚上睡一觉,但很可能会使用这个解决方案,因为唯一的例外恰好是一个只有“好”的场景:) Tx @ErikE - Jeroen
更多地考虑这个解决方案。对我的数据提出一些疑问,这表明我很少有一个月或一天而不是一年。所以我正在跳过“很高兴”的要求,并尝试这个解决方案。再次@ErikE,标记为答案。 - Jeroen
酷@Jeroen,我很高兴它会对你有用。小心使用包容性/独占端点并正确使用= vs.> =。 - ErikE
已经过了一年,但我终于在我的爱好项目中解决了这个问题。它就像一个魅力,在UncertainDate的额外C#类的帮助下。一旦24小时的奖励等待期结束,您将获得额外奖励! - Jeroen


我喜欢在C#中使用3个可空列和3个nullable int的结构。

它确实需要在数据库处理方面做一些努力,但是你可以避免解析字符串,你也可以直接用SQL或年和月查询SQL等等......


2
2018-06-21 21:11



我的第一次尝试确实只是这一次,它对你所拥有或没有的信息非常“诚实”。但是,@ ErikE上面的回答是一个很好的竞争对手,因为它对信息也是“诚实的”:)。决定,决定......无论哪种方式:感谢您的回复! - Jeroen


无论你做什么,都会变得很混乱。对于这类日期的消费者,我会写一个特殊的类/结构,它封装了它的日期(我可能称之为PartialDate),以便更容易为消费者处理 - 就像Martin Fowler提倡者 钱类

如果你曝光了 约会时间 直接在C#中,如果您的“日期”为???? - 11-23,并且您想确定客户是否超过18,那么这可能会导致混淆 - 您将如何默认日期,消费者将如何知道日期的一部分是无效的......

拥有PartialDate的额外好处是它允许其他人阅读您的代码,以便快速意识到他们不是正常的,完整的日期,不应该被视为这样!

编辑

考虑到部分数据概念,我决定使用谷歌。我发现有这个概念 部分在Joda时间 和 关于这个主题的有趣PDF,这可能对您有用,也可能对您没用。


2
2018-06-21 21:15



感谢您的回复,以及链接@RichardOD,明天会看一下。 - Jeroen


有趣的问题......

我喜欢解决方案2B而不是解决方案3C,因为使用3C,它不会被规范化...当您更新其中一个整数时,您还必须更新DateTime或者您将不同步。

但是,当您将数据读入C#端时,我将拥有一个属性,将所有整数汇总到一个字符串格式,就像您在解决方案1中一样,以便可以轻松显示它。

我很好奇你需要对这些数据做什么类型的报告......或者你只是在数据库中存储和检索它。


1
2018-06-21 21:15





我不会太担心如何存储日期,我仍然会将日期存储在日期时间字段中,但是,如果知道日期的某些部分是否未填充,我会在日期的每个部分都有标记。无效,因此您的架构将是:

DBODate作为日期 DayIsSet as Bit MonthIsSet为Bit YearIsSet as Bit。

这样,您仍然可以实现所有有效的日期比较,并且仍然知道您正在处理的日期的精确度。 (至于日期,我总是将默认部分默认为该值的最小值:IE月份默认为1月,日期为第一,年份为1900或其他)。


1
2018-06-21 21:15



我试图密切关注datetime数据类型的原因是因为您自动获得所有正确的日期检查功能,如2月29日,相应月份的天数等。 - Nathan Tregillus
@ NB-我原来有同样的想法DB。问题是当你不知道年份时 - 你怎么知道2月29日是有效的?你还需要默认为一年的闰年,以适应2月29日的年份未知的情况...... - RichardOD
@RichardOD非常好点。任何这些解决方案都需要一些围绕日期验证的自定义逻辑。我不羡慕你的问题!我仍然认为最好的方法仍然是为每个日期字段设置默认值。有趣的是,年0004实际上是闰年,它由SQL中的DateTime2数据类型处理。 - Nathan Tregillus
响应@ N8的Tx。我不确定我是否会选择这个,因为日期列中的值本身不正确,这使得我在查询时会犯错误(你不知道没有那些位字段的日期意味着什么)。 - Jeroen
非常,我祝你好运。谢谢你有趣的问题! - Nathan Tregillus


显然,上面提到的所有解决方案确实代表了某种妥协。

因此,我建议仔细考虑哪个“级别”最有可能并优化。然后针对其他罕见情况进行适当的异常处理。

我不知道报告现在是否是一个问题,或者可能是稍后,但您可能会认为这是除了DB / C#问题之外的第三个维度。


1
2018-06-21 21:48



Wups,甚至还没想过要报告:O,在决定选择哪种解决方案时也可能需要考虑! - Jeroen