2009-07-06 6 views
9

У меня в моей базе данных MySQL есть нечто вроде дерева.Иерархические данные в MySQL

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

*categories table* 
id | name | parent_id 
1 | Toys | 0 
2 | Dolls | 1 
3 | Bikes | 1 

Каждый элемент в моей базе данных присваивается одной из этих категорий:

*items table* 
item | category_id 
barbie | 2 
schwinn| 3 

Проблема в том, если кто-то хочет видеть все TOYS (родительская категория), что является лучшим способом получить информацию из базы данных элементов? Единственный способ я знаю, как это сделать что-то вроде

SELECT * 
FROM items 
WHERE category_id = 2 
JOIN SELECT * 
    FROM items 
    WHERE category_id = 3 
    etc... 

Но если бы я был, как 10 категорий под игрушки, то я должен был бы сделать это присоединиться и запросов в 10 раз.

Есть ли лучший способ справиться с этим?

+0

Тех соединения является недопустимым синтаксисом SQL (и если вы исправить синтаксис, например, с помощью скобки вокруг второго выбора вы получите пустой результирующий набор); может быть, вы имеете в виду UNION? –

ответ

0

Предполагая, что вы знаете, идентификатор категории Игрушки, и ничто не находится в категории верхнего уровня игрушки:

SELECT * FROM items WHERE category_id IN (SELECT id FROM categories WHERE parent_id = 1) 
+0

И btw ничего не ДОЛЖНО быть в категории без листа - по тем же причинам, что и Хаар, для «никогда не подклассифицировать конкретный класс»; если вам нужна другая/разная подкатегория игрушек, сделайте ее ребенком игрушки, так что она находится на том же уровне, что и другие подкатегории. –

0
  1. Используйте IN operator.
  2. Использовать stored procedure.
  3. Оптимизируйте свои таблицы, чтобы лучше отражать, как они привыкли привыкать.
3

Я предполагаю, что вы знаете, как получить идентификационный номер, и это не вопрос. Кроме того, parent_id должен также быть FK ссылки id, и я хотел бы использовать NULL для верхнего слоя, а не 0.

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

SELECT * 
FROM items 
WHERE items.category_id IN (SELECT id FROM categories 
          WHERE categories.parent_id = 1 
          OR categories.id = 1); 

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

Procedure getItemsInCategory 
Input: @category_id integer 
Output: items rows 
{ 
    For each item in (SELECT * 
         FROM items 
         WHERE items.category_id = @category_id): 
     return the row; 

    For each id in (SELECT id 
        FROM categories 
        WHERE categories.parent_id = @category_id): 
     return the rows in getItemsInCategory(id); 
} 
4

Но если бы я был, как 10 категорий под игрушкой, то я должен был бы сделать это присоединиться и запросы в 10 раз. Есть ли лучший способ справиться с этим?

Да, есть способ хранения данных под названием «nested sets». Вставить данные немного сложнее, но просто выбрать целую многоуровневую ветку, используя один оператор select.

Кроме того, Celko написал a book об этой теме, с главой о вложенных множествах и других главах о других методах.

+0

+1 Почему-то я никогда не видел этого раньше, и это отлично. Первый результат (http://dev.mysql.com/tech-resources/articles/hierarchical-data.html) до сих пор является лучшим объяснением, которое я нашел, и он идеально подходит для иерархической модели данных. –

0

Я не знаком с MySQL, но вот как это сделать в TSQL (SQL SERVER), может быть, попытаться найти эквивалентный способ сделать это в MySQL?

1) Цикл по всем категориям, чтобы получить детей для конкретного пункта, в данном случае категории ид = 1

2) Фильтр элементов в том, что связанные с детьми в иерархии CTE (Common Таблица Expression) ,

With Hierarchy As 
(

SELECT id, name, parent_id 
from   categories 
where  id = 1 
UNION ALL 
SELECT  child.id, child.name, child.parent_id 
from   categories child 
inner join Hierarchy parent on child.parent_id = parent.id 
) 
SELECT * FROM items 
WHERE category_id IN 
(
    Select id 
    from Hierarchy 
) 
21

Вы хотите дать родительское ID:

Так предположим, что вы дали

set @parentId = 1 /*toys*/ 

select 
    * 
from 
    Items i 
inner join Categories c on c.id = i.categoryId 
where 
    c.parentId = @parentId 

Это даст вам детали вы хотите - с одной основной недостаток дизайна: он не обрабатывает множественным уровни иерархических категорий.

Допустим, у вас это ТАБЛИЦЫ:

*Categories table* 
id | name | parentId 
1 | Toys | 0 
2 | Dolls | 1 
3 | Bikes | 1 
4 | Models | 2 
5 | Act.Fig.| 2 
6 | Mountain| 3 
7 | BMX  | 3 

И Items:

*items table* 
item | category_id 
Barbie | 4 
GIJoe | 5 
Schwinn| 6 
Huffy | 7 

Единственный способ, чтобы получить все необходимые детали, это сделать автообъединение:

select 
    * 
from 
    Items i 
inner join Categories c on c.id = i.categoryId 
inner join Categories c2 on c.parentId = c2.id 
where 
    c2.parentId = @parentId 

Этот шаблон не масштабируется - так как вы можете иметь МНОГОКРАТНЫЕ уровни иерархии.

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

В дополнение к таблице категорий, вы построите вторую таблицу:

*CategoriesFlat table* The Name column is here only for readability 
id | name | parentId 
1 | Toys | 1 
----------------- 
2 | Dolls | 1 
2 | Dolls | 2 
----------------- 
4 | Models | 1 
4 | Models | 2 
4 | Models | 4 
5 | Act.Fig.| 1 
5 | Act.Fig.| 2 
5 | Act.Fig.| 5 
----------------- 
3 | Bikes | 1 
3 | Bikes | 3 
----------------- 
6 | Mountain| 1 
6 | Mountain| 3 
6 | Mountain| 6 
7 | BMX  | 1 
7 | BMX  | 3 
7 | BMX  | 7 

Таким образом, вы можете написать:

select 
    * 
from 
    Items i 
inner join CategoriesFlat c on c.id = i.categoryId 
where 
    c.parentId = @parentId 

и получить все соответствующие категории и товары.

Вот great slideshow about SQL anti-patterns и решения для них. (Иерархические данные в SQL - это анти-шаблон, но не унывайте - мы все сталкиваемся с этим)

+0

Хорошее слайд-шоу. – ChrisW

+0

Фантастический ответ! Если бы я мог, я бы поставил вам больше. Действительно хорошие вещи, чтобы знать. Отличные ресурсы тоже, спасибо за обмен. – EvilChookie

0

Я хотел бы поделиться с вами своей идеей.

Ограничение смежности Модель: Следуйте Managing Hierarchical Data in MySQL Как уже описано смежности модель имеет ограничения, которые вы должны знать уровень, прежде чем получить путь.

Использование вложенной модели: Но если вы преобразуете структуру данных вашей структуры данных в модель вложенного набора, вы все равно можете использовать самосоединение, чтобы получить дерево.

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

Спасибо :)

Смежные вопросы