Я создаю базу данных для сайта электронной коммерции, которая имеет вложенные категории, и я использую модифицированный алгоритм обхода предварительного заказа. У меня вопрос, как я могу получить доступ ко всем узлам на уровне 2, т.е. статьи, портфолио, контакт
Статья явно не говорит вам, как получить все узлы с одного уровня. Но если вы внимательно прочитаете его, он расскажет вам, как сделать больше -> получить счетчик глубины для каждой категории. Тогда все, что вам нужно сделать, это отфильтровать по этой глубине.
SELECT node.name, (COUNT(parent.name) - 1) AS depth
FROM nested_category AS node, nested_category AS parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt
GROUP BY node.name
HAVING depth = 1
ORDER BY node.lft;
РЕДАКТИРОВАТЬ (что происходит):
Для того, чтобы использовать lft
а также rgt
колонны nested_category
Таблица, которую мы должны выбрать таблицу дважды.
SELECT *
FROM nested_category AS node, nested_category AS parent
если вы проверите этот запрос, вы обнаружите, что для каждой строки в nested_category
мы снова выбираем все строки. Итак, что мы хотим сейчас, это удалить все строки из первой таблицы (ту, которую мы назвали AS node
) где они не дети их parent
, Вот почему мы фильтруем с WHERE node.lft BETWEEN parent.lft AND parent.rgt
Я хочу упомянуть, что этот запрос:
SELECT *
FROM nested_category AS node, nested_category AS parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt
ORDER BY node.lft;
равно
SELECT *
FROM nested_category AS node
LEFT JOIN nested_category AS parent ON (node.lft BETWEEN parent.lft AND parent.rgt)
ORDER BY node.lft;
Так что теперь у нас есть все дети с их родителями + 1 (из-за того, как мы фильтруем, каждый child
принадлежат себе)
+-------------+----------------------+-----+-----+-------------+----------------------+------+------+
| category_id | name | lft | rgt | category_id | name | lft | rgt |
+-------------+----------------------+-----+-----+-------------+----------------------+------+------+
| 1 | ELECTRONICS | 1 | 20 | 1 | ELECTRONICS | 1 | 20 |
| 2 | TELEVISIONS | 2 | 9 | 1 | ELECTRONICS | 1 | 20 |
| 2 | TELEVISIONS | 2 | 9 | 2 | TELEVISIONS | 2 | 9 |
| 3 | TUBE | 3 | 4 | 1 | ELECTRONICS | 1 | 20 |
| 3 | TUBE | 3 | 4 | 3 | TUBE | 3 | 4 |
| 3 | TUBE | 3 | 4 | 2 | TELEVISIONS | 2 | 9 |
| 4 | LCD | 5 | 6 | 2 | TELEVISIONS | 2 | 9 |
| 4 | LCD | 5 | 6 | 1 | ELECTRONICS | 1 | 20 |
| 4 | LCD | 5 | 6 | 4 | LCD | 5 | 6 |
| 5 | PLASMA | 7 | 8 | 1 | ELECTRONICS | 1 | 20 |
| 5 | PLASMA | 7 | 8 | 5 | PLASMA | 7 | 8 |
| 5 | PLASMA | 7 | 8 | 2 | TELEVISIONS | 2 | 9 |
| 6 | PORTABLE ELECTRONICS | 10 | 19 | 6 | PORTABLE ELECTRONICS | 10 | 19 |
| 6 | PORTABLE ELECTRONICS | 10 | 19 | 1 | ELECTRONICS | 1 | 20 |
| 7 | MP3 PLAYERS | 11 | 14 | 7 | MP3 PLAYERS | 11 | 14 |
| 7 | MP3 PLAYERS | 11 | 14 | 1 | ELECTRONICS | 1 | 20 |
| 7 | MP3 PLAYERS | 11 | 14 | 6 | PORTABLE ELECTRONICS | 10 | 19 |
| 8 | FLASH | 12 | 13 | 1 | ELECTRONICS | 1 | 20 |
| 8 | FLASH | 12 | 13 | 8 | FLASH | 12 | 13 |
| 8 | FLASH | 12 | 13 | 6 | PORTABLE ELECTRONICS | 10 | 19 |
| 8 | FLASH | 12 | 13 | 7 | MP3 PLAYERS | 11 | 14 |
| 9 | CD PLAYERS | 15 | 16 | 1 | ELECTRONICS | 1 | 20 |
| 9 | CD PLAYERS | 15 | 16 | 9 | CD PLAYERS | 15 | 16 |
| 9 | CD PLAYERS | 15 | 16 | 6 | PORTABLE ELECTRONICS | 10 | 19 |
| 10 | 2 WAY RADIOS | 17 | 18 | 1 | ELECTRONICS | 1 | 20 |
| 10 | 2 WAY RADIOS | 17 | 18 | 10 | 2 WAY RADIOS | 17 | 18 |
| 10 | 2 WAY RADIOS | 17 | 18 | 6 | PORTABLE ELECTRONICS | 10 | 19 |
+-------------+----------------------+-----+-----+-------------+----------------------+------+------+
Следующий шаг — получить счетчик глубины. Чтобы сделать это, мы должны сгруппировать по каждому ребенку (пример использует GROUP BY node.name
но это также можно сделать на node.category_id
и посчитать количество parents
— 1 на каждую группу (COUNT(parent.name) - 1) AS depth
(его также хорошо использовать parent.category_id
вместо)
Так делаю
SELECT node.*, (COUNT(parent.category_id) - 1) AS depth
FROM nested_category AS node
LEFT JOIN nested_category AS parent ON (node.lft BETWEEN parent.lft AND parent.rgt)
GROUP BY node.category_id
ORDER BY node.lft;
мы получаем это
+-------------+----------------------+-----+-----+-------+
| category_id | name | lft | rgt | depth |
+-------------+----------------------+-----+-----+-------+
| 1 | ELECTRONICS | 1 | 20 | 0 |
| 2 | TELEVISIONS | 2 | 9 | 1 |
| 3 | TUBE | 3 | 4 | 2 |
| 4 | LCD | 5 | 6 | 2 |
| 5 | PLASMA | 7 | 8 | 2 |
| 6 | PORTABLE ELECTRONICS | 10 | 19 | 1 |
| 7 | MP3 PLAYERS | 11 | 14 | 2 |
| 8 | FLASH | 12 | 13 | 3 |
| 9 | CD PLAYERS | 15 | 16 | 2 |
| 10 | 2 WAY RADIOS | 17 | 18 | 2 |
+-------------+----------------------+-----+-----+-------+
И теперь последний шаг, чтобы сказать, что мы хотим только эти записи, которые имеют глубину = 1 (HAVING depth = 1
, HAVING
используется здесь, потому что применяется после агрегатов (и поэтому может фильтровать по агрегатам))
SELECT node.*, (COUNT(parent.category_id) - 1) AS depth
FROM nested_category AS node
LEFT JOIN nested_category AS parent ON (node.lft BETWEEN parent.lft AND parent.rgt)
GROUP BY node.category_id
HAVING depth = 1
ORDER BY node.lft;
+-------------+----------------------+-----+-----+-------+
| category_id | name | lft | rgt | depth |
+-------------+----------------------+-----+-----+-------+
| 2 | TELEVISIONS | 2 | 9 | 1 |
| 6 | PORTABLE ELECTRONICS | 10 | 19 | 1 |
+-------------+----------------------+-----+-----+-------+
Надеюсь, теперь все понятно. Снова извините за мой плохой английский, если я сделал несколько ошибок.
Других решений пока нет …