Структура базы данных инвентаризации, когда элементы инвентаря имеют различные атрибуты

10

Я создаю базу данных инвентаризации для хранения информации об оборудовании предприятия. Устройства, которые база данных отслеживает в диапазоне от рабочих станций, ноутбуков, коммутаторов, маршрутизаторов, мобильных телефонов и т. Д. Я использую серийные номера устройств в качестве первичного ключа. У меня проблема в том, что другие атрибуты для этих устройств различаются, и я не хочу, чтобы в таблице инвентаризации были поля, не связанные с другими устройствами. Ниже приведена ссылка на ERD части базы данных (некоторые отношения FK не показаны). Я пытаюсь настроить его, например, чтобы устройство с типом устройства рабочей станции не могло быть помещено в таблицу телефонов. Похоже, что для проверки типа или класса устройства требуется использование большого количества триггеров, и новые таблицы в любое время будут отслеживать другое устройство с другими атрибутами;

ERD1

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

Пример виджета ERD

Эта структура будет прекрасно работать, если все атрибуты будут применимы к элементам, которые я храню. Например, если в базе данных хранятся только мобильные телефоны, атрибутами могут быть такие вещи, как сенсорный экран, трекпад, клавиатура, 4G, 3G ... что угодно. В этом случае все они относятся к телефонам. Моя база данных будет иметь такие атрибуты, как hostname, circuitType, phoneNumber, которые применяются только к определенным типам устройств.

Я хочу настроить его так, чтобы только устройству этого типа можно было назначать только те атрибуты, которые относятся к данному типу устройства. Любые предложения о том, как настроить эту базу данных? Я не уверен, что это правильное использование отношений один-к-одному или есть лучший способ сделать это. Заранее спасибо, что нашли время разобраться в этом.

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

/programming/9335548/how-to-structure-database-for-inventory-of-unlike-items

/programming/1249632/database-structure-for-items-with-varying-attributes

/programming/5559587/product-inventory-with-multiple-attributes

/programming/6613802/question-about-setting-up-inventory-database

/programming/514111/how-to-best-represent-items-with-variable-of-attributes-in-a-database

TheSecretSquad
источник

Ответы:

6

Supertype / Подтип

Как насчет изучения паттерна супертипа / подтипа? Общие столбцы идут в родительской таблице. Каждый отдельный тип имеет свою собственную таблицу с идентификатором родителя в качестве собственного PK и содержит уникальные столбцы, не общие для всех подтипов. Вы можете включить столбец типа в родительскую и дочернюю таблицы, чтобы гарантировать, что каждое устройство не может иметь более одного подтипа. Создайте FK между дочерними и родительскими элементами (ItemID, ItemTypeID). Вы можете использовать FK для таблиц супертипа или подтипа, чтобы поддерживать желаемую целостность в другом месте. Например, если ItemID любого типа разрешен, создайте FK для родительской таблицы. Если можно ссылаться только на SubItemType1, создайте FK для этой таблицы. Я бы оставил TypeID вне ссылочных таблиц.

Именование

Когда дело доходит до именования, у меня есть два варианта, как я вижу (поскольку третий выбор просто «ID», на мой взгляд, является сильным анти-паттерном). Либо вызовите ключ подтипа ItemID, как он есть в родительской таблице, либо назовите его именем подтипа, таким как DoohickeyID. После некоторых размышлений и некоторого опыта с этим я рекомендую назвать это DoohickeyID. Причина этого в том, что, хотя может быть путаница с таблицей подтипов, действительно скрытой, содержащей элементы (а не Doohickeys), это небольшой минус по сравнению с тем, когда вы создаете FK для таблицы Doohickey, а имена столбцов не матч!

В EAV или нет в EAV - Мой опыт работы с базой данных EAV

Если EAV - это то, что вы действительно должны делать, то это то, что вы должны делать. Но что, если это было не то, что ты должен был сделать?

Я создал базу данных EAV, которая используется в бизнесе. Слава Богу, набор данных небольшой (хотя существуют десятки типов элементов), поэтому производительность неплохая. Но было бы плохо, если бы в базе данных было более нескольких тысяч элементов! Кроме того, таблицы так трудно запросить. Этот опыт привел меня к желанию избегать баз данных EAV в будущем, если это вообще возможно.

