Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/sql-server/21.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql server 从层次结构中删除项目_Sql Server_Tsql_Tree - Fatal编程技术网

Sql server 从层次结构中删除项目

Sql server 从层次结构中删除项目,sql-server,tsql,tree,Sql Server,Tsql,Tree,我有一些这样的树结构 树中的某些节点将被禁用。禁用节点的子节点需要连接到禁用节点的父节点。如果禁用该选项,则将其添加到以下父级,依此类推 在此图片中,节点2被禁用,节点4和5连接到节点2的父节点,即节点1 这就是结果 一个更复杂的案例id,其中禁用节点的父节点也被禁用 在这个示例中,我获得了一些testdata的所有子节点,但我不知道是否可以删除禁用的节点,但保持子节点连接到下一个可能的父节点 CREATE TABLE #Node ( Id INT, ParentI

我有一些这样的树结构

树中的某些节点将被禁用。禁用节点的子节点需要连接到禁用节点的父节点。如果禁用该选项,则将其添加到以下父级,依此类推

在此图片中,节点2被禁用,节点4和5连接到节点2的父节点,即节点1

这就是结果

一个更复杂的案例id,其中禁用节点的父节点也被禁用

在这个示例中,我获得了一些testdata的所有子节点,但我不知道是否可以删除禁用的节点,但保持子节点连接到下一个可能的父节点

CREATE TABLE #Node
(
     Id INT, 
     ParentID INT, 
     Name NVARCHAR(20),   
     skipNode BIT
);

INSERT INTO #Node 
VALUES (1, NULL, 'node-1', 0),
       (2, 1, 'node-2', 1),
       (3, 1, 'node-3', 0),
       (4, 2, 'node-4', 0),
       (5, 2, 'node-5', 0),
       (6, 3, 'node-6', 0),
       (7, 4, 'node-6', 0),
       (8, 4, 'node-6', 0);

WITH RCTE AS
(
    SELECT 
        anchor.Id AS ItemId, 
        skipNode,
        anchor.ParentId AS ItemParentId, 
        1 AS Lvl, 
        anchor.[Name],
        CAST(name AS VARCHAR(1000)) AS NodePath
    FROM
        #Node anchor 
    WHERE 
        anchor.[Id] = 1

    UNION ALL

    SELECT 
        nextDepth.Id AS ItemId, 
        nextDepth.skipNode, 
        nextDepth.ParentId AS ItemParentId, 
        Lvl+1 AS Lvl, 
        nextDepth.[Name],
        CAST((rec.NodePath + '/' + nextDepth.[Name]) AS VARCHAR(1000)) AS NodePath
    FROM 
        #Node nextDepth
    INNER JOIN 
        RCTE rec ON nextDepth.ParentId = rec.ItemId
)
SELECT ItemId, skipNode , ItemParentId, [Name], NodePath
FROM RCTE AS hierarchy

DROP TABLE #Node
禁用节点2的预期结果为

ItemId      skipNode ItemParentId Name   
----------- -------- ------------ -------
1           0        NULL         node-1 
3           0        1            node-3 
6           0        3            node-6 
4           0        1            node-4 
5           0        1            node-5 
7           0        4            node-6 
8           0        4            node-6 

这个例子应该能让你明白:

USE tempdb;
GO

DROP FUNCTION IF EXISTS dbo.GetParentNode;
GO

CREATE FUNCTION dbo.GetParentNode
( 
    @NodePath varchar(max)
)
RETURNS int
AS 
BEGIN
    /*
        SELECT dbo.GetParentNode('12/13/14');
        SELECT dbo.GetParentNode('12/14');
    */

    DECLARE @ReturnValue int;
    DECLARE @StringToProcess varchar(max) = REVERSE(@NodePath);
    DECLARE @DelimiterLocation int;

    SET @DelimiterLocation = CHARINDEX('/', @StringToProcess);

    IF @DelimiterLocation > 0
    BEGIN
        SET @StringToProcess = SUBSTRING(@StringToProcess, @DelimiterLocation + 1, LEN(@StringToProcess));
        SET @DelimiterLocation = CHARINDEX('/', @StringToProcess);

        IF @DelimiterLocation = 0
        BEGIN
            SET @ReturnValue = CAST(REVERSE(@StringToProcess) AS int);
        END ELSE BEGIN
            SET @ReturnValue = CAST(REVERSE(LEFT(@StringToProcess, @DelimiterLocation - 1)) AS int);
        END;
    END;

    RETURN @ReturnValue;   
END;
GO

DROP TABLE IF EXISTS dbo.Nodes;
GO

CREATE TABLE dbo.Nodes
(
     NodeID int, 
     ParentNodeID int, 
     NodeName nvarchar(20),   
     IsDisabled bit
);

