代码之家  ›  专栏  ›  技术社区  ›  Pablo Retyk

如何在MSSQL 2005中创建递归查询?

  •  24
  • Pablo Retyk  · 技术社区  · 16 年前

    假设我有下表:

    CustomerID ParentID Name
    ========== ======== ====
    1          null     John
    2          1        James
    3          2        Jenna
    4          3        Jennifer
    5          3        Peter
    6          5        Alice
    7          5        Steve
    8          1        Larry 
    

    我想在一个查询中检索詹姆斯的所有后代(詹娜、詹妮弗、彼得、爱丽丝、史蒂夫)。 谢谢, 巴勃罗。

    4 回复  |  直到 16 年前
        1
  •  34
  •   mathieu    16 年前

    在SQL Server 2005上,您可以使用 CTEs (Common Table Expressions) :

    with Hierachy(CustomerID, ParentID, Name, Level)
    as
    (
    select CustomerID, ParentID, Name, 0 as Level
        from Customers c
        where c.CustomerID = 2 -- insert parameter here
        union all
        select c.CustomerID, c.ParentID, c.Name, ch.Level + 1
        from Customers c
        inner join Hierachy ch
        on c.ParentId = ch.CustomerID
    )
    select CustomerID, ParentID, Name
    from Hierachy
    where Level > 0
    
        2
  •  3
  •   zozzancs    14 年前

    对于自下而上,使用mathieu的答案,稍作修改:

    
    
    with Hierachy(CustomerID, ParentID, Name, Level)
    as
    (
    select CustomerID, ParentID, Name, 0 as Level
        from Customers c
        where c.CustomerID = 2 -- insert parameter here
        union all
        select c.CustomerID, c.ParentID, c.Name, ch.Level + 1
        from Customers c
        inner join Hierachy ch
    
        -- EDITED HERE --
        on ch.ParentId = c.CustomerID
        ----------------- 
    
    )
    select CustomerID, ParentID, Name
    from Hierachy
    where Level > 0
    
    
    
        3
  •  0
  •   Jasper Bekkers    16 年前

    没有存储过程,你就无法在SQL中进行递归。解决这个问题的方法是使用嵌套集,它们基本上将SQL中的树建模为集。

    请注意,这将需要更改当前数据模型,或者可能需要弄清楚如何在原始模型上创建视图。

    Postgresql示例(使用很少的Postgresql扩展,只有SERIAL和ON COMMIT DROP,大多数RDBMS将具有类似的功能):

    设置:

    CREATE TABLE objects(
        id SERIAL PRIMARY KEY,
        name TEXT,
        lft INT,
        rgt INT
    );
    
    INSERT INTO objects(name, lft, rgt) VALUES('The root of the tree', 1, 2);
    

    添加孩子:

    START TRANSACTION;
    
    -- postgresql doesn't support variables so we create a temporary table that 
    -- gets deleted after the transaction has finished.
    
    CREATE TEMP TABLE left_tmp(
        lft INT
    ) ON COMMIT DROP; -- not standard sql
    
    -- store the left of the parent for later use
    INSERT INTO left_tmp (lft) VALUES((SELECT lft FROM objects WHERE name = 'The parent of the newly inserted node'));
    
    -- move all the children already in the set to the right
    -- to make room for the new child
    UPDATE objects SET rgt = rgt + 2 WHERE rgt > (SELECT lft FROM left_tmp LIMIT 1);
    UPDATE objects SET lft = lft + 2 WHERE lft > (SELECT lft FROM left_tmp LIMIT 1);
    
    -- insert the new child
    INSERT INTO objects(name, lft, rgt) VALUES(
        'The name of the newly inserted node', 
        (SELECT lft + 1 FROM left_tmp LIMIT 1), 
        (SELECT lft + 2 FROM left_tmp LIMIT 1)
    );
    
    COMMIT;
    

    从下到上显示轨迹:

    SELECT
        parent.id, parent.lft
    FROM
        objects AS current_node
    INNER JOIN
        objects AS parent
    ON
        current_node.lft BETWEEN parent.lft AND parent.rgt
    WHERE
        current_node.name = 'The name of the deepest child'
    ORDER BY
        parent.lft;
    

    显示整个树:

    SELECT
        REPEAT('   ', CAST((COUNT(parent.id) - 1) AS INT)) || '- ' || current_node.name AS indented_name
    FROM
        objects current_node
    INNER JOIN
        objects parent
    ON
        current_node.lft BETWEEN parent.lft AND parent.rgt
    GROUP BY
        current_node.name,
        current_node.lft
    ORDER BY
        current_node.lft;
    

    从树的某个元素中选择所有内容:

    SELECT
        current_node.name AS node_name
    FROM
        objects current_node
    INNER JOIN
        objects parent
    ON
        current_node.lft BETWEEN parent.lft AND parent.rgt
    AND
        parent.name = 'child'
    GROUP BY
        current_node.name,
        current_node.lft
    ORDER BY
        current_node.lft;
    
        4
  •  -9
  •   Kaniu    16 年前

    除非我遗漏了什么,否则递归是不必要的。..

    SELECT d.NAME FROM Customers As d
    INNER JOIN Customers As p ON p.CustomerID = d.ParentID
    WHERE p.Name = 'James'