简体   繁体   English

嵌套Set Query以检索每个节点的所有祖先

[英]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

  1. Get a selection ( WHERE ) which results in three rows in an undefined order: 获取一个选择( WHERE ),导致三行以未定义的顺序:

    \n("Food") (“餐饮”)\n("Yellow") (“黄色”)\n("Fruit") (“水果”)\n
  2. 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
  3. 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
  4. 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 . 您可以使用JOINSUB-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因为按升序排列BaconFood来得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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM