Необходима структура базы данных - PullRequest
19 голосов
/ 26 октября 2009

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

Propeller -> aircraft -> wood -> brand -> product.

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

альтернативный текст http://www.usfultimate.com/temp/db_design.jpg

Теперь, все это выглядело хорошо и замечательно, пока я не понял, что категория "дерево" будет также использоваться под пропеллером -> воздушная лодка -> (дерево). Это будет означать, что «дерево» придется воссоздавать каждый раз, когда я хочу использовать его под другим родителем. Это не конец света, но я хотел знать, есть ли более оптимальный способ сделать это.

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

* Edit. Думал о создании таблицы «тегов». Таким образом, я мог бы присвоить тег «дерево», «металл» или «50 дюймов» 1 для многих предметов. Я бы по-прежнему оставлял тип родительского типа для основных категорий, но таким образом категории не должны были бы заходить так глубоко, и повторения не было бы.

Ответы [ 8 ]

27 голосов
/ 08 ноября 2009

Во-первых, пользовательский интерфейс: как пользователь I ненавижу для поиска товара в каталоге, организованном строго иерархическим способом. Я никогда не помню, в какой категории sub-sub-sub-sub ...- "экзотический" продукт, и это вынуждает меня тратить время на изучение "многообещающих" категорий, просто чтобы обнаружить, что он относится к категории (для меня, по крайней мере, ) странным образом.

То, что Кевин Пено предлагает, является хорошим советом и известно как граненый просмотр . Как писал Марсия Бейтс в После того, как точечная бомба: Получение доступа к веб-информации прямо на этот раз , « ..» - это иерархическая классификация, а реляционные базы данных - иерархические базы данных. . ..".

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

Чтобы быстро понять, что такое граненый поиск, есть несколько демонстраций для изучения на Проект интерфейса поиска Flamenco - Интерфейсы поиска, которые текут .

Во-вторых, логика приложения: то, что предлагает Manitra , также является хорошим советом (насколько я понимаю), то есть разделение nodes и links дерева / графа. в разных отношениях. То, что он называет «таблицей предков» (однако это гораздо более интуитивное имя), известно как транзитивное замыкание ориентированного ациклического графа (DAG) (отношение достижимости). Помимо производительности, он значительно упрощает запросы, как сказал Манитра.

Но Я предлагаю представление для такой «таблицы предков» (транзитивное замыкание), чтобы обновления выполнялись в режиме реального времени и постепенно, а не периодически с помощью пакетного задания. В статьях, которые я упоминал в своем ответе на язык запросов для наборов графов: вопрос моделирования данных , есть код SQL (но я думаю, что его нужно немного адаптировать к конкретным СУБД). В частности, посмотрите Поддержание транзитивного закрытия графиков в SQL (.ps - postscript).

Продукты-Категории отношений

Первый пункт Манитры также заслуживает внимания.

Он говорит о том, что между товарами и категориями существует отношение «многие ко многим». Т.е. каждый товар может относиться к одной или нескольким категориям, а в каждой категории может быть ноль или более товаров.

Для заданных переменных отношений (relvars) продуктов и категорий такие отношения могут быть представлены, например, как relvar PC с по крайней мере атрибутами P # и C #, то есть номерами продуктов и категорий (идентификаторами) в отношениях внешнего ключа с соответствующими Номера продуктов и категорий.

Это дополняет управление иерархиями категорий. Конечно, это всего лишь эскиз дизайна.

О граненом просмотре в SQL

Полезной концепцией для реализации "граненого просмотра" является реляционное деление или, даже, реляционное сравнение (см. Нижнюю часть связанной страницы). То есть разделив ПК (категории продуктов) на (растущий) список категорий, выбранных пользователем (фасетная навигация), можно получить только продукты в таких категориях (конечно, категории предполагаются , а не все взаимоисключающие, в противном случае выбирается две категории одна получит ноль продуктов).

СУБД на основе SQL обычно не имеют этих операторов (деление и сравнение), поэтому ниже я приведу несколько интересных статей, которые их реализуют / обсуждают:

и так далее ...

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

Отступление на «плоскостность»

Я кратко посмотрел статью, связанную с Pras , Управление иерархическими данными в MySQL , но я перестал читать после нескольких строк во введении:

Введение

Большинство пользователей в то или иное время имеют имеет дело с иерархическими данными в SQL базы данных и, без сомнения, узнали, что управление иерархическими данными не что такое реляционная база данных за. Таблицы реляционных базы данных не являются иерархическими (например, XML), но это просто плоский список . У иерархических данных есть родитель-потомок отношения, которые не являются естественными представлены в реляционной базе данных Таблица. ...

