【问题标题】:MySQL parent -> child queryMySQL父->子查询
【发布时间】:2010-07-27 02:58:17
【问题描述】:

我正在使用 mySQL,并且我需要从一个表中选择与父表中任何级别的 ID 匹配的数据 -> 另一个表中的子数据层次结构。

此外,我想用一个写得很好的 SQL 查询来解决这个问题,而不是我的 PHP 代码中的递归函数,因为这个特性会被使用很多。

我确实尝试过搜索,并且偶然发现了许多类似的问题(大部分都已解决),但没有一个对我有帮助。

为了帮助说明情况,这是我当前的设置

表格“文章”:

  • article_id
  • category_id
  • ...

表格类别

  • category_id
  • parent_id
  • ...

我需要从 "articles.category_id" 为 10 的 "articles" 中选择所有文章。但还要从 "categories.category_id" 10 所属的树中接收所有类别的所有文章。

含义,其中“10”是父级及其所有子级,向上是 10 是子级及其所有父级。

没有递归php函数也可以吗?

谢谢。

【问题讨论】:

    标签: php mysql hierarchical-data


    【解决方案1】:

    鉴于您使用的是 MySQL,不可能使用您正在使用的邻接列表设计在一个查询中获取整个树。

    其他一些品牌的数据库支持 SQL 扩展来处理这种设计。 Oracle、Microsoft SQL Server、IBM DB2 和 PostgreSQL 8.4(目前处于测试阶段)支持 SQL 扩展。

    还有其他数据库设计可以让您更有效地查询树。这个问题已在 StackOverflow、博客和文章中多次解决。

    您还可以阅读 Joe Celko 的“Trees and Hierarchies in SQL for Smarties”,其中深入探讨了几种此类设计。

    【讨论】:

    • 比尔,如果您在我的回答中谈到的功能中发现任何流程,我会很感兴趣。我知道您使用数据库的经验比我多。
    • @Ionut:好的,看看我对你的回答的评论。
    • Bill,感谢您花时间看一看。我真的很感激。
    【解决方案2】:

    这在MySQL 中是可以做到的,但需要一点努力。您必须编写这样的函数:

    CREATE FUNCTION hierarchy_connect_by_parent_eq_prior_id(value INT) RETURNS INT
    NOT DETERMINISTIC
    READS SQL DATA
    BEGIN
            DECLARE _id INT;
            DECLARE _parent INT;
            DECLARE _next INT;
            DECLARE CONTINUE HANDLER FOR NOT FOUND SET @id = NULL;
    
            SET _parent = @id;
            SET _id = -1;
    
            IF @id IS NULL THEN
                    RETURN NULL;
            END IF;
    
            LOOP
                    SELECT  MIN(id)
                    INTO    @id
                    FROM    categories
                    WHERE   parent = _parent
                            AND id > _id;
                    IF @id IS NOT NULL OR _parent = @start_with THEN
                            SET @level = @level + 1;
                            RETURN @id;
                    END IF;
                    SET @level := @level - 1;
                    SELECT  id, parent
                    INTO    _id, _parent
                    FROM    categories
                    WHERE   id = _parent;
            END LOOP;
    END
    

    并在查询中使用它:

    SELECT  id, parent, level
    FROM    (
            SELECT  hierarchy_connect_by_parent_eq_prior_id(id) AS id, @level AS level
            FROM    (
                    SELECT  @start_with := 0,
                            @id := @start_with,
                            @level := 0
                    ) vars, categories 
            WHERE   @id IS NOT NULL
            ) ho
    JOIN    categories hi
    ON      hi.id = ho.id
    

    更多详情请参阅我的博客中的此条目:

    【讨论】:

    • 这看起来很有趣,谢谢你的努力!我有另一个入站情况类似的项目,我一定会尝试一下。
    • 看起来是我问题的完美答案。但我在 DECLARE 语句中遇到错误:DECLARE id INT 错误代码:1064。您的 SQL 语法有错误;检查与您的 MySQL 服务器版本相对应的手册,以了解要使用的正确语法
    • 回答我自己的评论:看来您必须在执行此函数之前更改 DELIMITER (DELIMITER //)
    【解决方案3】:

    在关系数据库中存储分层数据的最常见模式是相邻列表或modified preorder (aka nested set)。另一种方法是使用物化路径,它基本上是一种缓存机制,位于相邻列表的顶部。另见this table for a comparison of pros and cons

    【讨论】:

      【解决方案4】:

      我不知道这对您有多大帮助,但我编写了一个小函数,它使用单个 MySQL 查询生成层次树。基本上,所有重要的逻辑都移到了 PHP 中。我的解决方案使用邻接列表模型,然后使用 PHP 引用,以便通过平面结构构建树形数据结构。看看下面的要点,看看你是否得到了一些灵​​感。我会为您提供更多帮助,但在我的工作中有一些问题需要处理。

      http://gist.github.com/104357

      【讨论】:

      • 好的,我读了你的代码。如何编写只包含所需层次结构中的节点的查询?例如,假设您正在运行 Slashdot。您将数百万个 cmets 排列在数千个单独的层次结构中,但存储在一个数据库表中。如何只查询属于用户当前正在查看的线程的 cmets?
      • 我想这是我实现的弱点。对于大型数据集,在内存中获取整个表数据没有多大意义。我的解决方案有点假设。在我写这篇文章的时候,它被认为是针对具有少量数据的表(不确定小到底是什么意思),我认为在一个查询中获取所有数据比在多个查询中获取小块更好。然后将在 PHP 代码中完成对某个“线程”的访问。
      • 一次获取所有数据还是一次获取位更好,取决于有多少数据。 :-) 除了“parent_id”之外,您可以做的一件事就是将“root_id”与每个树节点一起存储。这样,您可以查询具有相同“root_id”的所有行,并确保结果仅包含您想要的树。您仍然可能会得到一棵超出 PHP 内存的大树,但至少您不会为不相关的树获取数据。
      猜你喜欢
      • 1970-01-01
      • 2012-01-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-03-02
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多