[英]Nested Set Query to retrieve all ancestors of each node
I have a MySQL query that I thought was working fine to retrieve all the ancestors of each node, starting from the top node, down to its immediate node. 我有一个MySQL查询,我认为它可以很好地检索每个节点的所有祖先,从顶部节点开始,直到它的直接节点。 However when I added a 5th level to the nested set, it broke.
但是当我在嵌套集中添加第5级时,它就破了。
Below are example tables, queries and SQL Fiddles: 下面是示例表,查询和SQL Fiddles:
Four Level Nested Set: 四级嵌套集:
CREATE TABLE Tree
(title varchar(20) PRIMARY KEY,
`tree` int,
`left` int,
`right` int);
INSERT Tree
VALUES
("Food", 1, 1, 18),
('Fruit', 1, 2, 11),
('Red', 1, 3, 6),
('Cherry', 1, 4, 5),
('Yellow', 1, 7, 10),
('Banana', 1, 8, 9),
('Meat', 1, 12, 17),
('Beef', 1, 13, 14),
('Pork', 1, 15, 16);
The Query: 查询:
SELECT t0.title node
,(SELECT GROUP_CONCAT(t2.title)
FROM Tree t2
WHERE t2.left<t0.left AND t2.right>t0.right
ORDER BY t2.left) ancestors
FROM Tree t0
GROUP BY t0.title;
The returned result for node Banana
is Food,Fruit,Yellow
- Perfect. 节点
Banana
的返回结果是Food,Fruit,Yellow
- 完美。 You can see this here SQL Fiddle - 4 Levels 你可以在这里看到SQL Fiddle - 4 Levels
When I run the same query on the 5 level table below, the 5th level nodes come back in the wrong order: 当我在下面的5级表上运行相同的查询时,第5级节点以错误的顺序返回:
CREATE TABLE Tree
(title varchar(20) PRIMARY KEY,
`tree` int,
`left` int,
`right` int);
INSERT Tree
VALUES
("Food", 1, 1, 24),
('Fruit', 1, 2, 13),
('Red', 1, 3, 8),
('Cherry', 1, 4, 7),
('Cherry_pie', 1, 5, 6),
('Yellow', 1, 9, 12),
('Banana', 1, 10, 11),
('Meat', 1, 14, 23),
('Beef', 1, 15, 16),
('Pork', 1, 17, 22),
('Bacon', 1, 18, 21),
('Bacon_Sandwich', 1, 19, 20);
The returned result for Bacon_Sandwich
is Bacon,Food,Meat,Pork
which is not the right order, it should be Food,Meat,Pork,Bacon
- You can see this here SQL Fiddle - 5 Levels Bacon_Sandwich
的返回结果是Bacon,Food,Meat,Pork
,这不是正确的顺序,它应该是Food,Meat,Pork,Bacon
- 你可以在这里看到这个SQL小提琴 - 5级
I am not sure what is happening because I don't really understand subqueries well enough. 我不确定发生了什么,因为我不太了解子查询。 Can anyone shed any light on this?
任何人都可以对此有所了解吗?
EDIT AFTER INVESTIGATION: 调查后编辑:
Woah!! 哇!! Looks like writing all this out and reading up about ordering with
GROUP_CONCAT
gave me some inspiration. 看起来写这一切并阅读有关
GROUP_CONCAT
订购的信息给了我一些灵感。
Adding ORDER BY
to the actual GROUP_CONCAT
function and removing from the end of the subquery solved the issue. 将
ORDER BY
添加到实际的GROUP_CONCAT
函数并从子查询的末尾删除解决了该问题。 I now receive Food,Meat,Pork,Bacon
for the node Bacon_Sandwich
我现在收到节点
Bacon_Sandwich
Food,Meat,Pork,Bacon
SELECT t0.title node
,(SELECT GROUP_CONCAT(t2.title ORDER BY t2.left)
FROM Tree t2
WHERE t2.left<t0.left AND t2.right>t0.right
) ancestors
FROM Tree t0
GROUP BY t0.title;
I still have no idea why though. 我仍然不知道为什么。 Having
ORDER BY
at the end of the subquery works for 4 levels but not for 5?!?! 子查询结束时的
ORDER BY
适用于4级但不适用于5级!?!
If someone could explain what the issue is and why moving the ORDER BY
fixes it, I'd be most grateful. 如果有人可以解释问题是什么以及为什么移动
ORDER BY
修复它,我将非常感激。
First it's important to understand that you have an implicit GROUP BY
首先,了解您有一个隐含的
GROUP BY
非常重要
If you use a group function in a statement containing no GROUP BY clause, it is equivalent to grouping on all rows.
如果在不包含GROUP BY子句的语句中使用组函数,则它等效于对所有行进行分组。
To make the point more understandable I'll leave out subqueries and reduce the problem to the banana. 为了使这一点更容易理解,我将省略子查询并将问题减少到香蕉。 Banana is the set [10, 11].
香蕉是集[10,11]。 The correct sorted ancestors are those:
正确排序的祖先是:
SELECT "banana" as node, GROUP_CONCAT(title ORDER by `left`)
FROM Tree WHERE `left` < 10 AND `right` > 11
GROUP BY node;
The ORDER BY
must be in GROUP_CONCAT()
as you want the aggregation function to sort. ORDER BY
必须在GROUP_CONCAT()
因为您希望对聚合函数进行排序。 ORDER BY
outside sorts by the aggregated results (ie the result of GROUP_CONCAT()
). ORDER BY
由聚合结果排序(即GROUP_CONCAT()
的结果)。 The fact that it worked until level 4 is just luck. 直到4级工作的事实才是运气。
ORDER BY
has no effect on an aggregate function. ORDER BY
对聚合函数没有影响。 You would get the same results with or without the ORDER BY
: 无论是否有
ORDER BY
您都会得到相同的结果:
SELECT GROUP_CONCAT(title)
FROM Tree WHERE `left` < 10 AND `right` > 11
/* ORDER BY `left` */
It might help to understand what SELECT GROUP_CONCAT(title ORDER BY left) FROM Tree WHERE … ORDER BY left
does: 它可能有助于理解
SELECT GROUP_CONCAT(title ORDER BY left) FROM Tree WHERE … ORDER BY left
:
Get a selection ( WHERE
) which results in three rows in an undefined order: 获取一个选择(
WHERE
),导致三行以未定义的顺序:
\n("Food")(“餐饮”)\n("Yellow")
(“黄色”)\n("Fruit")
(“水果”)\n
Aggregate the result into one row (implicit GROUP BY
) in order to be able to use an aggregate function: 将结果聚合到一行(隐式
GROUP BY
)以便能够使用聚合函数:
\n(("Food","Yellow", "Fruit"))((“食物”,“黄色”,“水果”))\n
Fire the aggregate function ( GROUP_CONCAT(title, ORDER BY link)
) on it. GROUP_CONCAT(title, ORDER BY link)
聚合函数( GROUP_CONCAT(title, ORDER BY link)
)。 Ie order by link and then concatenate: 即按链接顺序然后连接:
\n("Food,Fruit,Yellow")( “食品,水果,黄色”)\n
And now finally it sorts that result ( ORDER BY
). 现在终于对结果进行排序(
ORDER BY
)。 As it's only one row, sorting changes nothing. 因为它只有一行,所以排序不会改变。
\n("Food,Fruit,Yellow")( “食品,水果,黄色”)\n
You can get the result using JOIN
or SUB-QUERY
. 您可以使用
JOIN
或SUB-QUERY
获取结果。
Using JOIN: 使用JOIN:
SELECT t0.title node, GROUP_CONCAT(t2.title ORDER BY t2.left) ancestors
FROM Tree t0
LEFT JOIN Tree t2 ON t2.left < t0.left AND t2.right > t0.right
GROUP BY t0.title;
Check this SQL FIDDLE DEMO 检查这个SQL FIDDLE DEMO
Using SUB-QUERY: 使用SUB-QUERY:
SELECT t0.title node,
(SELECT GROUP_CONCAT(t2.title ORDER BY t2.left)
FROM Tree t2 WHERE t2.left<t0.left AND t2.right>t0.right) ancestors
FROM Tree t0
GROUP BY t0.title;
Check this SQL FIDDLE DEMO 检查这个SQL FIDDLE DEMO
OUTPUT OUTPUT
| NODE | ANCESTORS |
|----------------|-----------------------|
| Bacon | Food,Meat,Pork |
| Bacon_Sandwich | Food,Meat,Pork,Bacon |
| Banana | Food,Fruit,Yellow |
| Beef | Food,Meat |
| Cherry | Food,Fruit,Red |
| Cherry_pie | Food,Fruit,Red,Cherry |
| Food | (null) |
| Fruit | Food |
| Meat | Food |
| Pork | Food,Meat |
| Red | Food,Fruit |
| Yellow | Food,Fruit |
In your sub query you had used ORDER BY
after WHERE
clause which won't affect the output. 在子查询中,您在
WHERE
子句之后使用了ORDER BY
,这不会影响输出。 By default GROUP_CONCAT() function will orders the output string in ascending order of column value. 默认情况下, GROUP_CONCAT()函数将按列值的升序对输出字符串进行排序。 It won't consider you explicit ORDER BY clause.
它不会考虑您显式的ORDER BY子句。
If you check your output of first query which returns the data in ascending order of title column. 如果检查第一个查询的输出,它以标题列的升序返回数据。 So the returned result for node
Banana
is Food,Fruit,Yellow
. 所以节点
Banana
的返回结果是Food,Fruit,Yellow
。
But in your second result for Bacon_Sandwich
is Bacon,Food,Meat,Pork
because in ascending order Bacon
comes first than Food
will come. 但在你的
Bacon_Sandwich
的第二个结果是Bacon,Food,Meat,Pork
因为按升序排列Bacon
比Food
来得Bacon_Sandwich
。
If you want to order the result based on left
column than you have to specify ORDER BY
inside the GROUP_CONCAT()
function as above. 如果您想订购基于结果
left
的列比你要指定ORDER BY
里面GROUP_CONCAT()
作为上述功能。 Check my both queries. 检查我的两个查询。
I prefer that you use JOIN
instead of SUB-QUERY
for improving performance. 我更喜欢使用
JOIN
而不是SUB-QUERY
来提高性能。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.