Чтобы понять, почему это требование настойчивости отношений просто бессмыслица , представьте куб в трехмерной декартовой системе координат : он будет идентифицирован 8 координатами (триплетами), скажем, P1 (x1, y1, z1), P2 (x2, y2, z2), ..., P8 (x8, y8, z8) [здесь мы не имеем дело с ограничениями на эти координаты, чтобы они действительно представляли куб] .

Теперь мы поместим этот набор координат (точек) в переменную отношения и назовем эту переменную Points. представим значение отношения Points в виде таблицы ниже:

<b>Points</b>|  <b>x</b> |  <b>y</b> |  <b>z</b> |
=======+====+====+====+
       | x1 | y1 | z1 |
       +----+----+----+
       | x2 | y2 | z2 |
       +----+----+----+
       | .. | .. | .. |
       | .. | .. | .. |
       +----+----+----+
       | x8 | y8 | z8 |
       +----+----+----+

Разве этот кубик "сплющивается" простым актом представления его в табличной форме? Отношение (значение) - это то же самое, что и его табличное представление?

Переменная отношения предполагает в качестве значений наборы точек в n-мерном дискретном пространстве, где n - количество атрибутов отношения («столбцов»). Что значит для n-мерного дискретного пространства быть «плоским»? Просто чепуха, как я писал выше.

Не поймите меня неправильно. Конечно, SQL - это плохо спроектированный язык и что СУБД на основе SQL полны идиосинкразий и недостатков (NULL, избыточность, ...), особенно плохих, СУБД -as-dumb-store type (без ссылочных ограничений, без ограничений целостности, ...). Но это не имеет ничего общего с воображаемыми ограничениями реляционной модели данных: наоборот, чем больше они отворачиваются от нее, тем хуже результат.

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

В модели "Вложенный набор"

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


РЕДАКТИРОВАТЬ: Стефан Эггермонт в комментариях ниже возразил, что " Модель плоского списка является проблемой. Это абстракция реализации, которая затрудняет достижение производительности. ...".

Теперь моя точка зрения такова:

  1. эта "модель плоского списка" является фантазией : только то, что один выкладывает (представляет) отношения в виде таблиц ("плоских списков"), не означает, что отношения являются "плоскими списками" (объектом) "и его представления не одно и то же);
  2. логическое представление (отношение) и физические детали хранения (горизонтальная или вертикальная декомпозиция, сжатие, индексы (хеши, b + дерево, r-дерево, ...), кластеризация, разбиение и т. Д.) Различны; одна из точек реляционной модели данных ( RDM ) состоит в том, чтобы отделить логическую модель от «физической» (с преимуществами как для пользователей, так и для разработчиков СУБД);
  3. является прямым следствием подробностей физического хранения (реализация), а , а не логического представления (комментарий Эггермонта является классическим примером логически-физического смешения ).

Модель RDM никоим образом не ограничивает реализации; каждый может свободно использовать кортежи и отношения по своему усмотрению. Отношения - это , не обязательно файлы, а кортежи - , не обязательно записи файла. Такое соответствие является тупой реализацией прямого изображения .

К сожалению, реализации СУБД на основе SQL - это , слишком часто глупые реализации с прямым изображением, и они страдают от низкой производительности в различных сценариях - OLAP / ETL существуют продукты для устранения этих недостатков.

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

Конечно, суть не в том, что должен существовать «оптимальный» дизайн физической памяти, но в том, что любой дизайн физической памяти может быть абстрагирован хорошим декларативным языком на основе по реляционной алгебре / исчислениям (а SQL - это плохой пример) или, более точно, по языку логического программирования (например, Пролог - см. мой ответ на « преобразователь пролог в SQL » вопрос). Хорошая СУБД должна оперативно изменять дизайн физической памяти, основываясь на статистике доступа к данным (и / или подсказках пользователя).

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

10 голосов
/ 26 октября 2009

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

Подводя итог, можно сказать, что использование простого parent_category_id не очень хорошо масштабируется, и вам будет сложно писать эффективные SQL-запросы. Ответ заключается в использовании вложенных наборов, которые позволяют визуализировать модель категории «многие ко многим» как наборы, вложенные в другие наборы.

4 голосов
/ 06 ноября 2009

Мои предложения

  • установить отношение «многие ко многим» между «Предметом» и «Категорией», чтобы продукт мог отображаться во многих узлах иерархии (используется в ebay, sourceforge ...)
  • сохранить иерархию категорий

Производительность по иерархии категорий

Если ваша иерархия категорий является глубиной, то вы можете создать таблицу «Предки». Эта таблица будет сгенерирована при пакетной обработке и будет содержать:

  • ChildId (идентификатор категории)
  • AncestorId (идентификатор его родителя, прародителя ... все категории предков)

