SQL 在自引用表中查找祖先/后代

Posted

技术标签:

【中文标题】SQL 在自引用表中查找祖先/后代【英文标题】:SQL finding ancestors/decendants in a self-referencing table 【发布时间】:2012-03-06 21:19:10 【问题描述】:

我有一个引用自身的表,如下所示:

CREATE TABLE Foo (
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
parent INT NULL,
name VARCHAR (30) NOT NULL,
FOREIGN KEY (parent) REFERENCES Foo(id) ON DELETE CASCADE);

样本数据:

id   parent name
1    NULL   a
2    NULL   b
3    1      a1
4    1      a2
5    3      a1x
6    3      a2x

我想编写查询来列出给定行的祖先和后代,例如

CALL find_ancestors('a1x')

会回来

id name
3  a1
1  a

CALL find_descendants('a')

会回来

id name
3  a1
5  a1x

如何为 mysql 5 编写这些存储过程?谢谢


悬赏问题:还选择返回行与源的距离并将最大距离参数传递给过程,例如

CALL find_ancestors('a1x')

会回来

id name distance
3  a1   1
1  a    2

CALL find_ancestors_bounded('a1x',1)

会回来

id name distance
3  a1   1

【问题讨论】:

我考虑阅读“Joe Celko 为 Smarties 编写的 SQL 中的树和层次结构”,其中涵盖了这类问题。这是很有趣的理论知识,但我一直想知道 sql 树,人们在实践中实际使用它们做什么? 在我的例子中,a 是欧洲,b 是亚洲,a1 是奥地利,等等...... 啊。谢谢。说“读这本书”作为我的答案感觉不够,所以我没有把它作为答案。但是上面的塞尔科的书几乎是圣经。其他解释见***: Is it possible to query a tree structure table in MySQL in a single query, to any depth? 上面似乎是在处理二叉树,或者至少链接的例子是这样。而且,由于手头没有这本书,我很想给 n 元示例代码加分。 虽然答案中给出的示例是二叉树,但我认为该技术中没有任何东西将其限制为二叉树。 (请注意,修改的预排序树遍历技术几乎要求您的数据是固定的——对于大型动态树,您的数据库将通过不断地重新排序树而被淹没。)存储过程的迭代解决方案将允许您保持结构不变. 【参考方案1】:

假设我们有一个包含四个元素的表,id、item、class 和 parent_id。我们希望拥有任何给定项目的完整祖先,我们需要做的是一个自定义的 mysql 函数,它实际上将遍历每条记录,为我们的项目 parent_id 寻找匹配项,一旦找到匹配项,如果匹配的项目有parent_id,它将再次开始循环,依此类推。每次我们的函数找到匹配时,它都会将它存储在一个逗号分隔的字符串中,该字符串将在最后返回(例如:1,2,3,4)

我们的函数看起来像这样:

DELIMITER $$
DROP FUNCTION IF EXISTS `junk`.`GetAncestry` $$
CREATE FUNCTION `junk`.`GetAncestry` (GivenID INT) RETURNS VARCHAR(1024)
DETERMINISTIC
BEGIN
    DECLARE rv VARCHAR(1024);
    DECLARE cm CHAR(1);
    DECLARE ch INT;

    SET rv = '';
    SET cm = '';
    SET ch = GivenID;
    WHILE ch > 0 DO
        SELECT IFNULL(parent_id,-1) INTO ch FROM
        (SELECT parent_id FROM pctable WHERE id = ch) A;
        IF ch > 0 THEN
            SET rv = CONCAT(rv,cm,ch);
            SET cm = ',';
        END IF;
    END WHILE;
    RETURN rv;
END $$
DELIMITER ;

此代码由 RolandoMySQLDBA 编写

【讨论】:

以上是关于SQL 在自引用表中查找祖先/后代的主要内容,如果未能解决你的问题,请参考以下文章

查找引用某个表中特定列的所有存储过程

在自引用表上编写递归 SQL 查询

遍历祖先同胞后代

SQL 查找树中的所有直系后代

ADT 树 - 是节点本身的祖先/后代吗?

Excel表中连接sql并使用sql语句引用excel单元格数据作为查询条件