Я думаю о том, как представить сложную структуру в базе данных SQL Server.
Рассмотрим приложение, в котором необходимо хранить сведения о семействе объектов, которые имеют одни и те же атрибуты, но имеют множество других, не являющихся общими. Например, коммерческий страховой пакет может включать покрытие ответственности, транспортных средств, имущества и возмещения в рамках одной и той же записи полиса.
Это легко реализовать в C # и т. Д., Так как вы можете создать Политику с коллекцией Разделов, где Раздел наследуется, как требуется для различных типов покрытия. Тем не менее, реляционные базы данных, кажется, не позволяют это легко.
Я вижу, что есть два основных варианта:
Создайте таблицу политик, затем таблицу разделов со всеми необходимыми полями для всех возможных вариантов, большинство из которых будут нулевыми.
Создайте таблицу правил и многочисленные таблицы разделов, по одному для каждого вида покрытия.
Обе эти альтернативы кажутся неудовлетворительными, тем более что необходимо писать запросы по всем разделам, что может включать многочисленные объединения или многочисленные нулевые проверки.
Какова лучшая практика для этого сценария?
Ответы:
@ Билл Карвин описывает три модели наследования в своей книге « Антипаттерны SQL» , предлагая решения для антипаттерна « Значение сущности SQL» . Это краткий обзор:
Наследование в одной таблице (или Наследование таблиц в иерархии):
Использование одной таблицы, как в первом варианте, является, вероятно, самым простым дизайном. Как вы упомянули, многим атрибутам, относящимся к подтипу, необходимо присвоить
NULL
значение в строках, где эти атрибуты не применяются. В этой модели у вас будет одна таблица политик, которая будет выглядеть примерно так:Простота дизайна - это плюс, но основные проблемы с этим подходом заключаются в следующем:
Когда дело доходит до добавления новых подтипов, вам придется изменить таблицу, чтобы разместить атрибуты, которые описывают эти новые объекты. Это может быстро стать проблематичным, если у вас много подтипов или если вы планируете добавлять подтипы на регулярной основе.
База данных не сможет принудительно определить, какие атрибуты применяются, а какие нет, поскольку нет метаданных, которые бы определяли, какие атрибуты принадлежат каким подтипам.
Вы также не можете применять
NOT NULL
атрибуты подтипа, которые должны быть обязательными. Вам придется справиться с этим в вашем приложении, что в целом не идеально.Наследование бетонного стола:
Другой подход к решению проблемы наследования заключается в создании новой таблицы для каждого подтипа, повторяя все общие атрибуты в каждой таблице. Например:
Этот дизайн в основном решит проблемы, определенные для метода единой таблицы:
Обязательные атрибуты теперь могут быть применены с помощью
NOT NULL
.Добавление нового подтипа требует добавления новой таблицы вместо добавления столбцов к существующей.
Также нет риска, что для определенного подтипа будет установлен неподходящий атрибут, такой как
vehicle_reg_no
поле для политики свойств.type
Атрибут не нужен, как в методе с одной таблицей. Тип теперь определяется метаданными: имя таблицы.Однако эта модель также имеет несколько недостатков:
Общие атрибуты смешиваются с определенными атрибутами подтипа, и нет простого способа их идентифицировать. База данных тоже не будет знать.
При определении таблиц вам придется повторять общие атрибуты для каждой таблицы подтипов. Это определенно не СУХОЙ .
Поиск всех политик, независимо от подтипа, становится трудным и потребует кучу
UNION
s.Вот как вам нужно будет запрашивать все политики независимо от типа:
Обратите внимание, что добавление новых подтипов потребовало бы изменения вышеуказанного запроса с дополнительным
UNION ALL
для каждого подтипа. Это может легко привести к ошибкам в вашем приложении, если эта операция будет забыта.Наследование таблиц классов (или таблица наследования типов):
Это решение, которое @David упоминает в другом ответе . Вы создаете единую таблицу для своего базового класса, которая включает в себя все общие атрибуты. Затем вы должны создать конкретные таблицы для каждого подтипа, первичный ключ которого также служит внешним ключом для базовой таблицы. Пример:
Это решение решает проблемы, выявленные в двух других проектах:
Обязательные атрибуты могут быть применены с
NOT NULL
.Добавление нового подтипа требует добавления новой таблицы вместо добавления столбцов к существующей.
Нет риска, что для определенного подтипа установлен неподходящий атрибут.
Нет необходимости в
type
атрибуте.Теперь общие атрибуты больше не смешиваются с определенными атрибутами подтипа.
Мы можем остаться сухими, наконец. При создании таблиц нет необходимости повторять общие атрибуты для каждой таблицы подтипов.
Управление автоинкрементом
id
для политик становится проще, потому что это может обрабатываться базовой таблицей, а не каждой таблицей подтипов, генерирующей их независимо.Поиск всех политик, независимо от подтипа, теперь становится очень простым: нет
UNION
необходимости - просто аSELECT * FROM policies
.Я считаю подход таблицы классов наиболее подходящим в большинстве ситуаций.
Названия этих трех моделей взяты из книги Мартина Фаулера « Шаблоны архитектуры корпоративных приложений» .
источник
Третий вариант - создать таблицу «Policy», а затем таблицу «SectionsMain», в которой хранятся все поля, общие для всех типов разделов. Затем создайте другие таблицы для каждого типа раздела, которые содержат только поля, которые не являются общими.
Выбор наилучшего зависит в основном от того, сколько полей у вас есть и как вы хотите написать свой SQL. Они все будут работать. Если у вас есть только несколько полей, то я бы, вероятно, пошел с # 1. С «множеством» полей я бы склонялся к # 2 или # 3.
источник
Учитывая предоставленную информацию, я бы смоделировал базу данных следующим образом:
ПОЛИТИКА
ОБЯЗАННОСТИ
СВОЙСТВА
... и так далее, потому что я ожидаю, что будут разные атрибуты, связанные с каждым разделом политики. В противном случае, не может быть один
SECTIONS
стол и в дополнение кpolicy_id
, там был быsection_type_code
...В любом случае, это позволит вам поддерживать дополнительные разделы для каждой политики ...
Я не понимаю, что вы считаете неудовлетворительным в этом подходе - это то, как вы храните данные, сохраняя ссылочную целостность и не дублируя данные. Термин "нормализован" ...
Поскольку SQL основан на SET, он довольно чужд понятиям процедурного / OO-программирования и требует, чтобы код переходил из одной области в другую. ORM часто рассматриваются, но они плохо работают в больших объемах и сложных системах.
источник
Кроме того, в решении Daniel Vassallo, если вы используете SQL Server 2016+, есть другое решение, которое я использовал в некоторых случаях без значительной потери производительности.
Вы можете создать только таблицу только с общим полем и добавить один столбец со строкой JSON, которая содержит все специфичные для подтипа поля.
Я протестировал этот дизайн для управления наследованием, и я очень рад за гибкость, которую я могу использовать в соответствующем приложении.
источник
Другой способ сделать это, используя
INHERITS
компонент. Например:Таким образом, можно определить наследование между таблицами.
источник
INHERITS
помимо PostgreSQL ? MySQL например?Я склоняюсь к методу № 1 (унифицированная таблица разделов), чтобы эффективно извлекать целые политики со всеми их разделами (что, как я полагаю, будет делать ваша система).
Кроме того, я не знаю, какую версию SQL Server вы используете, но в 2008+ Sparse Columns помогают оптимизировать производительность в ситуациях, когда многие значения в столбце будут равны NULL.
В конечном итоге вам придется решить, насколько «похожи» разделы политики. Если они существенно не различаются, я думаю, что более нормализованное решение может быть более сложным, чем оно стоит ... но только вы можете сделать этот вызов. :)
источник
В качестве альтернативы рассмотрите возможность использования баз данных документов (таких как MongoDB), которые изначально поддерживают богатые структуры данных и вложенность.
источник
Проверьте ответ, который я дал здесь
Свободное сопоставление NHibernate один-к-одному с синтетическими ключами
источник