Теперь в моей базе данных я создал хранимую процедуру, которая автоматически создает представления PIVOTed для каждого существующего подтипа. Я могу просто запросить у AutoDoohickey. В моих метаданных о подтипах есть столбец «ShortName», содержащий объектно-безопасное имя, подходящее для использования в именах представлений. Я даже сделал взгляды обновляемыми! К сожалению, вы не можете обновить их в соединении, но вы МОЖЕТЕ вставить в них уже существующую строку, которая будет преобразована в ОБНОВЛЕНИЕ. К сожалению, вы не можете обновить только несколько столбцов, потому что нет никакого способа указать 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, дайте мне знать, и я опубликую образец для вас.)

И еще одна идея

Положите все свои данные в одну таблицу. Дайте столбцам общие имена, а затем повторно используйте / используйте их для разных целей. Создайте взгляды на них, чтобы дать им разумные имена. Добавьте столбцы, когда неиспользуемый столбец подходящего типа данных недоступен, и обновите свои представления. Несмотря на мою длину о подтипе / супертипе, это может быть лучшим способом.

ErikE
источник
Я подумал об этой схеме, в которой каждая таблица подтипов имеет PK из родительского поля и необычные поля. Я думал, что смогу поместить поле типа в родительскую и каждую таблицу подтипов, а затем наложить на них ограничение CHECK. Я решил избегать такой схемы, потому что она потребует новой таблицы в любое время, когда необходимо отслеживать устройство нового типа, и много взаимосвязей «один к одному». Это казалось грязным и негибким. Я ценю ваш вклад, хотя.
TheSecretSquad
Я создал базу данных EAV, которая используется в бизнесе. Слава Богу, набор данных небольшой (хотя существуют десятки типов элементов), поэтому производительность неплохая. Но это было бы, если бы в базе данных было более нескольких тысяч элементов. Этот опыт привел меня к тому, что я действительно хочу избегать баз данных EAV в будущем, если это вообще возможно, потому что они так трудны для запроса.
ErikE
Кроме того, время, потраченное на автоматизацию использования / создания / модификации реальных таблиц подтипов, на мой взгляд, в конечном итоге будет наилучшим.
ErikE
Изучив шаблон EAV, я понял, что значения атрибутов должны иметь общий тип данных (в данном случае все строки). Кроме того, запрос настройки EAV будет рутиной. Супертип / подтип выглядит лучше. Мой вопрос сейчас, некоторые таблицы разрешают только определенные типы устройств. Нужно ли проверять это, помещая идентификатор класса устройства (телефон, компьютер, маршрутизатор) в каждую таблицу и накладывая проверочное ограничение на это поле, или я исключаю это поле из таблиц подтипов и использую триггер для каждой из них? Пожалуйста, смотрите ERD3 для справки.
TheSecretSquad
1
Для запроса данных в формате EAV нередко создается datamart из реляционных таблиц для данных, которые вы хотите запросить, а затем заполняете их с помощью некоторого сценария. Запросы будут выполняться быстрее, но только для данных, которые вы вводите в datamart, и настройка требует значительного планирования.
FrustratedWithFormsDesigner
6

В вашем случае лучший подход - это вариант модели Entity-Attribute-Value (EAV). Есть много людей, которые уклоняются от EAV, потому что это в некотором смысле бесполезно и часто используется не по назначению. Тем не менее, EAV является решением, которое хорошо работает для ваших конкретных требований.

Вариант, который вы хотите включить в свою ситуацию, состоит в том, чтобы абстрагировать атрибуты на один уровень от ваших сущностей (то есть предметов инвентаря). По сути, вы хотите определить типы устройств, которые имеют список атрибутов. Затем вы определяете экземпляры устройств, которые имеют значения для каждого из атрибутов, которые должны иметь устройства этого типа.

Вот эскиз ERD:

ERD

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

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

