Внедрение гибких отношений в РСУБД - каковы на самом деле компромиссы? - PullRequest
7 голосов
/ 24 июня 2011

У меня есть набор продуктов с различными возможными атрибутами для каждого продукта. Например. Продукт А имеет название, размер, цвет, форму. Продукт B имеет название, калории, сахар и т. Д. Один из способов решить эту проблему:

1) Создание таблиц

Products (id, name)
Attributes (id, name)
Product_Attributes (product_id, attribute_id, value as string)

Это обеспечивает максимальную гибкость, но я слышал, что многие люди рекомендуют против этого, хотя я не уверен, почему. Я имею в виду, что если бы эти таблицы назывались Teams, Players, Team_Players, мы все согласились бы, что это правильный реляционный дизайн.

Каждый, кто объясняет мне, почему это плохо, делает это в контексте полностью гибкого реляционного дизайна, в котором вы никогда не создаете реальные таблицы за несколькими базовыми начальными таблицами (например, object, attribute, object_attribute) - что Я думаю, что мы все можем согласиться, это плохо. Но это гораздо более ограниченная и ограниченная версия (только продукты, а не каждый объект в системе), поэтому я не думаю, что было бы справедливо объединять эти две архитектуры вместе.

С какими проблемами вы столкнулись (опытные или теоретические), которые делают этот дизайн таким плохим?

2) Еще один способ решить эту проблему - создать таблицу Product с несколькими столбцами, такими как Size, Color, Shape, Weight, Sugar и т. Д., А затем добавить несколько дополнительных столбцов в конце, чтобы дать нам некоторую гибкость. Это создаст обычно разреженные строки, заполненные в основном значениями NULL. Людям нравится этот подход, но у меня вопрос: сколько столбцов может быть у вас до того, как этот подход потеряет свою производительность? Если у вас есть 200 столбцов, я думаю, что это уже не умный ход, а как насчет 100 столбцов? 50 столбцов? 25 столбцов?

3) Последний известный мне подход заключается в том, чтобы хранить все атрибуты в виде большого двоичного объекта (возможно, JSON) в одном столбце таблицы «Продукты». Мне нравится такой подход, но он не чувствуется правильным. Запросы сложны. И если вы хотите иметь возможность легко изменить имя атрибута позже, вам нужно либо проанализировать каждую запись по отдельности, либо сделать так, чтобы они вводились в ваш BLOB-объект по некоторому идентификатору. Если вы идете по пути id, вам понадобятся другие атрибуты таблицы, и все будет выглядеть как подход № 1 сверху, за исключением того, что вы не сможете присоединить attribute_id со своим BLOB-объектом, поэтому я надеюсь, что вы не хотите ничего запрашивать по имени атрибута.

Что мне нравится в этом подходе, так это то, что вы можете запрашивать один продукт, а в своем коде вы можете легко получить доступ ко всем его свойствам - быстро. И если вы удалите продукт, вам не придется очищать другие таблицы - легко оставаться последовательным.

4) Я читал кое-что о возможности индексирования строго типизированных форматов xml в некоторых СУБД, но, честно говоря, я не очень много знаю об этом подходе.

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

Ответы [ 4 ]

10 голосов
/ 25 июня 2011

Вы, вероятно, сможете найти много интересного по этой теме, выполнив поиск в Google по "значению атрибута сущности antipattern".

Одна из проблем этого подхода заключается в том, что вы в конечном итоге смешиваете метаданные с реальными данными. Теперь ваш «атрибут» должен сообщить базе данных, что именно содержится в столбце «значение». Это может очень затруднить обработку этих данных во внешнем интерфейсе, программном обеспечении для создания отчетов и т. Д.

Во-вторых, вам будет очень трудно обеспечить соблюдение целостности данных в базе данных. Когда ваш продукт имеет атрибут «вес», что мешает кому-то поставить «22 дюйма» в значение? Или не числовое значение полностью. Вы можете сказать: «Ну, мое заявление справится с этим». Затем вам нужно менять свое приложение каждый раз, когда вы хотите добавить новый атрибут, потому что приложение должно знать, как его обрабатывать. Если вы собираетесь выполнить всю эту работу, просто добавьте новый столбец.

