mysql - 嵌套集查询以检索每个节点的所有祖先

标签 mysql sql select sql-order-by nested-sets

我有一个 MySQL 查询,我认为它可以很好地检索每个节点的所有祖先,从顶级节点开始,向下到其直接节点。但是,当我向嵌套集中添加第 5 层时,它就坏了。

下面是示例表、查询和 SQL Fiddles:

四层嵌套集:

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);

查询:

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;

节点 Banana 的返回结果是 Food,Fruit,Yellow - 完美。你可以在这里看到这个SQL Fiddle - 4 Levels

当我在下面的 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);

Bacon_Sandwich 的返回结果是 Bacon,Food,Meat,Pork 顺序不对,应该是 Food,Meat,Pork,Bacon - 你可以在这里看到 SQL Fiddle - 5 Levels

我不确定发生了什么,因为我不太了解子查询。任何人都可以阐明这一点吗?

调查后编辑:

哇!!看起来写出所有这些并阅读有关使用 GROUP_CONCAT 进行排序的内容给了我一些灵感。

ORDER BY 添加到实际的 GROUP_CONCAT 函数并从子查询的末尾删除解决了这个问题。我现在收到节点 Bacon_SandwichFood,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;

虽然我仍然不知道为什么。在子查询末尾有 ORDER BY 适用于 4 个级别但不适用于 5 个级别?!?!

如果有人可以解释问题是什么以及为什么移动 ORDER BY 可以解决问题,我将不胜感激。

最佳答案

首先了解您有一个 implicit GROUP BY 很重要

If you use a group function in a statement containing no GROUP BY clause, it is equivalent to grouping on all rows.

为了使这一点更易于理解,我将省略子查询并将问题简化为香蕉。香蕉是集合 [10, 11]。正确排序的祖先是那些:

SELECT "banana" as node, GROUP_CONCAT(title ORDER by `left`)
  FROM Tree WHERE `left` < 10 AND `right` > 11
  GROUP BY node;

ORDER BY 必须在 GROUP_CONCAT() 中,因为您希望聚合函数进行排序。 ORDER BY 按聚合结果(即 GROUP_CONCAT() 的结果)外部排序。它一直工作到 4 级的事实只是运气。 ORDER BY 对聚合函数没有影响。无论是否使用 ORDER BY,您都会得到相同的结果:

SELECT GROUP_CONCAT(title)
  FROM Tree WHERE `left` < 10 AND `right` > 11
  /* ORDER BY `left` */

这可能有助于理解什么 SELECT GROUP_CONCAT(title ORDER BY left) FROM Tree WHERE … ORDER BY left 做:

  1. 获取一个选择(WHERE),它以未定义的顺序产生三行:

    ("Food")
    ("Yellow")
    ("Fruit")
    
  2. 将结果聚合到一行(隐式 GROUP BY)以便能够使用聚合函数:

    (("Food","Yellow", "Fruit"))
    
  3. 在其上触发聚合函数 (GROUP_CONCAT(title, ORDER BY link))。 IE。按链接排序,然后连接:

    ("Food,Fruit,Yellow")
    
  4. 现在终于对结果进行排序 (ORDER BY)。因为它只有一行,所以排序不会改变任何内容。

    ("Food,Fruit,Yellow")
    

关于mysql - 嵌套集查询以检索每个节点的所有祖先,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20993518/

相关文章:

php - PDO query() 或 fetch() 不工作

php - 使用 PHP 或 SQL 按出现优先级对 MYSQL 结果进行排序

mysql - ColdFusion cfdump 不显示对 Lucee 版本 5.2.1.9 的查询

sql - 在 SQL 中将多行合并为具有单独列的单行

javascript - jQuery - 将函数应用于具有相同类名的多个选择元素

php警告为什么?无法解决

sql - 在 SQL Server 2008 R2 中引用前一行值进行算术计算

sql - 使用 A Union B 与 Where A OR B 的 Postgres 性能

MySQL:无法从特定分区中选择记录?

MySQL数据库查询返回空结果