Джоэл Браун
источник
Это похоже на то, что рекомендует ssmusoke. Я изменил свой ERD, используя его рекомендации, и похоже, что он соответствует вашему. Не стесняйтесь проверить новый RD на http://www.dividegraphics.com/ERD2.jpg и предоставить любую обратную связь.
TheSecretSquad
@reallythecrash - Вы правы, я предлагаю тот же базовый подход, что и ssmusoke, я просто изменил свой ответ в надежде облегчить понимание как структуры модели, так и логического обоснования использования EAV, которое Многие люди (несправедливо) осуждают как анти-образец.
Джоэл Браун
После некоторых исследований я понимаю, почему люди могут считать EAV анти-паттерном. Я думаю, что хранить данные с помощью EAV просто, но особенно сложно для запросов и поддержки типов данных. Я думаю, что это шаблон с узкой целью, и его должны использовать опытные разработчики, которые могут реализовать его должным образом, т.е. не я. Я, вероятно, выберу парадигму супертипа / подтипа.
TheSecretSquad
@JoelBrown - какое программное обеспечение вы использовали для создания схемы?
Видар
@Vidar - я использовал Visio с ERT-формами smartshapes, которые я создал, чтобы использовать визуальные соглашения Джеймса Мартина и нарисовал их с помощью нестандартного рисунка линий. Я считаю, что это хороший инструмент для быстрого / чернового моделирования данных. Когда диаграмма слишком формальная, это может заставить некоторых людей думать, что она закончена, поэтому что-то отрывочное помогает не дать людям поспешно прийти к выводам о том, насколько надежной / законченной является модель данных.
Джоэл Браун
1
  1. Общий подход заключается в следующем:

a) Подход модели Entity-Attribute-Value для привязки атрибутов различных устройств к типу устройства. У каждого типа устройства будет список атрибутов, значения которых вы отслеживаете

б) Для каждого типа устройства вы отслеживаете данные инвентаризации по серийному номеру, который соответствует одному устройству.

  1. Таким образом, вы получите следующие таблицы:

а) Атрибуты - определяют атрибуты для всех устройств (все, что идет в этой таблице) столбцы: идентификатор, имя, описание

b) Атрибуты элемента - определяет разрешенные атрибуты для конкретного устройства - itemid, attributeid

c) Определение элемента - определяет элемент, скажем, Black Berry Torch 4500, Iphone 4S, Iphone 3S и т. д. - id, имя, описание, categoryid (если вы хотите добавить категории, такие как мобильные телефоны, коммутаторы и т. д.)

d) Устройства - отдельные устройства - id, itemid, инвентаризация, деактивация, серийный номер ... (в основном все другие атрибуты для устройства)

Если вы хотите отслеживать любую другую информацию о перемещениях устройства, вы можете добавить дополнительные таблицы, связанные с устройством, по мере необходимости.

Стивен Сенкомаго Мусоке
источник
Спасибо за ваш вклад. Это соответствует тому, что я ищу, я просто не мог понять, как это сделать. Я изменил свой ERD, чтобы отразить ваши спецификации. Кажется, что требуется больше работы для ввода всех допустимых атрибутов для каждого типа устройства, но также похоже, что он предлагает максимальную гибкость. Я собираюсь сделать небольшой прототип, чтобы увидеть, работает ли он так, как я думаю. Еще раз спасибо. Я загрузил ERD с изменениями, если вы хотите посмотреть и сообщить мне, на правильном ли я пути. http://www.dividegraphics.com/ERD2.jpg
TheSecretSquad
Да, вы на правильном пути.
Стивен Сенкомаго Мусоке
EAV предложит большую гибкость, но у вас также есть гораздо больше метаданных, чтобы поддерживать его работу.
FrustratedWithFormsDesigner
@FrustratedWithFormsDesigner кажется неизбежным, когда система хранит широкий спектр элементов, телефонов, коммутаторов, ПК, ноутбуков и т. Д. Лучше больше метаданных, чем больше таблиц, я бы сказал,
Стивен Сенкомаго Мусоке
1
@ssmusoke: Согласен, но я хотел подчеркнуть этот момент, потому что я видел, как люди не понимают важность метаданных, и тогда их реализация EAV становится кошмаром.
FrustratedWithFormsDesigner