Поскольку этот Вопрос является продолжением « Правильна ли моя реализация шаблона проектирования типа / подтипа (для взаимоисключающих подклассов)? , что само по себе является продолжением Не знаю, как преобразовать переменную сущность в реляционную таблицу , я бы спросил: что именно вы пытаетесь оптимизировать? Место хранения? Объектная модель? Сложность запроса? Запрос производительности? При оптимизации одного аспекта по сравнению с другим существуют компромиссы, поскольку нельзя оптимизировать все аспекты одновременно.
Я полностью согласен с мнениями Ремуса относительно:
- У каждого подхода есть свои плюсы и минусы (то есть постоянный фактор «зависит»), и
- Первым приоритетом является эффективность модели данных (неэффективная модель данных не может быть исправлена чистым и / или эффективным кодом приложения)
Тем не менее, перед вами стоит выбор между следующим, упорядоченным в порядке наименьшей нормализации к большей части нормализации:
- продвижение собственности
E
в таблицу базового типа
- держать его в нескольких таблицах подтипов
- полностью нормализуется
E
до новой промежуточной таблицы подклассов на том же уровне C
, что A
и B
непосредственно будет подклассами ( @ ответ MDCCL )
Давайте посмотрим на каждый вариант:
Переместить свойство E
в таблицу базового типа
PROs
- Снижение сложности запросов для запросов, которые нуждаются,
E
но не требуют X
, Y
или Z
.
- Потенциально более эффективен для запросов, которые нуждаются,
E
но не нужны X
, Y
или Z
(особенно в совокупности запросов) из-за отсутствия JOIN.
- Потенциал для создания индекса
(D, E)
(и, если да, потенциально фильтрованного индекса, (D, E)
где EntityType <> C
, если такое условие разрешено)
ПРОТИВ
- Не могу отметить
E
какNOT NULL
- Требуется дополнительная
CHECK CONSTRAINT
таблица базового типа, чтобы убедиться, что E IS NULL
когда EntityType = C
(хотя это не большая проблема)
- Необходимо информировать пользователей модели данных о том, почему это
E
необходимо NULL
и даже следует полностью игнорировать, когда EntityType = C
.
- Немного менее эффективно, когда
E
используется тип с фиксированной длиной, и большая часть строк предназначена для типа EntityType C
(т.е. не использует, E
следовательно, он есть NULL
) и не использует ни SPARSE
параметр в столбце, ни сжатие данных в кластеризованном индексе.
- Потенциально менее эффективен для запросов, которые не нужны,
E
поскольку присутствие E
в таблице базового типа увеличит размер каждой строки, что, в свою очередь, уменьшит количество строк, которые могут поместиться на странице данных. Но это сильно зависит от точного типа данных E
, FILLFACTOR, количества строк в таблице базового типа и т. Д.
Сохранить свойство E
в каждой таблице подтипа
PROs
- Более чистая модель данных (т. Е. Не нужно беспокоиться о том, чтобы объяснить другим, почему столбец
E
в таблице базового типа не следует использовать, потому что «его действительно нет»)
- Вероятно, более близко напоминает объект-модель
- Может пометить столбец как
NOT NULL
обязательное свойство объекта
- Нет необходимости в дополнительных
CHECK CONSTRAINT
таблицах базового типа, чтобы гарантировать, что E IS NULL
когда EntityType = C
(хотя это не огромный выигрыш)
ПРОТИВ
- Требуется ПРИСОЕДИНИТЬСЯ к подтипу Table (s), чтобы получить это свойство
- Потенциально немного менее эффективно при необходимости
E
, благодаря JOIN, в зависимости от того, сколько строк A
+ у B
вас, а не сколько строк C
есть.
- Немного сложнее / сложнее для операций, которые имеют дело только с сущностями
A
и B
(и не C
) как с одним и тем же «типом». Конечно, вы можете абстрагировать это через представление, которое выполняет UNION ALL
между a SELECT
из таблиц JOINed для A
и другой SELECT
из таблиц JOINed для B
. Это позволит снизить сложность запросов SELECT , но не так полезно для INSERT
и UPDATE
запросов.
- В зависимости от конкретных запросов и от того, как часто они выполняются, это может быть потенциальной неэффективностью в тех случаях, когда наличие индекса
(D, E)
действительно поможет одному или нескольким часто используемым запросам, поскольку они не могут быть проиндексированы вместе.
Нормализовать E
до промежуточной таблицы между базовым классом и A
&B
(Обратите внимание, что мне нравится ответ @ MDCCL как жизнеспособной альтернативы, в зависимости от обстоятельств. Следующее не предназначено для строгой критики этого подхода, а как средство добавления некоторой перспективы - моей, конечно, - путем оценки это в том же контексте, что и два варианта, которые я уже предложил. Это облегчит прояснение того, что я вижу как относительную разницу между полной нормализацией и текущим подходом частичной нормализации.)
PROs
- модель данных полностью нормализована (в этом не может быть ничего плохого, учитывая, что именно для этого предназначены СУБД)
- уменьшенная сложность запросов для запросов, нуждающихся
A
и B
, но не C
(то есть нет необходимости в двух запросах, соединенных через UNION ALL
)
ПРОТИВ
- немного больше занимаемого места (
Bar
таблица дублирует идентификатор, и есть новый столбец BarTypeCode
) [незначительно, но кое-что нужно знать]
- Небольшое увеличение сложности запроса, так как требуется дополнительное,
JOIN
чтобы добраться до A
илиB
- увеличенная площадь поверхности для блокировки, в основном включенная
INSERT
( DELETE
может обрабатываться неявно через пометку внешних ключей как ON CASCADE DELETE
), поскольку транзакция будет оставаться открытой на таблице базового класса немного дольше (т. е. Foo
) [незначительно, но кое-что следует знать]
нет непосредственного знания фактического типа - A
или B
- в Таблице базового класса Foo
; она знает только тип , Br
который может быть A
или B
:
Это означает, что если вам нужно выполнить запросы к общей базовой информации, но нужно либо классифицировать по типу сущности, либо отфильтровать один или несколько типов сущностей, то в таблице базового класса недостаточно информации, и в этом случае вам нужно стол. Это также снизит эффективность индексации столбца.LEFT JOIN
Bar
FooTypeCode
нет единого подхода к взаимодействию с A
& B
vs C
:
Это означает, что если каждая сущность имеет непосредственное отношение к таблице базового класса, так что существует только одно соединение, чтобы получить полную сущность, то каждый может быстрее и проще создать знакомство с точки зрения работы с моделью данных. Там будет общий подход к запросам / хранимым процедурам, что позволит им быстрее разрабатывать и с меньшей вероятностью иметь ошибки. Последовательный подход также позволяет быстрее и проще добавлять новые подтипы в будущем.
потенциально менее адаптируемый к бизнес-правилам, которые меняются со временем:
Это означает, что вещи всегда меняются, и довольно легко перейти E
к Таблице базового класса, если она станет общей для всех подтипов. Также достаточно легко перенести общее свойство в подтипы, если изменения в природе сущностей делают это целесообразным изменением. Достаточно просто разбить подтип на два подтипа (просто создать другое SubTypeID
значение) или объединить два или более подтипа в один. И наоборот, что, если E
впоследствии оно станет общим свойством всех подтипов? Тогда промежуточный слой Bar
таблицы будет бессмысленным, а сложность не будет того стоить. Конечно, невозможно знать, произойдет ли такое изменение через 5 или даже 10 лет, поэтому Bar
таблица не обязательнои даже весьма вероятно, что это плохая идея (именно поэтому я сказал « потенциально менее адаптируемый»). Это просто моменты для рассмотрения; это игра в любом направлении.
потенциально неуместная группировка:
Значение, только потому , что E
собственность поделена между типами сущностей A
и B
не означает , что A
и B
должны быть сгруппированы вместе. То, что вещи «выглядят» одинаково (т.е. имеют одинаковые свойства), не означает, что они одинаковы.
Резюме
Точно так же, как принятие решения о том, когда / когда проводить денормализацию, наилучший подход к этой конкретной ситуации зависит от рассмотрения следующих аспектов использования модели данных и обеспечения того, чтобы выгоды перевешивали затраты:
- сколько строк у вас будет для каждого EntityType (посмотрите, по крайней мере, на 5 лет вперед, предполагая рост выше среднего)
- Сколько ГБ будет каждая из этих таблиц (базовый тип и подтипы) будет через 5 лет?
- какой конкретный тип данных является свойством
E
- это только одно свойство или есть несколько или даже несколько свойств
- какие запросы вам понадобятся
E
и как часто они будут выполняться
- какие запросы вам понадобятся, которые не нужны
E
и как часто они будут выполняться
Я думаю, что по умолчанию склоняюсь к хранению E
в отдельных таблицах подтипов, потому что это, по крайней мере, «чище». Я хотел бы рассмотреть переход E
к таблице базового типа IF: большинство строк были не для EntityType of C
; и количество рядов было по крайней мере в миллионах; и я выполняю запросы чаще, чем не выполняемые, E
и / или запросы, которые выиграют от индекса, (D, E)
либо выполняются очень часто, и / или требуют достаточно системных ресурсов, так что наличие индекса уменьшает общее использование ресурсов или, по крайней мере, предотвращает скачки потребления ресурсов превышают допустимые уровни или длятся достаточно долго, чтобы вызвать чрезмерную блокировку и / или увеличение взаимоблокировок.
ОБНОВИТЬ
ОП прокомментировал этот ответ, что:
Мои работодатели изменили бизнес-логику, полностью исключив E!
Это изменение особенно важно, потому что именно то, что я предсказал, может произойти в подразделе «CON» в разделе «Нормализовать E
до промежуточной таблицы между базовым классом и разделом A
& B
» выше (6-й пункт). Конкретная проблема заключается в том, насколько легко / сложно провести рефакторинг модели данных, когда такие изменения происходят (и они всегда происходят). Некоторые утверждают, что любая модель данных может быть реорганизована / изменена, поэтому начните с идеала. Но хотя на техническом уровне верно то, что все может быть реорганизовано, реальность ситуации зависит от масштаба.
Ресурсы не бесконечны, не только процессор / диск / оперативная память, но и ресурсы для разработки: время и деньги. Компании постоянно устанавливают приоритеты для проектов, потому что эти ресурсы очень ограничены. И довольно часто (по крайней мере, по моему опыту) проекты, направленные на повышение эффективности (даже как производительности системы, так и ускорения разработки / уменьшения количества ошибок), имеют приоритет перед проектами, которые повышают функциональность. В то время как это разочаровывает нас, технических специалистов, потому что мы понимаем, каковы долгосрочные выгоды от проектов рефакторинга, просто характер бизнеса заключается в том, что менее техническим специалистам легче увидеть прямую связь между новой функциональностью и новыми доход. Это сводится к следующему: «мы вернемся, чтобы исправить это позже» ==
Имея это в виду, если размер данных достаточно мал, чтобы можно было вносить изменения по самому запросу, и / или у вас есть окно обслуживания, которое достаточно длинное, чтобы не только вносить изменения, но и откатываться, если что-то происходит неверно, тогда нормализация E
до промежуточной таблицы между таблицей базового класса и таблицами A
& B
подкласса может сработать (хотя это все равно оставляет вас без непосредственных знаний о конкретном типе ( A
илиB
) в таблице базового класса). НО, если у вас есть сотни миллионов строк в этих таблицах и невероятное количество кода, ссылающегося на таблицы (код, который должен быть проверен при внесении изменений), то обычно стоит быть более прагматичным, чем идеалистическим. И это среда, с которой мне приходилось сталкиваться годами: 987 миллионов строк и 615 ГБ в таблице базового класса, распределенных по 18 серверам. И так много кода попало в эти таблицы (таблицы базового класса и подкласса), что имело место большое сопротивление - в основном со стороны руководства, а иногда и всей команды - к внесению каких-либо изменений в связи с объемом разработки и Ресурсы QA, которые должны быть выделены.
Итак, еще раз, «лучший» подход может быть определен только от ситуации к ситуации: вам нужно знать свою систему (то есть, сколько данных и как все таблицы и код связаны), как выполнить рефакторинг, и люди с которым вы работаете (ваша команда и, возможно, руководство - можете ли вы получить их взнос для такого проекта?). Есть некоторые изменения, которые я упоминал и планировал в течение 1-2 лет, и мне потребовалось несколько спринтов / релизов, чтобы реализовать 85% из них. Но если у вас есть только <1 миллион строк и не много кода, привязанного к этим таблицам, то вы, вероятно, сможете начать с более идеальной / «чистой» стороны вещей.
Просто помните, какой бы путь вы ни выбрали, обратите внимание на то, как эта модель будет работать как минимум в течение следующих 2 лет (если это возможно). Обращайте внимание на то, что сработало и что вызвало боль, даже если в то время это казалось величайшей идеей (а это значит, что вы также должны позволить себе принять провал - мы все это делаем - чтобы вы могли честно оценить болевые точки ). И обратите внимание на то, почему определенные решения сработали или не сработали, чтобы вы могли принимать решения, которые с большей вероятностью будут «лучше» в следующий раз :-).
Согласно моей интерпретации ваших спецификаций, вы хотите найти метод для реализации двух разных (но связанных ) структур супертип-подтип .
Чтобы раскрыть подход для достижения вышеупомянутой задачи, я собираюсь добавить к рассматриваемому сценарию два классических гипотетических типа сущностей, называемых
Foo
иBar
, которые я подробно опишу ниже.Бизнес правила
Вот несколько утверждений, которые помогут мне создать логическую модель:
A Foo is either one Bar or one C
A Foo is categorized by one FooType
A Bar is either one A or one C
A Bar is classified by one BarType
Логическая модель
И затем, результирующая логическая модель IDEF1X [1] показана на рисунке 1 (и вы также можете загрузить ее из Dropbox в формате PDF ):
Дополнение Foo and Bar
Я не добавлял
Foo
иBar
чтобы модель выглядела лучше, но чтобы она была более выразительной. Я считаю, что они важны из-за следующего:Как
A
иB
разделить названный атрибутE
, эта особенность предполагает, что они являются типами субъективности отдельного (но связанного) вида концепции , события , личности , измерения и т. Д.,Bar
Которые я представлял с помощью типа превосходства, который, в свою очередь, является тип подчиненностиFoo
, который содержитD
атрибут в верхней части иерархии.Поскольку
C
только один атрибут разделяет с остальными обсуждаемыми типами сущностей, т. Е.D
Этот аспект намекает на то, что это тип сущности другого типа концепции , события , лица , измерения и т. Д., Поэтому я изобразил это обстоятельство в силуFoo
супер тип объекта.Тем не менее, это всего лишь предположения, и поскольку реляционная база данных предназначена для точного отражения семантики определенного бизнес-контекста , вам необходимо идентифицировать и классифицировать все вещи, представляющие интерес в вашей конкретной области, чтобы вы могли, точно, захватить больше смысла ,
Важные факторы на этапе проектирования
Весьма полезно осознавать тот факт, что, исключая всю терминологию, исключительный кластер супертип-подтип является обычным отношением. Опишем ситуацию следующим образом:
Таким образом, в этих случаях есть соответствие (или количество элементов) один к одному (1: 1).
Как вы знаете из предыдущих постов, атрибут дискриминатора (столбец, если он реализован) играет первостепенную роль при создании ассоциации такого рода, поскольку он указывает на правильный экземпляр подтипа, с которым связан супертип . Миграция первичного ключа из (I) супертипе к (б) подтипы также имеет первостепенное значение.
Бетонная конструкция DDL
А затем я написал структуру DDL, основанную на логической модели, представленной выше:
С этой структурой вы избегаете хранения меток NULL в ваших базовых таблицах (или отношениях ), что внесет неоднозначность в вашу базу данных.
Целостность, последовательность и другие соображения
После того, как вы реализуете свою базу данных, вы должны убедиться, что (a) каждая исключительная строка супертипа всегда дополняется соответствующим аналогом подтипа и, в свою очередь, гарантировать, что (b) такая строка подтипа совместима со значением, содержащимся в столбце дискриминатора супертипа. , Поэтому довольно удобно использовать ACID
TRANSACTIONS
, чтобы убедиться, что эти условия выполняются в вашей базе данных.Вы не должны отказываться от логической обоснованности, самовыражения и точности вашей базы данных, это те аспекты, которые определенно делают вашу базу данных более надежной.
Два ранее опубликованных ответа уже включают соответствующие пункты, которые, безусловно, стоит учитывать при проектировании, создании и управлении вашей базой данных и ее прикладными программами.
Получение данных с помощью определений VIEW
Вы можете настроить некоторые представления, которые объединяют столбцы разных групп подтипов супертипов , так что вы можете извлекать данные под рукой, например, не каждый раз записывая необходимые предложения JOIN. Таким образом, вы можете легко ВЫБРАТЬ непосредственно ИЗ ВИДА ( производного отношения или таблицы ), представляющего интерес.
Как видите, «Тед» Кодд был, несомненно, гением. Инструменты, которые он завещал, довольно сильны и элегантны, и, конечно же, хорошо интегрированы друг с другом.
Связанные ресурсы
Если вы хотите проанализировать какую-то обширную базу данных, которая включает отношения супертип-подтип, вы найдете полезными необычные ответы, предложенные @PerformanceDBA на следующие вопросы переполнения стека:
Историческая / проверяемая база данных .
[O] ne таблица или много для множества разных, но взаимодействующих событий? , который содержит вложенные подтипы.
Заметка
1. Определение интеграции для информационного моделирования ( IDEF1X ) - это очень рекомендуемый метод моделирования данных, который был установлен в качестве стандарта в декабре 1993 года Национальным институтом стандартов и технологий США ( NIST ). Он основывается на (а) раннем теоретическом материале, написанном д-ром Э. Ф. Коддом; на (б) в сущности-связи с учетом данных, разработанного д - ром П. Ченом ; а также о (c) методике проектирования логических баз данных, созданной Робертом Г. Брауном. Стоит отметить, что IDEF1X был формализован с помощью логики первого порядка.
источник
E
вообще убрав ! Причина для того, чтобы принять ответ пользователя srutzky , состоит в том, что он дает хорошие моменты, которые помогают мне принять решение о выборе наиболее эффективного маршрута. Если бы не это, я бы принял ваш ответ. Я проголосовал за ваш ответ ранее. Еще раз спасибо!