我正在使用eBay类别,我正在寻找一种最有效的方法来检索匹配的"叶子类别列表#34;(仅限顶级),当给出一个术语时,其完整的面包屑匹配类别名称的一部分
这是我一直在使用的sqlfiddle。
假设我只有两个Leaf类别(1900年后和1900年前)
这是他们的面包屑
Antiques > Antique Clocks > Bracket Clocks > Post-1900
Antiques > Antique Clocks > Bracket Clocks > Pre-1900
如果术语" Bracket"然后使用结果将包含两行,每个面包屑一行,但是如果" Post-19"这个术语只返回一行。
每行应包含两个字段CategoryID
和breadcrumb
,CategoryID必须是" leaf类别"。
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 :(得分:0)
SQL只能(没有"聪明"使用存储过程等)返回固定数量的列。
(仅供参考 - 您链接到的答案,恕我直言,#34;聪明"。它正在使用会话变量等做一些非常明显的事情,如果他们没有&#39 ;伤害表现会伤害可读性 - 所以我会尝试从不同角度回答你的问题。)
因此,您可以修复(硬编码)面包屑" depth",并且使用固定数量的JOIN语句,一切都变得非常简单。
我假设面包屑深度是1到无穷大之间的任何东西?即另一个"集合"项目可以在较小深度的类别下提交?
在这种情况下,您的GROUP_CONCAT
可能是解决方案的一部分,因为它可以模拟"变量列数"在SQL中。 (它返回1列,但内部可以包含灵活数量的分隔值。)
您的问题仍然是SQL的性质仍然只能根据JOIN
语句将一个表连接到另一个(单个)表。您的面包屑数据结构已经很好地规范化,并且假设每个子类别都是其父级的连接。
你可以尝试动态构建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
添加某种排序,以确保它不会做出隐含意外的事情。您可以(例如)为此目的维护级别ID。
<强>更新强>
一旦你理解了我上面所做的事情,就应该用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`
;