Как спроектировать дату рождения в БД и ORM для смеси известных и неизвестных частей даты - PullRequest
11 голосов
/ 22 июня 2011

Обратите внимание, мой вопрос оказывается похожим на вопрос SO 1668172 .


Это вопрос дизайна, который наверняка уже всплывал для других, но покаЯ не мог найти ответ, который соответствует моей ситуации.Я хочу записать дату рождения в своем приложении с несколькими «уровнями» информации:

  • NULL значение, т. Е. DoB неизвестно
  • 1950-??-?? ТолькоЗначение года в DoB известно, дата / месяц не
  • ????-11-23 Только месяц, день или их комбинация, но без года
  • 1950-11-23 Полный DoB известен

Технологии, которые я использую для своего приложения, следующие:

  • Asp.NET 4 (C #), возможно, с MVC
  • Некоторые ORMрешение, вероятно, Linq-to-sql или
  • MSSQL Server 2008 от NHibernate, сначала просто Express Edition

Возможности для бита SQL, который до сих пор приходил мне в голову:

  • 1) Используйте один столбец varchar, допускающий значение NULL, например 1950-11-23, и замените неизвестные на «X», например, XXXX-11-23 или 1950-XX-XX
  • 2) Используйте три столбца Nullable int, например 1950,11 и 23
  • 3) Использовать столбец INT для года, плюс столбец datetime для полностью известных DoB

Для завершения этой проблемы на C # я простодобрались до этих двух вариантов:

  • A) Использовать строковое свойство для представления DoB, конвертировать только для целей просмотра.
  • B) Использовать пользовательскую (?) структуру или класс для DoBс тремя обнуляемыми целыми числами
  • C) Используйте обнуляемое DateTime вместе с обнуляемым целым числом для года

Решения, кажется, образуют согласованные пары в 1A , 2B или 3C .Конечно, 1А не очень хорошее решение, но оно устанавливает базовый уровень.

Любые советы и ссылки высоко ценятся.Ну, если они связаны, во всяком случае:)


Редактировать, об ответах : я отметил один ответ как принятый, потому что я думаю, что он будет работать для меня.Однако стоит посмотреть и на другие ответы, если вы наткнулись здесь на тот же вопрос.

Ответы [ 6 ]

3 голосов
/ 22 июня 2011

Сторона 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()? На мой взгляд, эти вещи лучше всего решать с помощью класса.

2 голосов
/ 22 июня 2011

Что бы вы ни делали, это будет беспорядочно.Для потребителей такого рода дат я бы написал специальный класс / структуру, которая инкапсулирует, какого рода это дата (я бы назвал ее чем-то вроде PartialDate), чтобы было проще иметь дело с потребителями - во многом как Мартин Фаулер.выступает за класс денег .

Если вы выставите DateTime непосредственно в C #, это может привести к путанице, если у вас есть «дата» ???? -11-23, и вы хотели определить, например, был ли клиент старше 18 лет - как бы вы указали дату по умолчанию, как потребитель узнал бы, что часть даты была недействительной и т. Д ...

Дополнительная выгода отналичие PartialDate позволит другим людям, читающим ваш код, быстро понять, что они ненормальные, полные даты и не должны рассматриваться как таковые!

Редактировать

Подумав еще о концепции Частичных данных, я решил обратиться к Google.Я обнаружил, что существует концепция Частичное по времени Joda и интересный PDF-файл по теме , который может или не может быть полезен для вас.

2 голосов
/ 22 июня 2011

Мне нравится идея о 3 int nullable int столбцах и структура 3 nullable int в C #.

это требует определенных усилий при обработке db, но вы можете избежать разбора строк и выполнить запрос с SQLнепосредственно по году или году и месяцу и т. д. ...

1 голос
/ 22 июня 2011

Очевидно, что все решения, упомянутые выше, представляют собой своего рода компромисс.

Поэтому я бы рекомендовал тщательно подумать, какой из «уровней» является наиболее вероятным, и оптимизировать его для этого.Затем перейдите к правильной обработке исключений для других редких случаев.

Я не знаю, является ли создание отчетов проблемой для вас сейчас или может быть позже, но вы можете рассмотреть это как третье измерение, кроме DB /Проблемы C #.

1 голос
/ 22 июня 2011

Я бы не сильно беспокоился о том, как хранить дату, я все равно сохранял бы дату в поле даты и времени, НО, если бы я знал, что какая-то часть даты не была заполнена, у меня были бы флаги для каждого раздела дата недействительна, поэтому ваша схема будет:

DBODate as Date DayIsSet as Bit MonthIsSet as Bit YearIsSet as Bit.

Таким образом, вы все еще можете реализовать все действительные сравнения дат и все еще знать точность даты, над которой вы работаете. (Что касается даты, я бы всегда по умолчанию использовал недостающую часть как минимум этого значения: IE по умолчанию месяц - январь, день - первый, год - 1900 или что-то в этом роде).

1 голос
/ 22 июня 2011

Интересная проблема ...

Мне нравится решение 2B, а не решение 3C, потому что с 3C оно не нормализуется ... когда вы обновляете одно из целых чисел, вам также необходимо обновить DateTime, иначе вы не синхронизированы. 1003 *

Однако, когда вы читаете данные в конец C #, у меня будет свойство, которое сворачивает все целые числа в строку, отформатированную так, как у вас в решении 1, чтобы ее можно было легко отобразить.

Мне любопытно, какой тип отчетов вам нужно будет использовать для этих данных ... или если вы просто будете хранить и извлекать их из базы данных.

...