【问题标题】:How to generate a tree view from this result set based on Tree Traversal Algorithm?如何基于树遍历算法从这个结果集中生成树视图?
【发布时间】:2011-04-08 00:03:38
【问题描述】:

我有这张桌子:

CREATE TABLE `categories` (
  `id` int(11) NOT NULL auto_increment,
  `category_id` int(11) default NULL,
  `root_id` int(11) default NULL,
  `name` varchar(100) collate utf8_unicode_ci NOT NULL,
  `lft` int(11) NOT NULL,
  `rht` int(11) NOT NULL,
  PRIMARY KEY  (`id`),
  KEY `category_id` (`category_id`),
  KEY `lft` (`lft`,`rht`),
  KEY `root_id` (`root_id`)
) 

基于这个问题: Getting a modified preorder tree traversal model (nested set) into a <ul>

不同之处在于我在一张桌子上有很多树。每行都有一个外键,表示其父级和顶级父级:category_id 和 root_id。此外,我还有基于此示例的 lft 和 rht 字段: http://articles.sitepoint.com/article/hierarchical-data-database/2

基于此行:

INSERT INTO `categories` VALUES(1, NULL, NULL, 'Fruits', 1, 14);
INSERT INTO `categories` VALUES(2, 1, 1, 'Apple', 2, 3);
INSERT INTO `categories` VALUES(3, 1, 1, 'Orange', 4, 9);
INSERT INTO `categories` VALUES(4, 3, 1, 'Orange Type 1', 5, 6);
INSERT INTO `categories` VALUES(5, 3, 1, 'Orange Type 2', 7, 8);
INSERT INTO `categories` VALUES(6, 1, 1, 'Pear', 10, 11);
INSERT INTO `categories` VALUES(7, 1, 1, 'Banana', 12, 13);
INSERT INTO `categories` VALUES(8, NULL, NULL, 'Eletronics', 1, 14);
INSERT INTO `categories` VALUES(9, 8, 8, 'Cell Phones', 2, 3);
INSERT INTO `categories` VALUES(10, 8, 8, 'Computers', 4, 9);
INSERT INTO `categories` VALUES(11, 10, 8, 'PC', 5, 6);
INSERT INTO `categories` VALUES(12, 10, 8, 'MAC', 7, 8);
INSERT INTO `categories` VALUES(13, 8, 8, 'Printers', 10, 11);
INSERT INTO `categories` VALUES(14, 8, 8, 'Cameras', 12, 13);

如何构建一个表示这棵树的有序列表?

使用下面的sql:

SELECT c. * , (COUNT( p.id ) -1) AS depth
FROM `categorias` AS p
CROSS JOIN categories AS c
WHERE (
c.lft
BETWEEN p.lft
AND p.rht
)
GROUP BY c.id
ORDER BY c.lft;

我得到了这个结果:

如您所见,我也需要按 root_id 排序,以便生成正确的树。

另外,在得到树之后,有没有办法按名称对每个节点进行排序?