В-третьих, как вы обеспечиваете, чтобы данный продукт имел все необходимые атрибуты? В строке вы можете сделать столбец NOT NULL, и тогда они потребуются для передачи этой строки в базу данных. Вы не можете применить это в модели EAV.

В-четвертых, такая модель обычно приводит к путанице. Люди не уверены, какие «атрибуты» поддерживаются, или они дублируют атрибут, или они забывают обрабатывать атрибут при создании отчета. Например, если у меня есть атрибут «Вес (кг)» и другой атрибут для «Вес (фунты)», и кто-то спрашивает меня: «Какой самый тяжелый продукт в вашей базе данных?» Я бы лучше помнил, что мне нужно проверить оба атрибута.

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

В-шестых, может потребоваться МНОГО объединений, чтобы получить одну строку данных для данного продукта. Вы можете возвращать атрибуты в виде отдельных строк, но теперь вам нужно придумать настраиваемые списки для перечисления этих продуктов и т. Д. Аналогично, написание поисковых запросов для этой модели может быть очень трудным, и в обеих этих ситуациях вы, вероятно, есть проблемы с производительностью.

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

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

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

Существует много проблем с гибкими моделями данных, но первая, которая может вас укусить, заключается в том, что запросы очень громоздки очень быстро.Например, если вы хотите получить атрибут Size для каждого продукта, запрос будет относительно простым.

SELECT p.name product_name, 
       pa.value product_size
  FROM product p    
         left outer join product_attribute pa on (p.product_id = pa.product_id)
         left outer join attribute a on (pa.attribute_id = a.attribute_id and 
                                         a.name          = 'size')

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

SELECT p.name product_name, 
       pa_size.value product_size
       pa_color.value product_color
  FROM product p    
         left outer join product_attribute pa_size on (p.product_id = pa_size.product_id)
         left outer join product_attribute pa_color on (p.product_id = pa_size.product_id)
         left outer join attribute a_size on (pa_size.attribute_id = a.attribute_id and 
                                              a_size.name          = 'size')
         left outer join attribute a_color on (pa_color.attribute_id = a.attribute_id and
                                              a_color.name         = 'color')

Очень быстро, когда вы начинаете хотеть захватить 10 атрибутов или писать сложные поиски (покажите мне продукты, где цвет синий, а размер средний), запросы становятся очень сложными как для разработчиков, так и для написания.поддерживать и для оптимизатора базы данных для создания плана запроса.Если вы объединяете 30 таблиц вместе, оптимизатору придется сократить дерево планов, которые он считает очень, очень быстро, чтобы иметь возможность генерировать план запроса в разумные сроки.Это приводит к тому, что оптимизатор отбрасывает многообещающие пути слишком рано и генерирует пути, не оптимальные для многих ваших запросов.

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

Помимо этих затрат для разработчиков, вы в конечном итоге создаете много затрат для всей организации.

  • Ни один инструмент запросов не справится с такой моделью данных.Таким образом, все пользователи, которые в настоящее время могут запустить свой любимый инструмент запросов и запустить некоторые отчеты из вашей базы данных, теперь застряли в ожидании, когда разработчики напишут свои отчеты и сделают для них выдержки.
  • Качество данных становится очень труднымсоблюдение.Становится очень сложно проверять условия, которые включают несколько атрибутов (например, если размер продукта средний, вес должен составлять от 1 до 10 фунтов, если указан рост продукта, то также требуется ширина), чтобы люди не делали такиечеки.Они не пишут отчеты, чтобы определить, где такие правила нарушаются.Таким образом, данные заканчивают тем, что представляют собой небольшой набор данных, которые нижестоящие процессы решают, что они не могут использовать, потому что они недостаточно полны.
  • Вы переносите слишком много начальных обсуждений требований в будущее, когда понимание основных сущностей, вероятно, приведет к гораздо лучшему дизайну в целом.Если вы не можете согласиться с набором атрибутов, которые должна поддерживать первая версия продукта, вы не совсем понимаете, что должна делать эта версия.Даже если вы успешно закодировали очень универсальное приложение, это означает, что после его создания потребуется много времени для настройки (потому что кто-то должен будет выяснить, какие атрибуты он поддерживает в этот момент).И тогда вы обнаружите, что при настройке приложения вы пропустили массу требований, которые стали понятны только после определения атрибутов - вы не можете знать, что ширина требуется, если указана высота, если вы не знаете,в первую очередь они будут хранить высоту или ширину.
    В худшем случае ответом на эту проблему во время настройки является немедленное определение необходимости предоставления гибкого способа задания бизнес-правил и рабочих процессов, чтобы люди, конфигурирующие приложение, могли быстро кодировать свои бизнес-правила при добавлении новых атрибутов.и чтобы они могли управлять потоком приложения, группируя атрибуты или пропуская определенные страницы (т. е. имеют страницу, на которой требуются марка и модель, если тип продукта - автомобиль, пропустите эту страницу, если сейчас).Но для того, чтобы сделать это, вам нужно создать целую среду разработки.И вы собираетесь продвигать работу по написанию кода приложения для тех, кто настраивает продукт.Если вы не очень хорошо умеете создавать среды разработки, и если люди, конфигурирующие продукт, не являются действительно разработчиками, это не закончится хорошо.
