请注意,我的问题与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列,例如
1950
, 11
,和 23
- 3)使用年份的INT列,以及完整已知DoB的日期时间列
对于这个问题的C#结尾,我只涉及这两个选项:
- A)使用字符串属性来表示DoB,仅用于视图目的。
- B)为DoB使用自定义(?)结构或类,具有三个可空整数
- 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()
返回?在我看来,这些是最好用课程解决的。
我喜欢在C#中使用3个可空列和3个nullable int的结构。
它确实需要在数据库处理方面做一些努力,但是你可以避免解析字符串,你也可以直接用SQL或年和月查询SQL等等......
无论你做什么,都会变得很混乱。对于这类日期的消费者,我会写一个特殊的类/结构,它封装了它的日期(我可能称之为PartialDate),以便更容易为消费者处理 - 就像Martin Fowler提倡者 钱类。
如果你曝光了 约会时间 直接在C#中,如果您的“日期”为???? - 11-23,并且您想确定客户是否超过18,那么这可能会导致混淆 - 您将如何默认日期,消费者将如何知道日期的一部分是无效的......
拥有PartialDate的额外好处是它允许其他人阅读您的代码,以便快速意识到他们不是正常的,完整的日期,不应该被视为这样!
编辑
考虑到部分数据概念,我决定使用谷歌。我发现有这个概念 部分在Joda时间 和 关于这个主题的有趣PDF,这可能对您有用,也可能对您没用。
有趣的问题......
我喜欢解决方案2B而不是解决方案3C,因为使用3C,它不会被规范化...当您更新其中一个整数时,您还必须更新DateTime或者您将不同步。
但是,当您将数据读入C#端时,我将拥有一个属性,将所有整数汇总到一个字符串格式,就像您在解决方案1中一样,以便可以轻松显示它。
我很好奇你需要对这些数据做什么类型的报告......或者你只是在数据库中存储和检索它。
我不会太担心如何存储日期,我仍然会将日期存储在日期时间字段中,但是,如果知道日期的某些部分是否未填充,我会在日期的每个部分都有标记。无效,因此您的架构将是:
DBODate作为日期
DayIsSet as Bit
MonthIsSet为Bit
YearIsSet as Bit。
这样,您仍然可以实现所有有效的日期比较,并且仍然知道您正在处理的日期的精确度。 (至于日期,我总是将默认部分默认为该值的最小值:IE月份默认为1月,日期为第一,年份为1900或其他)。
显然,上面提到的所有解决方案确实代表了某种妥协。
因此,我建议仔细考虑哪个“级别”最有可能并优化。然后针对其他罕见情况进行适当的异常处理。
我不知道报告现在是否是一个问题,或者可能是稍后,但您可能会认为这是除了DB / C#问题之外的第三个维度。