如何在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 \'代替未知内容,例如
XXXX-11-23
1950-XX-XX
2)使用三个可为null的int列,例如
1950
11
23
3)将INT列用于年份,将datetime列用于完整的DoB 对于此问题的C#端,我仅涉及以下两个选项: A)使用字符串属性表示DoB,仅出于查看目的进行转换。 B)为DoB使用custom(?)结构或类,其中包含三个可为空的整数 C)在年份中使用可为空的DateTime以及可为空的整数 解决方案似乎在1A,2B或3C形成匹配对。当然1A不是一个很好的解决方案,但它确实设置了基准。 任何提示和链接都将受到高度赞赏。好吧,如果他们有关系,无论如何:) 编辑有关答案的内容:我将一个答案标记为已接受,因为我认为这对我有用。如果您在此处遇到相同的问题,还是值得一试的。     

解决方法

        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 int可为空的列和C#中3可为int的结构的想法。 它确实在数据库处理上花费了一些精力,但是您可以避免围绕字符串进行解析,并且还可以直接按年份或年份和月份使用SQL查询,等等。     ,        无论您做什么,都会使DB混乱。对于这类日期的消费者,我将编写一个特殊的类/结构,封装该日期/日期的类型(我可能将其称为PartialDate之类的名称),以使其更容易为消费者处理(例如Martin)福勒提倡金钱阶层。 如果您直接在C#中公开DateTime,如果您的\“ date \”为????-11-23,并且您想确定客户是否超过18岁,这可能会引起混乱-您将如何默认日期,消费者如何知道日期的一部分无效等等。 拥有PartialDate的另一个好处是,它使其他阅读您的代码的人可以快速认识到他们是不正常的,完整的日期,因此不应被视为如此! 编辑 考虑了部分数据的概念后,我决定去Google。我发现有一个关于Joda time的Partial的概念,以及关于该主题的有趣的PDF,这可能对您有用或不有用。     ,有趣的问题... 我喜欢解决方案2B而不是解决方案3C,因为使用3C时,它不会被标准化...当您更新其中一个int时,您也必须更新DateTime否则会不同步。 但是,当您将数据读取到C#端时,我将拥有一个属性,该属性会将所有int汇总为一个字符串,该字符串的格式与解决方案1中的格式相同,以便可以轻松显示。 我很好奇您需要对这些数据执行哪种类型的报告...或者是否只是从数据库中进行存储和检索。     ,        我不必担心如何存储日期,我仍然会将日期存储在datetime字段中,但是,如果知道日期的某些部分是否未填充,则对于日期的每个部分都具有标志无效,因此您的架构将是: DBODate作为日期 DayIsSet为位 MonthIsSet为位 YearIsSet为位。 这样,您仍然可以实现所有有效的日期比较,并且仍然知道正在处理的日期的精度。 (关于日期,我总是默认使用缺少的部分作为该值的最小值:IE月份的默认值是一月,天是第一位,年份是1900左右)。     ,        显然,上面提到的所有解决方案均表示某种折衷。 因此,我建议仔细考虑哪个“级别”是最可能的,并对此进行优化。然后,对其他罕见情况进行适当的异常处理。 我不知道报告是现在还是以后对您来说是一个问题,但是您可能认为这是除DB / C#问题之外的第三个维度。     

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...