SQL может только (не будучи «умным» с использованием хранимой процедуры и т. Д.) Когда-либо возвращать фиксированное количество столбцов.
(FYI - ответ, на который вы ссылаетесь, IMHO «умный». Он делает некоторые действительно очевидные вещи с переменными сеанса и т. Д., Которые, если они не ухудшают производительность, ухудшают читаемость, поэтому я собираюсь попытаться ответить на ваш вопрос под другим углом.)
Таким образом, вы можете исправить (hardcode) «глубину» палитры и с фиксированным числом операторов JOIN все станет очень простым.
Я предполагаю, что глубины крошки должны быть между 1 и бесконечностью? то есть другой «сбор» предметов может быть подан под меньшей глубиной категорий?
В этом случае ваш GROUP_CONCAT
, вероятно, является частью решения, поскольку он эмулирует «переменные столбцы» в SQL. (Он возвращается как 1 столбец, но может содержать гибкое количество значений с разделителями внутри.)
Ваша проблема заключается в том, что по-прежнему природа SQL может присоединяться только к одной таблице в другую (одиночную) таблицу за JOIN
. Ваша структура данных палитры хорошо нормализована и предполагает объединение на каждую подкатегорию своего родителя.
You может попытаться динамически построить SQL - но это, вероятно, сгорит вас. Вероятно, вы остаетесь с двумя «очевидными» вариантами:
- Сохраненная процедура. (Исходя из базового SQL.)
- Измените свою схему. (Решить эту проблему, когда данные хранятся, а не извлекаются.)
Хранимых процедуры могут решить эту проблему в ряде способов - один очевидный вариант состоит в перебирать построения каждые хлебную крошку процедурно, хранения досягаемости во временной таблице , затем, наконец, выбрав всю временную таблицу.
Я рад дать вам руководство по этому вопросу, но я не буду делать это как часть этого ответа (если не запрошено), потому что я уверен, что производительность будет достаточно плоха, что вы в конечном счете не захотите использовать его.
Другой «основной» вариант заключается в том, чтобы перестроить схему. В этом случае уровень нормализации, который вы достигли, делает вещи чрезмерно сложными. Это хорошо «академически», и это хорошо для дискового пространства. Но это не самореализация для решения вашей проблемы очень хорошо!
Denormalising имеет еще один важный компромисс. У вас будет больше сложностей при изменении данных в схеме. Я бы рекомендовал начать с написания подпрограммы, которая «перестраивает» данные (если вы примете этот подход), потому что в противном случае ситуация перестанет синхронизироваться, и вы будете навсегда пытаться выяснить, что пошло не так. (Я говорю из опыта.)
Для каждой соответствующей записи (вы сравниваете пользовательский ввод CategoryName
) вы хотите вернуть и быть в состоянии группы все, что предшествует в дереве. И без «умных» вещей.
Один из (нескольких) методов денормализации будет состоять в том, чтобы поддерживать длинный список листьев от предков до depth * width
. (Как я уже сказал, это не хранение эффективным Вы должны оценить, если это является проблемой в сценарии производства.). Для примера данных, будет выглядеть следующим образом:
+------------+--------+
| AncestorId | LeafId |
+------------+--------+
| 20081 | 66840 |
| 20081 | 96762 |
| 13851 | 66840 |
| 13851 | 96762 |
| 100904 | 66840 |
| 100904 | 96762 |
| 66840 | 66840 |
| 96762 | 96762 |
+------------+--------+
Таким образом, теперь вы можете сделать что-то вроде этого:
CREATE TABLE `tree_branches` (
`AncestorId` int(11) NOT NULL,
`LeafId` int(11) NOT NULL
);
INSERT INTO `tree_branches` SET `AncestorId`=20081, `LeafId`=66840;
INSERT INTO `tree_branches` SET `AncestorId`=20081, `LeafId`=96762;
INSERT INTO `tree_branches` SET `AncestorId`=13851, `LeafId`=66840;
INSERT INTO `tree_branches` SET `AncestorId`=13851, `LeafId`=96762;
INSERT INTO `tree_branches` SET `AncestorId`=100904, `LeafId`=66840;
INSERT INTO `tree_branches` SET `AncestorId`=100904, `LeafId`=96762;
INSERT INTO `tree_branches` SET `AncestorId`=66840, `LeafId`=66840;
INSERT INTO `tree_branches` SET `AncestorId`=96762, `LeafId`=96762;
SELECT
GROUP_CONCAT(`breadCrumbCategories`.`CategoryName` SEPARATOR " > ")
FROM `ebay_categories` AS `matchedCategory`
INNER JOIN `tree_branches` AS `matchedCategoryLeaves` ON (`matchedCategoryLeaves`.`AncestorId` = `matchedCategory`.`categoryId`)
INNER JOIN `tree_branches` AS `breadCrumbs` ON (`breadCrumbs`.`LeafId` = `matchedCategoryLeaves`.`LeafId`)
INNER JOIN `ebay_categories` AS `breadCrumbCategories` ON (`breadCrumbCategories`.`CategoryId` = `breadCrumbs`.`ancestorId`)
WHERE
`matchedCategory`.`CategoryName` LIKE "Post%"
GROUP BY
`breadCrumbs`.`LeafId`
;
Вы должны добавить какой-то сортировки для GROUP_BY
, чтобы убедиться, что он не делает что-то неявно неожиданное. Вы можете (например) поддерживать идентификатор уровня для этой цели.
Update: После того, как вы поняли, что я сделал выше, вы должны проверить его с LIKE 'Ant%'
и наблюдать за ошибочный вывод. Добавьте второй пункт GROUP BY
и DISTINCT
, чтобы решить проблему, вызванную пользовательскими запросами, соответствующими нескольким крохам, которые являются предками одного и того же листа.
SELECT
DISTINCT GROUP_CONCAT(`breadCrumbCategories`.`CategoryName` SEPARATOR " > ")
FROM `ebay_categories` AS `matchedCategory`
INNER JOIN `tree_branches` AS `matchedCategoryLeaves` ON (`matchedCategoryLeaves`.`AncestorId` = `matchedCategory`.`categoryId`)
INNER JOIN `tree_branches` AS `breadCrumbs` ON (`breadCrumbs`.`LeafId` = `matchedCategoryLeaves`.`LeafId`)
INNER JOIN `ebay_categories` AS `breadCrumbCategories` ON (`breadCrumbCategories`.`CategoryId` = `breadCrumbs`.`ancestorId`)
WHERE
`matchedCategory`.`CategoryName` LIKE "An%"
GROUP BY
`breadCrumbs`.`LeafId`,
`matchedCategory`.`CategoryId`
;
Добавлено обновление об ошибке - см. Нижнюю часть ответа. – wally