sql - 在邻接表模型中克隆树的数据库表示的最有效方法

标签 sql sql-server performance algorithm tree

我使用邻接表模型将嵌套列表存储在我的数据库中。每个列表可能有 50-150 个节点,所以我们称它为平均 100 个节点。出现了用户想要克隆列表的情况(即,使用现有列表作为模板,从中创建新列表)。当新列表与现有列表仅略有不同时,此用例可能会为他们节省大量时间。

这是我正在使用的表架构的简化版本:

CREATE TABLE Nodes (
    NodeId int IDENTITY(1,1) NOT NULL,
    ParentId int NULL,
    ListId int NOT NULL,
    NodeText varchar(255) NOT NULL
)

我最初的想法是使用 INSERT ... SELECT 一次性复制所有节点,但这会使新记录引用旧的 ParentId 值。

我有一个有效的解决方案(在应用程序代码中,而不是在 SQL 中),但由于所需的查询数量,它似乎不是最佳选择。这是算法:

  1. 选择属于旧列表的所有记录。
  2. 遍历行并通过插入不同的 ListId 来添加到新列表。
  3. 从每个插入中选择 @@IDENTITY 并将其与当前行的数据一起存储。
  4. 再次遍历行并更新 Nodes 表,将 ParentId 设置为新 ID(来自上一步),其中 ParentId 等于旧 ID ListId 等于新的列表 ID。

正如我所说,这很好用,但它需要 300 多个查询才能克隆包含 100 个节点的单个列表。有没有更有效的方法来实现同样的事情?

最佳答案

试试这个。以下解决方案是零循环、零临时表一。

SQLFiddle

DECLARE @CurrentID int = IDENT_CURRENT('Nodes'),
        @OldListId int = 1,
        @NewListId int;

SELECT @NewListId = ISNULL(MAX(ListId) ,0)+1 FROM Nodes

SET IDENTITY_INSERT Nodes ON

;WITH NewNode as (
    SELECT ROW_NUMBER()  OVER(ORDER BY NodeId)+ @CurrentID as NewNodeId, * 
    FROM Nodes WHERE ListId= @OldListId
)
INSERT INTO Nodes(NodeId,ParentId,ListId,NodeText)
SELECT N1.NewNodeId ,N2.NewNodeId , @NewListId, N1.NodeText FROM NewNode N1 LEFT OUTER JOIN NewNode N2 ON 
N1.ParentId = N2.NodeId

--SELECT N1.* , N2.NewNodeId as NewParentId FROM NewNode N1 LEFT OUTER JOIN NewNode N2 ON 
--N1.ParentId = N2.NodeId

SET IDENTITY_INSERT Nodes OFF

上述解决方案生成树,然后插入到表中。请注意使用适当的事务和锁定机制以确保数据一致

关于sql - 在邻接表模型中克隆树的数据库表示的最有效方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20338268/

相关文章:

mysql - SQL 查询 : retrieve existing dates period with overlap

sql - 计算两点之间的距离(纬度,经度)

mysql - 从t-sql中的末尾获取字符串中第一次出现char的索引

css - 在 vue 中的单页应用程序中对抗无样式文本的闪烁

c# - “ConnectionString 属性尚未初始化。”修理

java - 为 SELECT 语句使用 JPA 值得吗?

sql - Regexp_extract 出现 '-q_' 后的所有内容

sql - 每 15 分钟更新一次表

performance - memcached 和 Redis 等工具的主要用例是什么?

java - 遍历字符串中所有字符的最快方法