How do I create a recursive query in MSSQL 2005?

后端 未结 4 453
心在旅途
心在旅途 2021-01-30 14:31

Let\'s say I have the following table:

CustomerID ParentID Name
========== ======== ====
1          null     John
2          1        James
3          2        J         


        
相关标签:
4条回答
  • 2021-01-30 14:55

    You can't do recursion in SQL without stored procedures. The way to solve this is using Nested Sets, they basically model a tree in SQL as a set.

    Notice that this will require a change to the current data model or possibly figuring out how to create a view on the original model.

    Postgresql example (using very few postgresql extensions, just SERIAL and ON COMMIT DROP, most RDBMSes will have similar functionality):

    Setup:

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

    Adding a child:

    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;
    

    Display a trail from bottom to top:

    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;
    

    Display the entire tree:

    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 everything down from a certain element of the tree:

    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;
    
    0 讨论(0)
  • 2021-01-30 15:00

    On SQL Server 2005 you can use 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
    
    0 讨论(0)
  • 2021-01-30 15:03

    For bottom up use mathieu's answer with a little modification:

    
    
    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
    
    
    
    0 讨论(0)
  • 2021-01-30 15:20

    Unless I'm missing something, recursion isn't necessary...

    SELECT d.NAME FROM Customers As d
    INNER JOIN Customers As p ON p.CustomerID = d.ParentID
    WHERE p.Name = 'James'
    
    0 讨论(0)
提交回复
热议问题