【问题标题】:Use SQL to clone a tree structure represented in a database使用 SQL 克隆数据库中表示的树结构
【发布时间】:2019-05-10 15:49:36
【问题描述】:

给定一个表示分层树结构并具有三列的表

  1. ID(主键,非自动递增)
  2. 父组ID
  3. 一些价值

我知道该分支的最低节点,我想将其复制到一个新分支,该分支具有相同数量的父节点,也需要克隆。

我正在尝试编写一个 SQL INSERT INTO 语句,该语句将复制同一 main 的每一行的副本,将第一部分 GroupID 复制到一个新的 GroupID 中。

示例开始表:

ID | ParentGroupID | SomeValue
------------------------
1  |      -1       |    a
2  |       1       |    b
3  |       2       |    c

运行简单的 INSERT INTO 语句后的目标:

ID | ParentGroupID | SomeValue
------------------------
1  |      -1       |    a
2  |       1       |    b
3  |       2       |    c
4  |      -1       |    a-cloned
5  |       4       |    b-cloned
6  |       5       |    c-cloned

最终的树结构

+--a (1)
|  +--b (2)
|     +--c (3)
|
+--a-cloned (4)
|  +--b-cloned (5)
|     +--c-cloned (6)

正如这个演示数据所显示的那样,ID 并不总是很好地间隔开,所以我不能总是假设父级的 ID 比有父级的行的当前 ID 小 1。

另外,我正在尝试在 T-SQL 中执行此操作(适用于 Microsoft SQL Server 2005 及更高版本)。

这感觉像是一个经典的练习,应该有一个纯 SQL 的答案,但我太习惯于编程,我的大脑不会在关系 SQL 中思考。

【问题讨论】:

  • 您使用的是哪个版本的 SQL Server?
  • 我不清楚如何确定克隆的 ParentGroupID。 B 的克隆如何获得 4 的 ParentGroupId 而 A 的克隆获得的 ParentGroupId 等于其源行?
  • 我需要支持 SQL Server 2005。(我更新了原帖以表明这一点)
  • ID 是什么类型的字段 - 标识列还是手动分配的整数类型?
  • 这个想法是为新分支保持相同的树结构。当我需要克隆一些与树节点 c (ID==3) 关联的数据时,我需要创建一个新节点,然后该节点也具有相同数量的父节点。我也将最终的树结构添加到原始帖子中,因为我可以在 cmets 中进行花哨的格式设置我认为我的 ID 列出正确......我只是试图捏造虚拟数据作为一个简单的例子。

标签: sql sql-server sql-server-2005 tsql


【解决方案1】:

试试这个,根据Quassnoi的文章Adjacency List vs Nested Sets: SQL Server的查询:

WITH q AS
(
    SELECT  h.*, 1 AS level
    FROM    Table1 h
    WHERE   id = 3
    UNION ALL
    SELECT  hp.*, level + 1
    FROM    q
    JOIN    Table1 hp
    ON      hp.id = q.ParentGroupID
), q2 AS (
    SELECT
        ID,
        ParentGroupID,
        SomeValue,
        (SELECT MAX(level) FROM q) - level AS level
    FROM q
)
INSERT INTO table1
SELECT
    (SELECT MAX(ID) FROM Table1) + level + 1 AS ID,
    CASE WHEN level = 0 THEN -1
         ELSE (SELECT MAX(ID) FROM Table1) + level
    END AS ParentGroupID,
    SomeValue + '-cloned'
FROM    q2

在测试数据上运行时的结果:

ID  ParentGroupID  SomeValue  
1   -1             a          
2   1              b          
3   2              c          
4   -1             a-cloned
5   4              b-cloned
6   5              c-cloned

【讨论】:

  • 我要试试这个!再次感谢马克!我需要开始关注你和 Quassnoi 的帖子,因为你们似乎总是有答案!
【解决方案2】:

假设 ID 是一个标识列,具有自动分配的值。 我是即兴做这件事的,所以对任何语法错误表示歉意。我希望 cmets 说明意图。

/* Find all ancestors for a given ID */
WITH Ancestors(ChildID, AncestorID) AS
(
  SELECT id AS ChildID, id As AncestorID
     FROM YourTable WHERE ParentGroupID=--1
  UNION ALL
  SELECT a.ChildID, d.ParentGroupID
     FROM  Ancestors AS a INNER JOIN
       YourTable d ON d.ID = a.AncestorID
     WHERE (b1.ParentGroupID <> -1))
),
/* Insert new rows for all ancestors of a given ID and save the results, so we have access to the new ID. we also have a column for the old ID. */
IDMap(ID, ParentGroupID, SomeValue, OldID) AS
{
    // insert, using identity column assigned ID, and save the old ID
   INSERT INTO YourTable SELECT d.ParentGroupID, d.SomeValue+"-cloned", d.ID FROM YourTable d
      INNER JOIN Ancestors a ON a.ChildID = d.ID 
      WHERE a.AncestorID=<the ID to clone>
}
/* Now update the parentID in the inserted data to the new ID */
UPDATE YourTable
SET ParentGroupID = map.ID
FROM YouTable t INNER JOIN (SELECT * FROM IDMap) map
  ON t.ParentGroupID=map.OldID

【讨论】:

  • 我没有看到任何对@@identity 的电话,你从哪里得到它
  • 我同意,我也看不到。我期待 IDMap CTE 上有一个 OUTPUT 子句。也许它被深夜的脑雾所吞噬。我会更新查询。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-12-31
相关资源
最近更新 更多