How do I create a recursive query in MSSQL 2005?

后端 未结 4 526
心在旅途
心在旅途 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;
    

提交回复
热议问题