2015-12-15 2 views
2

Я работаю с категориями eBay. Я ищу наиболее эффективный способ получить список совпадающих «категорий листьев» (только верхнего уровня) с их полной панировкой термин, который соответствует части названия категорииРекурсивные SELECT категории сухари с LIKE В одном запросе

sqlfiddle Я работаю с.

Предполагая, что у меня было только две категории Leaf (Post-1900 и Pre-1900)

Вот их сухари

Antiques > Antique Clocks > Bracket Clocks > Post-1900 
Antiques > Antique Clocks > Bracket Clocks > Pre-1900 

Если термин «кронштейн» используется, то результаты будут содержать две строки , по одному для каждой панировки, но если «Post-19» - это термин, тогда будет возвращена только одна строка. Каждая строка должна содержать два поля: CategoryID и breadcrumb, категория Категории должна быть «категорией листа».

CREATE TABLE `ebay_categories` (
    `CategoryID` int(11) DEFAULT NULL, 
    `CategoryName` varchar(20) DEFAULT NULL, 
    `CategoryParentID` int(11) DEFAULT NULL, 
    `CategoryLevel` int(11) DEFAULT NULL 
); 

insert into `ebay_categories` (`CategoryID`, `CategoryName`, `CategoryParentID`, `CategoryLevel`) values('20081','Antiques','20081','1'); 
insert into `ebay_categories` (`CategoryID`, `CategoryName`, `CategoryParentID`, `CategoryLevel`) values('13851','Antique Clocks','20081','2'); 
insert into `ebay_categories` (`CategoryID`, `CategoryName`, `CategoryParentID`, `CategoryLevel`) values('100904','Bracket Clocks','13851','3'); 
insert into `ebay_categories` (`CategoryID`, `CategoryName`, `CategoryParentID`, `CategoryLevel`) values('96762','Post-1900','100904','4'); 
insert into `ebay_categories` (`CategoryID`, `CategoryName`, `CategoryParentID`, `CategoryLevel`) values('66840','Pre-1900','100904','4'); 

Я пытаюсь реализовать один и тот же метод используется here но были терпеть неудачу.

SELECT LeafID as CategoryID, GROUP_CONCAT(CategoryName SEPARATOR ' > ') AS breadcrumb FROM (

    (SELECT CategoryID as LeafID AS 

     SELECT * from ebay_categories WHERE CategoryName LIKE '%Antiq%') AS c 

) AS b GROUP BY LeafID 

) AS a ORDER BY breadcrumb ASC Limit 20 

ответ

0

SQL может только (не будучи «умным» с использованием хранимой процедуры и т. Д.) Когда-либо возвращать фиксированное количество столбцов.

(FYI - ответ, на который вы ссылаетесь, IMHO «умный». Он делает некоторые действительно очевидные вещи с переменными сеанса и т. Д., Которые, если они не ухудшают производительность, ухудшают читаемость, поэтому я собираюсь попытаться ответить на ваш вопрос под другим углом.)

Таким образом, вы можете исправить (hardcode) «глубину» палитры и с фиксированным числом операторов JOIN все станет очень простым.

Я предполагаю, что глубины крошки должны быть между 1 и бесконечностью? то есть другой «сбор» предметов может быть подан под меньшей глубиной категорий?

В этом случае ваш GROUP_CONCAT, вероятно, является частью решения, поскольку он эмулирует «переменные столбцы» в SQL. (Он возвращается как 1 столбец, но может содержать гибкое количество значений с разделителями внутри.)

Ваша проблема заключается в том, что по-прежнему природа SQL может присоединяться только к одной таблице в другую (одиночную) таблицу за JOIN. Ваша структура данных палитры хорошо нормализована и предполагает объединение на каждую подкатегорию своего родителя.

You может попытаться динамически построить SQL - но это, вероятно, сгорит вас. Вероятно, вы остаетесь с двумя «очевидными» вариантами:

  1. Сохраненная процедура. (Исходя из базового SQL.)
  2. Измените свою схему. (Решить эту проблему, когда данные хранятся, а не извлекаются.)

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

Я рад дать вам руководство по этому вопросу, но я не буду делать это как часть этого ответа (если не запрошено), потому что я уверен, что производительность будет достаточно плоха, что вы в конечном счете не захотите использовать его.

Другой «основной» вариант заключается в том, чтобы перестроить схему. В этом случае уровень нормализации, который вы достигли, делает вещи чрезмерно сложными. Это хорошо «академически», и это хорошо для дискового пространства. Но это не самореализация для решения вашей проблемы очень хорошо!

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` 
    ; 
+0

Добавлено обновление об ошибке - см. Нижнюю часть ответа. – wally