How do you sort a tree stored using the nested set model?

前端 未结 8 757
不思量自难忘°
不思量自难忘° 2021-01-31 20:32

When I refer to nested set model I mean what is described here.

I need to build a new system for storing \"categories\" (I can\'t think of better word for it) in a user

8条回答
  •  自闭症患者
    2021-01-31 21:04

    I have just finished writing the following which works for me in sorting an entire nested set tree.

    The sort (ideally) requires a view that lists the current level of each node in the tree and a procedure for swapping two nodes - both are included below, the sibling swap code comes from Joe Celkos ' Tree & Hierarchies' book which I strongly recommend to anyone using nested sets.

    The sort can be altered in the 'INSERT INTO @t' statement, here it is a simple alphanumeric sort on 'Name'

    This may be a poor way of doing it especially using the cursor for set based code but as I say it works for me, hope it helps.

    UPDATE:

    Code below now shows version without using cusor. I see about 10x speed improvements

    CREATE VIEW dbo.tree_view
    
    AS
    
    SELECT t2.NodeID,t2.lft,t2.rgt ,t2.Name, COUNT(t1.NodeID) AS level  
    FROM dbo.tree t1,dbo.tree t2
    WHERE t2.lft BETWEEN t1.lft AND t1.rgt
    GROUP BY t2.NodeID,t2.lft,t2.rgt,t2.Name
    
    GO
    
    ----------------------------------------------
    
      DECLARE @CurrentNodeID int
    DECLARE @CurrentActualOrder int
    DECLARE @CurrentRequiredOrder int
    DECLARE @DestinationNodeID int
    DECLARE @i0 int
    DECLARE @i1 int
    DECLARE @i2 int
    DECLARE @i3 int
    
    DECLARE @t TABLE (TopLft int,NodeID int NOT NULL,lft int NOT NULL,rgt int NOT NULL,Name varchar(50),RequiredOrder int NOT NULL,ActualOrder int NOT NULL)
    
    
    INSERT INTO @t (toplft,NodeID,lft,rgt,Name,RequiredOrder,ActualOrder)
        SELECT tv2.lft,tv1.NodeID,tv1.lft,tv1.rgt,tv1.Name,ROW_NUMBER() OVER(PARTITION BY tv2.lft ORDER BY tv1.ColumnToSort),ROW_NUMBER() OVER(PARTITION BY tv2.lft ORDER BY tv1.lft ASC)
        FROM dbo.tree_view tv1 
        LEFT OUTER JOIN dbo.tree_view tv2 ON tv1.lft > tv2.lft and tv1.lft < tv2.rgt and tv1.level = tv2.level+1
        WHERE tv2.rgt > tv2.lft+1
    
        DELETE FROM @t where ActualOrder = RequiredOrder
    
    
    WHILE EXISTS(SELECT * FROM @t WHERE ActualOrder <> RequiredOrder)
    BEGIN
    
    
        SELECT Top 1 @CurrentNodeID = NodeID,@CurrentActualOrder = ActualOrder,@CurrentRequiredOrder = RequiredOrder
        FROM @t 
        WHERE ActualOrder <> RequiredOrder
        ORDER BY toplft,requiredorder
    
        SELECT @DestinationNodeID = NodeID
        FROM @t WHERE ActualOrder = @CurrentRequiredOrder AND TopLft = (SELECT TopLft FROM @t WHERE NodeID = @CurrentNodeID) 
    
        SELECT @i0 = CASE WHEN c.lft < d.lft THEN c.lft ELSE d.lft END,
                @i1 =  CASE WHEN c.lft < d.lft THEN c.rgt ELSE d.rgt END,
                @i2 =  CASE WHEN c.lft < d.lft THEN d.lft ELSE c.lft END,
                @i3 =  CASE WHEN c.lft < d.lft THEN d.rgt ELSE c.rgt END
        FROM dbo.tree c
        CROSS JOIN dbo.tree d
        WHERE c.NodeID = @CurrentNodeID AND d.NodeID = @DestinationNodeID
    
        UPDATE dbo.tree
        SET lft = CASE  WHEN lft BETWEEN @i0 AND @i1 THEN @i3 + lft - @i1
                        WHEN lft BETWEEN @i2 AND @i3 THEN @i0 + lft - @i2
                ELSE @i0 + @i3 + lft - @i1 - @i2
                END,
            rgt = CASE  WHEN rgt BETWEEN @i0 AND @i1 THEN @i3 + rgt - @i1
                        WHEN rgt BETWEEN @i2 AND @i3 THEN @i0 + rgt - @i2
                ELSE @i0 + @i3 + rgt - @i1 - @i2
                END
        WHERE lft BETWEEN @i0 AND @i3 
        AND @i0 < @i1
        AND @i1 < @i2
        AND @i2 < @i3
    
        UPDATE @t SET actualorder = @CurrentRequiredOrder where NodeID = @CurrentNodeID
        UPDATE @t SET actualorder = @CurrentActualOrder where NodeID = @DestinationNodeID
    
        DELETE FROM @t where ActualOrder = RequiredOrder
    
    END
    

提交回复
热议问题