Это означает, что если у вас есть 3 категории: 1-пропеллер> 2 самолета> 3 дерева

Тогда таблица Ancestor будет содержать:

ChildId  AncestorId
1        2
1        3
2        3

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

Благодаря этой таблице вам потребуется всего 1 объединение для запроса по категории (с ее дочерними элементами).

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

4 голосов
/ 26 октября 2009

Если вы хотите, чтобы категории имели несколько родительских категорий, то это просто отношение «многие ко многим» вместо отношения «один ко многим».Вам нужно будет установить промежуточную таблицу между категорией и самой собой.

Однако я сомневаюсь, что это то, что вы хотите.Если я смотрю в категории Самолеты> Дерево , я бы не хотел видеть предметы из Гребля> Дерево .Существует две категории Древесина , поскольку они содержат разные предметы.

3 голосов
/ 08 ноября 2009

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

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

Стоит отметить, что идея «нескольких категорий» в основном заключается в том, как работает «тегирование». За исключением того, что в тегах мы разрешаем любому продукту иметь много категорий. Допуская, чтобы любой продукт относился ко многим категориям, вы позволяете клиенту полную способность фильтровать свой поиск, начинайте ing там, где они считают, что им нужно начать . Это может быть нажатие на «самолеты», затем «дрова», затем «турбореактивный двигатель» (или что-то еще). Или они могут начать поиск с Вуда и получить тот же результат.

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

В целом результат в основном одинаков, категории будут иметь естественную иерархию , , но это даст еще большую гибкость .

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

РЕДАКТИРОВАТЬ: Я только что понял, что вы смутно упомянули это в своем ответе. На самом деле я этого не заметил, но думаю, что это то, что вы хотели бы сделать вместо этого. В противном случае вы смешиваете две иерархические системы в свою программу без особой выгоды.

2 голосов
/ 09 ноября 2009

Теперь, все это выглядело хорошо и прекрасно, пока я не понял, что категория "дерево" будет также использоваться под пропеллером -> воздушная лодка -> (дерево). Это будет означать, что «дерево» придется воссоздавать каждый раз, когда я хочу использовать его под другим родителем. Это не конец света, но я хотел знать, есть ли более оптимальный способ сделать это.

Что, если у вас есть самолет с деревянной конструкцией, но пропеллер может быть из углеродного волокна, стекловолокна, металла, графита?

Я бы определил таблицу материалов и использовал бы ссылку на внешний ключ в таблице предметов. Если вы хотите поддерживать более одного материала (т. Е., Скажем, есть металлическая реинформация или винты ...), то вам понадобится таблица соответствия / lookup / xref.

MATERIALS_TYPE_CODE таблица

  • MATERIALS_TYPE_CODE рк
  • MATERIALS_TYPE_CODE_DESC

PRODUCTS стол

  • PRODUCT_ID, шт
  • MATERIALS_TYPE_CODE fk ЕСЛИ связан только один материал

PRODUCT_MATERIALS_XREF стол

  • PRODUCT_ID, шт
  • MATERIALS_TYPE_CODE рк

Я бы также связывал продукты друг с другом, используя таблицу соответствия / lookup / xref. Продукт может быть связан с более чем одним комплектным продуктом:

KITTED_PRODUCTS стол

  • PARENT_PRODUCT_ID, фк
  • CHILD_PRODUCT_ID, фк

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

2 голосов
/ 08 ноября 2009

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

Например, у нас были специальные теги, такие как 2009-Nov-Special. Любой продукт, подобный этому, был допущен к показу в качестве специального на первой странице в этом месяце. Таким образом, нам не нужно было создавать специальную систему для управления вращающимися событиями на главной странице, мы просто использовали существующую систему тегов. Позже это можно улучшить, чтобы скрыть эти теги от потребителей и т. Д.

Точно так же вы можете использовать префиксы тегов, такие как: style: wood mfg: Nike, чтобы позволить вам выполнять относительно сложные категоризацию и детализацию без сложного перестановки базы данных или ночных кошмаров EAV, все в системе тегов, которая дает вам больше гибкость, чтобы соответствовать ожиданиям пользователей. Помните, что пользователи могут рассчитывать на навигацию по продуктам иначе, чем вы, как это может ожидать владелец базы данных. Использование системы тегов может помочь вам включить интерфейс покупок, не ставя под угрозу ваш инвентарь, отслеживание продаж или что-либо еще.

1 голос
/ 09 ноября 2009

Вы можете легко проверить свои проекты БД на http://cakeapp.com

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