Как я могу выбрать все конечные узлы в иерархии SQL под данным узлом?
Вопрос
У меня есть набор данных, который моделирует иерархию категорий.Корневая категория содержит набор категорий верхнего уровня.Каждая категория верхнего уровня содержит набор подкатегорий.
Каждая подкатегория имеет набор организаций.Данная организация может быть представлена в нескольких подкатегориях.
Конечными узлами этой иерархии являются организации.Организация потенциально может быть представлена в нескольких подкатегориях.
Данные хранятся в трех таблицах SQL:
organizations
organization_id organization_name
1 Org A
2 Org B
3 Org C
4 Org D
5 Org E
6 Org F
categories
category_id parent_id category_name
0 NULL Top Level Category
1 0 First Category
2 0 Second Category
3 1 Sub Category A
4 1 Sub Category B
5 1 Sub Category C
6 2 Sub Category D
organizations_categories -- Maps organizations to sub_categories
organization_id category_id
1 3
2 3
2 6
3 4
4 4
5 4
6 5
6 4
7 6
8 6
Я хотел бы иметь возможность выбрать список всех уникальных организаций в рамках заданной категории или подкатегории.
То, как я делаю это прямо сейчас, включает в себя сначала выяснение, какие подкатегории были запрошены, а затем перебор каждой sub_category в коде и выполнение select, чтобы все организации были сопоставлены с этой категорией.Результаты каждого выбора добавляются к массиву.Этот массив содержит дубликаты всякий раз, когда организация появляется в нескольких подкатегориях.
Я бы с удовольствием заменил этот клудж запросом, который может эффективно выбирать список различных организаций по идентификатору одной из категорий в иерархии.
Я разрабатываю это решение, используя PHP и MySQL.
Спасибо за ваше время и предложения.
Решение
Предполагая, что ваша иерархия всегда имеет глубину ровно в 3 уровня:
SELECT DISTINCT
O.organization_id,
O.organization_name
FROM
Categories CAT
INNER JOIN Categories SUB ON
SUB.parent_id = CAT.category_id
INNER JOIN Category_Organizations CO ON
CO.category_id = SUB.category_id
INNER JOIN Organizations O ON
O.organization_id = CO.organization_id
WHERE
CAT.category_id = @category_id
Вы можете изменить это на один уровень, чтобы разрешить вам передавать идентификатор подкатегории.Если на данный момент вы не знаете, есть ли у вас идентификатор категории или подкатегории, вы можете сделать следующее:
SELECT DISTINCT
O.organization_id,
O.organization_name
FROM
Categories CAT
LEFT OUTER JOIN Categories SUB ON
SUB.parent_id = CAT.category_id
INNER JOIN Category_Organizations CO ON
CO.category_id IN (CAT.category_id, SUB.category_id)
INNER JOIN Organizations O ON
O.organization_id = CO.organization_id
WHERE
CAT.category_id = @category_id
Если ваша иерархия может иметь неизвестное количество уровней (или вы думаете, что это может произойти в будущем), то проверьте Деревья и иерархии Джо Селко в SQL для Smarties для альтернативных способов моделирования иерархии.В любом случае, вероятно, это хорошая идея - сделать это.
Другие советы
Не уверен, позволит ли это ваша модель данных, но вы можете использовать один столбец индекса и двоичное дерево, чтобы легко хранить эту информацию в одной таблице "OrganizationTree".Также имеет то преимущество, что вы используете один запрос без изменений для поиска на уровне категории, подкатегории или организации (например.дайте мне все результаты по X подкатегории)
Надеюсь, это поможет.
Адам.