Какую структуру БД я должен использовать для сайта с бесконечной подкатегорией?
-
07-07-2019 - |
Вопрос
Например, «Dole Banana» это тип продукта, он указан в разделе "Бананы" категории, когда я открываю "Фрукты" категорию, я хочу увидеть "Dole Banana".
+ Food
|--+ Fruits
|------+ Bananas
|------+ Apples
|--+ Vegetables
|------+ Onion
|------+ Spinach
Решение
Я обычно использовал деревья слева-справа , которые очень хорошо адаптированы к запросам к базе данных. У вас есть parentId, левое и правое значение для каждого узла. Каждый дочерний узел имеет левое / правое значение, которое находится между родительскими узлами слева и справа, что позволяет очень легко найти, например, всех дочерних / родительских узлов. Это немного увеличивает накладные расходы на вставку, но не должно быть слишком сильным, если вы не вставите много.
Редактировать. Однако, просто предупреждение. Вам нужно выполнить операции вставки / обновления в заблокированной транзакции, иначе дерево может испортиться.
Другие советы
Если вы ищете онлайн-ресурсы, посвященные этой проблеме, " Хранение дерева в базе данных " будет хорошей поисковой фразой.
Что касается решения, обратите внимание, что каждая подкатегория может иметь одну или нулевую родительскую категорию. Следовательно, все дерево может быть сохранено в одной само-ссылочной таблице с «родителем». поле. р>
Используя ваше дерево примеров:
ID | PARENT | NAME
-----+--------+-------------
1 | null | Food
2 | 1 | Fruits
3 | 2 | Bananas
4 | 2 | Apples
5 | 1 | Vegetables
6 | 5 | Onion
7 | 5 | Spinach
Таблица "Категории" с 3 полями. Р> <Ол>
Чтобы получить все корневые категории
select * from Categories where ParentCategoryId is null
Чтобы получить все подкатегории определенной категории:
select * from Categories where ParentCategoryId = 12
Вы можете использовать простую структуру таблицы с parent_category_id и извлекать все дерево с помощью рекурсии или реализовывать левые / правые значения и извлекать все дерево, используя предварительно упорядоченный метод обхода дерева.
Если вы имеете в виду бесконечное количество уровней, то таблицу с самореференциями можно использовать повторно. Пример: StuffID, StuffName, StuffParentID (от FK до Stuff ID)
Для конечного числа фиксированные таблицы: parent-child-grandchild
CREATE TABLE [dbo].[Category](
[CategoryId] [int] NOT NULL,
[ParentCategoryId] [int] NULL,
[CategoryName] [nvarchar](50) NOT NULL,
CONSTRAINT [PK_Category] PRIMARY KEY CLUSTERED
(
[CategoryId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON
[PRIMARY]
GO
ALTER TABLE [dbo].[Category] WITH CHECK ADD CONSTRAINT [FK_Category_Category] FOREIGN KEY([ParentCategoryId])
REFERENCES [dbo].[Category] ([CategoryId])
GO
ALTER TABLE [dbo].[Category] CHECK CONSTRAINT [FK_Category_Category]
GO
Для бесконечной иерархии используйте модифицированный алгоритм обхода дерева предзаказа р>
Вот другой подход, который может быть полезен для вас. У него немного больше затрат на обслуживание, чем у подхода PARENT_ID или lft / rght, но поиск намного проще (и быстрее).
Dole бананы могут быть в таблице продуктов. У вас есть один category_id для продукта.
У нас было требование разрешить несколько категорий для продукта. Это привело нас к созданию таблицы соединений category_products, где продукт может иметь несколько соединенных строк Затем мы должны были решить, есть ли бананы Dole только в бананах, или в бананах и всех их родителях. Поскольку скорость поиска была критической, мы поместили бананы пособия по безработице в его категории и все их родительские категории. Есть три объединения продуктов категории для бананов пособия по безработице. Р>
Используя эту структуру, вернуть все элементы из любой категории легко и быстро, всего один запрос. Вы не можете сделать это в подходе PARENT_ID (если вы не жестко закодировали родителей, бабушек и дедушек и т. Д.). Добавить категорию просто. Категоризация продукта требует вставки нескольких строк в таблицу соединений. Удаление и перемещение категорий немного сложнее.