2 голосов
/ 25 июня 2011

Я имею в виду, если бы эти таблицы назывались «Команды, Игроки, Team_Players», мы все согласились бы с тем, что это правильный реляционный дизайн.

Нет, мы бы не стали.Вот почему.

Вы начали с этого.

Products (id, name)
Attributes (id, name)
Product_Attributes (product_id, attribute_id, value as string)

Давайте отбросим номера идентификаторов, чтобы мы могли видеть, что на самом деле происходит.(Более длинные имена столбцов для ясности.)

Products (product_name)
Attributes (attribute_name)
Product_Attributes (product_name, attribute_name, value as string)

И переводить это командам и игрокам.,.

Teams (team_name)
Players (player_name)
Team_Players (team_name, player_name, value as string)

Итак, для выборочных данных у нас может быть

Team                   Player             Value
--
St. Louis Cardinals    Boggs, Mitchell    ?
St. Louis Cardinals    Carpenter, Chris   ?
St. Louis Cardinals    Franklin, Ryan     ?
St. Louis Cardinals    Garcia, Jaime      ?

Что на земле принадлежит вместо знаков вопроса?Допустим, мы хотим записать количество сыгранных игр.Теперь пример данных выглядит следующим образом.

Team                   Player             Value
--
St. Louis Cardinals    Boggs, Mitchell    23
St. Louis Cardinals    Carpenter, Chris   15
St. Louis Cardinals    Franklin, Ryan     19
St. Louis Cardinals    Garcia, Jaime      14

Хотите также хранить среднее значение ватина?Ты не можешьМало того, что вы не можете хранить среднее значение ватина вместе с играми, вы не можете определить, просмотрев базу данных, сыграл ли Митч Боггс в 23 играх, имел 23 удара, забил 23 пробежки, имел 23 "на летучих мышах", имел 23 сингла,или вычеркнуты 23 раза.

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

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

Теперь вы говорите, что атрибуты продукта будут существенно отличаться.Я не согласен.Там будет много атрибутов, которые являются общими для большого количества ваших товаров, таких как цена, единицы, размер, цвет, размеры, вес.Они должны быть в таблице продуктов как общие свойства.Это также те, которые пользователь, скорее всего, будет искать при выборе товара.

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

Наконец, у вас осталось несколько атрибутов, которые могут отличаться.Но насколько они отличаются?Являются ли они общими для продукта типа partiuclar (у книг есть эти атрибуты, у камер есть они), тогда соответствующая таблица для этого типа продукта могла бы работать хорошо.

Как только вы выполнили свою работу и выяснили все это, добавьте гибкость таблицы EAV, если она вам все еще нужна.Вышеприведенные шаги должны охватывать 98 +% реальных требований.

(Также довольно сложно разработать таблицу деталей заказа, если вы не знаете полей атрибутов, которые нужно записать для заказа - вы можетене полагайтесь на таблицу продуктов для этого)

(о, и я полностью согласен с тем, что говорит @Tom H.)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...