【问题讨论】:

    标签: php sql mysql html modified-preorder-tree-t


    【解决方案1】:

    如您所见,我也需要按 root_id 排序,以便生成正确的树。

    在构建嵌套树模型时,切勿在 lftrgt 上重复。事实上,您应该声明它们是唯一的。

    在您的数据模型中,类别 18 的集合重叠。比如说,114 用于项目 18

    用这些值替换它们:

    INSERT INTO `categories` VALUES(1, NULL, NULL, 'Fruits', 1, 14);
    INSERT INTO `categories` VALUES(2, 1, 1, 'Apple', 2, 3);
    INSERT INTO `categories` VALUES(3, 1, 1, 'Orange', 4, 9);
    INSERT INTO `categories` VALUES(4, 3, 1, 'Orange Type 1', 5, 6);
    INSERT INTO `categories` VALUES(5, 3, 1, 'Orange Type 2', 7, 8);
    INSERT INTO `categories` VALUES(6, 1, 1, 'Pear', 10, 11);
    INSERT INTO `categories` VALUES(7, 1, 1, 'Banana', 12, 13);
    INSERT INTO `categories` VALUES(8, NULL, NULL, 'Eletronics', 15, 29);
    INSERT INTO `categories` VALUES(9, 8, 8, 'Cell Phones', 16, 17);
    INSERT INTO `categories` VALUES(10, 8, 8, 'Computers', 19, 24);
    INSERT INTO `categories` VALUES(11, 10, 8, 'PC', 20, 21);
    INSERT INTO `categories` VALUES(12, 10, 8, 'MAC', 22, 23);
    INSERT INTO `categories` VALUES(13, 8, 8, 'Printers', 25, 26);
    INSERT INTO `categories` VALUES(14, 8, 8, 'Cameras', 27, 28);
    

    现在您不必在root_id 上订购。

    另外,在得到树之后,有没有办法按名称对每个节点进行排序?

    没有简单的方法,除非您从头开始按名称顺序插入节点。 name 更大的兄弟姐妹应该有更大的lftrgt

    INSERT INTO `categories` VALUES(1, NULL, NULL, 'Fruits', 1, 14);
    INSERT INTO `categories` VALUES(2, 1, 1, 'Apple', 2, 3);
    INSERT INTO `categories` VALUES(7, 1, 1, 'Banana', 4, 5);
    INSERT INTO `categories` VALUES(3, 1, 1, 'Orange', 6, 11);
    INSERT INTO `categories` VALUES(4, 3, 1, 'Orange Type 1', 7, 8);
    INSERT INTO `categories` VALUES(5, 3, 1, 'Orange Type 2', 9, 10);
    INSERT INTO `categories` VALUES(6, 1, 1, 'Pear', 12, 13);
    

    嵌套树只能有一个隐式顺序。

    MySQL中还有一种查询邻接表的方法:

    但是,如果您想订购除id 以外的任何其他东西,则必须创建一个额外的唯一订购列。

    您可能还想阅读这篇文章:

    它展示了如何更有效地存储和查询嵌套集。

    【讨论】:

    • 谢谢,我会阅读并尝试。虽然,我想知道是否没有办法保持重复的 lft 和 rht,因为我有办法区分(root_id)。我已经通过以下方式实现了查询:WHERE root_id =1 OR id =1 ORDER BY lft 来获取其中一棵树(但不是全部)。这真的是错误的方式吗?当使用此链接articles.sitepoint.com/article/hierarchical-data-database/3 上列出的rebuild_tree() 方法时,我还有root_id 供参考所以,除了排序列表的SELECT 之外,一切都正常工作。
    • 如果我按照您的步骤操作,如何更改rebuild_tree() 函数以仅重建一棵树而不是全部?既然我会删除root_id?
    • 如果你总是将树分开(比如WHERE root_id = 1),允许不同集合之间的重叠是可以的。但是您的原始查询混合了两棵树。要只重建一棵树,只需调用rebuild_tree(1)rebuild_tree(8),这只会重建从18 开始的树。
    • 是的,这正是我使用rebuild_tree($root_id) 所做的。但是,有没有办法继续使用这种方法并更改我的选择查询,以便我可以获得由树分隔的整个结果,我的意思是 root_id?今天我无法实现它,因为我不知道如何在查询中指定它: ORDER BY lft "but group by trees"
    • @Keyne:试试ORDER BY root_id, lft。如果您总是将Electronics 项目放在Fruits 项目之后,那么只需ORDER BY lft 就足够了。
    【解决方案2】:

    我明白了。

    您需要做的只是将 root_id 也设置为顶级父母,以便您可以正确地 ORDER BY。

    通过下面的查询,我可以分离树,并且只更新我正在处理的树:

    SELECT c . * , count( p.id ) AS depth
    FROM `categories` c
    CROSS JOIN categories p
    WHERE (
    c.lft
    BETWEEN p.lft
    AND p.rht
    )
    AND c.root_id = p.root_id
    GROUP BY c.id
    ORDER BY c.root_id, c.lft
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-09-28
      • 1970-01-01
      • 1970-01-01
      • 2017-02-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多