INSERT dbo.Nodes 
(
    NodeID, ParentNodeID, NodeName, IsDisabled
)
VALUES (1, NULL, 'node-1', 0),
       (2, 1, 'node-2', 1),
       (3, 1, 'node-3', 0),
       (4, 2, 'node-4', 0),
       (5, 2, 'node-5', 0),
       (6, 3, 'node-6', 0),
       (7, 4, 'node-6', 0),
       (8, 4, 'node-6', 0);

WITH AllNodes AS
(
    SELECT toplevel.NodeID, 
           toplevel.IsDisabled,
           toplevel.ParentNodeID, 
           1 AS NodeLevel, 
           toplevel.NodeName, 
           CAST(toplevel.NodeID AS varchar(max)) AS NodePath
    FROM dbo.Nodes AS toplevel 
    WHERE toplevel.NodeID = 1

    UNION ALL

    SELECT 
        n.NodeID, 
        n.IsDisabled, 
        n.ParentNodeID, 
        an.NodeLevel + 1, 
        n.NodeName,
        an.NodePath + CASE WHEN n.IsDisabled = 0 
                           THEN '/' + CAST(n.NodeID AS varchar(max))
                           ELSE ''
                      END
    FROM dbo.Nodes AS n
    INNER JOIN AllNodes AS an 
    ON an.NodeID = n.ParentNodeID
)
SELECT an.NodeID, an.IsDisabled, an.NodeName,
       an.ParentNodeID, an.NodeLevel, an.NodePath,
       dbo.GetParentNode(an.NodePath) AS TrueParentNodeID
FROM AllNodes AS an
WHERE an.IsDisabled = 0;

DROP TABLE dbo.Nodes;
GO


只是执行整个过程,看看它看起来是否正确。希望能有所帮助。

感谢您提供了一个有效的示例。我使用了您所拥有的,并对其进行了修改,以使用hierarchyid数据类型(这使您的工作变得非常简单。首先,修改后的设置代码:

WITH RCTE AS
(
    SELECT 
        anchor.Id AS ItemId, 
        skipNode,
        anchor.ParentId AS ItemParentId, 
        1 AS Lvl, 
        anchor.[Name],
        CAST(concat('/', id, '/') AS VARCHAR(1000)) AS NodePath
    FROM
        #Node anchor 
    WHERE 
        anchor.[Id] = 1

    UNION ALL

    SELECT 
        nextDepth.Id AS ItemId, 
        nextDepth.skipNode, 
        nextDepth.ParentId AS ItemParentId, 
        Lvl+1 AS Lvl, 
        nextDepth.[Name],
        CAST(concat(rec.NodePath, nextDepth.[id], '/') AS VARCHAR(1000)) AS NodePath
    FROM 
        #Node nextDepth
    INNER JOIN 
        RCTE rec ON nextDepth.ParentId = rec.ItemId
)
SELECT ItemId, skipNode , ItemParentId, [Name], cast(NodePath as [hierarchyid]) as NodePath
into dbo.rcte
FROM RCTE AS hierarchy
GO
仅有的两个修改是NodePath的格式(现在是以斜杠分隔的ID列表,而不是名称)和将结果转储到表中(而不是原始结果集)。现在是一个存储过程,它接收ItemID并将其从表中删除(相应地更新它所依赖的任何内容)

请注意,如果我真的在编写存储过程,我会有更多的内容(即检查我试图删除的内容是否确实存在,检查父对象是否存在,等等),但解决方案的要点就在那里

create or alter procedure RemoveItem (
    @ItemID int
)
AS
begin
    set nocount on;

    declare @NewParent hierarchyid = (
        select parent.NodePath
        from dbo.rcte as child
        join dbo.rcte as parent
            on child.ItemParentId = parent.ItemId
        where child.ItemId = @ItemID
    ), @OldParent hierarchyid = (
        select Child.NodePath
        from dbo.rcte as child
        where child.ItemId = @ItemID
    ), @NewParentItemID int = (
        select Child.ItemParentId
        from dbo.rcte as child
        where child.ItemId = @ItemID
    );

    update r
    set NodePath = NodePath.GetReparentedValue(@OldParent, @NewParent),
        ItemParentId = case 
            when ItemParentId = @ItemID 
            then @NewParentItemID 
            else ItemParentId
        end
    from dbo.rcte as r
    where NodePath.IsDescendantOf(@OldParent) = 1
        and r.ItemId <> @ItemID;

    delete dbo.rcte
    where ItemId = @ItemID;
end
go
exec dbo.RemoveItem @ItemID = 2;

select *, NodePath.ToString()
from dbo.rcte;