Супертип / підтип
Як щодо того, щоб вивчити шаблон супертипу / підтипу? Загальні стовпці містяться в батьківській таблиці. Кожен окремий тип має свою власну таблицю з ідентифікатором батьківського власного ПК і містить унікальні стовпці, не спільні для всіх підтипів. Ви можете включити стовпчик типу в батьківські та дочірні таблиці, щоб переконатися, що кожен пристрій не може бути більше ніж один підтип. Зробіть ФК між дітьми та батьком на (ItemID, ItemTypeID). Ви можете використовувати FKs для таблиць супертипу або підтипу, щоб підтримувати потрібну цілісність в іншому місці. Наприклад, якщо дозволено ItemID будь-якого типу, створіть FK для батьківської таблиці. Якщо тільки на SubItemType1 можна посилатися, створіть FK до цієї таблиці. Я б не залишив TypeID від посилань на таблиці.
Іменування
Що стосується іменування, у вас є два варіанти, як я бачу (оскільки третій вибір просто "ІД" є на мою думку сильним антидіаграмою). Або викличте ключ підтипу ItemID таким, як він є у батьківській таблиці, або назвіть його ім'я підтипу, наприклад DoohickeyID. Після деякої думки та досвіду з цим я виступаю за те, щоб називати це DoohickeyID. Причиною цього є те, що, хоч може виникнути плутанина щодо таблиці підтипу, дійсно в маскуванні, що містить елементи (а не Doohickeys), це невеликий мінус порівняно з тим, коли ви створюєте FK до таблиці Doohickey, а назви стовпців не матч!
Для EAV чи не для EAV - Мій досвід роботи з базою даних EAV
Якщо EAV - це те, що ви справді повинні робити, то це вам потрібно зробити. Але що робити, якщо це було не те, що потрібно було робити?
Я створив базу даних EAV, яка використовується у бізнесі. Слава Богу, набір даних невеликий (хоча існує кілька десятків типів елементів), тому продуктивність непогана. Але було б погано, якби в базі даних було більше декількох тисяч елементів! Крім того, таблиці настільки ТРЕБОВІ для запиту. Цей досвід змусив мене по-справжньому прагнути уникати баз даних EAV в майбутньому, якщо це взагалі можливо.
Тепер у своїй базі даних я створив збережену процедуру, яка автоматично створює PIVOTed представлення для кожного підтипу, що існує. Я можу просто запит від AutoDoohickey. Мої метадані про підтипи містять стовпчик "ShortName", що містить об'єкт-ім'я, придатне для використання в іменах перегляду. Я навіть зробив перегляди оновленими! На жаль, ви не можете оновити їх під час з'єднання, але ви МОЖЕТЕ вставити до них уже існуючий рядок, який буде перетворений на UPDATE. На жаль, ви не можете оновити лише кілька стовпців, оскільки немає можливості вказати VIEW, які стовпці ви хочете оновити за допомогою процесу перетворення INSERT-UPDATE: значення NULL виглядає як "оновити цей стовпець до NULL", навіть якщо Ви хотіли вказати "Не оновлювати цю колонку взагалі".
Незважаючи на все це оздоблення, щоб зробити базу даних EAV простішою у використанні, я все одно не використовую ці погляди в найбільш звичайних запитах, тому що це ПОЗНАЧЕНО. Умови запиту не є присудком, який підштовхується до Value
таблиці, тому перед фільтрацією він повинен створити проміжний набір результатів для всіх елементів типу цього перегляду. Ой. Тож у мене є багато, багато запитів з багатьма, багатьма приєднаннями, кожен з яких виходить, щоб отримати інше значення тощо. Вони працюють досить добре, але ой! Ось приклад. SP, який створює це (і його тригер оновлення), є одним з гігантських звірів, і я пишаюся цим, але це не те, що ви хочете коли-небудь намагатися підтримувати.
CREATE VIEW [dbo].[AutoModule]
AS
--This view is automatically generated by the stored procedure AutoViewCreate
SELECT
ElementID,
ElementTypeID,
Convert(nvarchar(160), [3]) [FullName],
Convert(nvarchar(1024), [435]) [Descr],
Convert(nvarchar(255), [439]) [Comment],
Convert(bit, [438]) [MissionCritical],
Convert(int, [464]) [SupportGroup],
Convert(int, [461]) [SupportHours],
Convert(nvarchar(40), [4]) [Ver],
Convert(bit, [28744]) [UsesJava],
Convert(nvarchar(256), [28745]) [JavaVersions],
Convert(bit, [28746]) [UsesIE],
Convert(nvarchar(256), [28747]) [IEVersions],
Convert(bit, [28748]) [UsesAcrobat],
Convert(nvarchar(256), [28749]) [AcrobatVersions],
Convert(bit, [28794]) [UsesDotNet],
Convert(nvarchar(256), [28795]) [DotNetVersions],
Convert(bit, [512]) [WebApplication],
Convert(nvarchar(10), [433]) [IFAbbrev],
Convert(int, [437]) [DataID],
Convert(nvarchar(1000), [463]) [Notes],
Convert(nvarchar(512), [523]) [DataDescription],
Convert(nvarchar(256), [27991]) [SpecialNote],
Convert(bit, [28932]) [Inactive],
Convert(int, [29992]) [PatchTestedBy]
FROM (
SELECT
E.ElementID + 0 ElementID,
E.ElementTypeID,
V.AttrID,
V.Value
FROM
dbo.Element E
LEFT JOIN dbo.Value V ON E.ElementID = V.ElementID
WHERE
EXISTS (
SELECT *
FROM dbo.LayoutUsage L
WHERE
E.ElementTypeID = L.ElementTypeID
AND L.AttrLayoutID = 7
)
) X
PIVOT (
Max(Value)
FOR AttrID IN ([3], [435], [439], [438], [464], [461], [4], [28744], [28745], [28746], [28747], [28748], [28749], [28794], [28795], [512], [433], [437], [463], [523], [27991], [28932], [29992])
) P;
Ось ще один тип автоматично сформованого перегляду, створеного іншою збереженою процедурою із спеціальних метаданих, що допомагає знаходити зв’язки між елементами, які можуть мати між собою кілька шляхів (зокрема: Модуль-> Сервер, Модуль-> Кластер-> Сервер, Модуль-> СУБД- > Сервер, Модуль-> СУБД-> Кластер-> Сервер):
CREATE VIEW [dbo].[Link_Module_Server]
AS
-- This view is automatically generated by the stored procedure LinkViewCreate
SELECT
ModuleID = A.ElementID,
ServerID = B.ElementID
FROM
Element A
INNER JOIN Element B
ON EXISTS (
SELECT *
FROM
dbo.Element R1
WHERE
A.ElementID = R1.ElementID1
AND B.ElementID = R1.ElementID2
AND R1.ElementTypeID = 38
) OR EXISTS (
SELECT *
FROM
dbo.Element R1
INNER JOIN dbo.Element R2 ON R1.ElementID2 = R2.ElementID1
WHERE
A.ElementID = R1.ElementID1
AND R1.ElementTypeID = 40
AND B.ElementID = R2.ElementID2
AND R2.ElementTypeID = 38
) OR EXISTS (
SELECT *
FROM
dbo.Element R1
INNER JOIN dbo.Element R2 ON R1.ElementID2 = R2.ElementID1
WHERE
A.ElementID = R1.ElementID1
AND R1.ElementTypeID = 38
AND B.ElementID = R2.ElementID2
AND R2.ElementTypeID = 3122
) OR EXISTS (
SELECT *
FROM
dbo.Element R1
INNER JOIN dbo.Element R2 ON R1.ElementID2 = R2.ElementID1
INNER JOIN dbo.Element C2 ON R2.ElementID2 = C2.ElementID
INNER JOIN dbo.Element R3 ON R2.ElementID2 = R3.ElementID1
WHERE
A.ElementID = R1.ElementID1
AND R1.ElementTypeID = 40
AND C2.ElementTypeID = 3080
AND R2.ElementTypeID = 38
AND B.ElementID = R3.ElementID2
AND R3.ElementTypeID = 3122
)
WHERE
A.ElementTypeID = 9
AND B.ElementTypeID = 17
Гібридний підхід
Якщо у вас ОБ'ЄДНІ деякі динамічні аспекти бази даних EAV, ви можете розглянути можливість створення метаданих так, як якщо б ви мали таку базу даних, а натомість фактично використовували шаблон дизайну супертип / підтип. Так, вам доведеться створювати нові таблиці, додавати та видаляти та змінювати стовпці. Але при належній попередній обробці (як я це робив із автоматичними переглядами бази даних EAV), ви могли б мати справжні об’єкти, схожі на таблицю. Тільки вони не були б такими загальними, як у мене, і оптимізатор запитів міг би передбачити натискання на базові таблиці (читай: добре з ними). Було б просто одне з'єднання між таблицею супертипу та таблицею підтипу. Ваша програма може бути налаштована на зчитування метаданих, щоб виявити, що вона повинна робити (або в деяких випадках вона може використовувати автоматично сформовані представлення даних).
Або, якщо у вас був багаторівневий набір підтипів, лише декілька приєднується. Під багаторівневим я маю на увазі, коли деякі підтипи мають спільні стовпці, але не всі, у вас може бути таблиця підтипу для тих, що є самим супертипом кількох інших таблиць. Наприклад, якщо ви зберігаєте інформацію про сервери, маршрутизатори та принтери, проміжний підтип "IP-пристрій" може мати сенс.
Я дам застереження, що я ще не створив таку гібридну базу даних супертипу / підтипу EAV, оздобленої метаболітом, як я пропоную тут ще спробувати у реальному світі. Але проблеми, з якими я стикався з EAV, не малі, і робити щось , мабуть, є абсолютно необхідним, якщо ваша база даних буде великою, і ви хочете гарної продуктивності без шаленого дорогого гігантського обладнання.
На мою думку, час, витрачений на автоматизацію використання / створення / модифікації реальних таблиць підтипів, був би найкращим. Орієнтація на гнучкість, керована даними, робить звук EAV таким привабливим (і повірте, мені подобається, як коли хтось запитує у мене новий атрибут типу елемента, я можу додати його приблизно за 18 секунд, і вони можуть негайно почати вводити дані на веб-сайті ). Але гнучкість може бути досягнута більш ніж одним способом! Попередня обробка - ще один спосіб зробити це. Це настільки потужний метод, що так мало людей використовує, що дає переваги від керування повністю даними, а від продуктивності - жорсткого кодування.
(Примітка. Так, ці погляди справді відформатовані таким чином, і PIVOT справді мають тригери оновлення. :) Якщо хтось справді зацікавлений у жахливих хворобливих деталях довгого та складного тригера UPDATE, дайте мені знати, і я опублікую зразок для вас.)
І ще одна ідея
Покладіть всі свої дані в одну таблицю. Дайте стовпцям загальні імена, а потім повторно використовуйте / зловживайте ними для кількох цілей. Створіть уявлення над ними, щоб дати їм розумні імена. Додайте стовпці, коли невикористаний стовпець відповідного типу даних недоступний, і оновіть свої представлення. Незважаючи на те, що я тривав щодо підтипу / супертипу, це може бути найкращим способом.