1

我有一个构成主题结构的相邻列表层次结构模型

   ID   Parent_Id Topic_Name
   1    Null      Topic 1
   2    Null      Topic 2
   3    2            Topic 3
   4    3               Topic 4
   5    2            Topic 5
   6    Null      Topic 6

这构成了我无法更改的应用程序的一部分-不幸的是,主题没有多个父级,我无法移至嵌套集-尽管如果这是该过程中的临时步骤-只要返回就可以了到相邻列表层次模型

我想指定一个主题 id,然后将其复制到一个新的主题 id 并保留下面的级别/结构

所以在我的例子中,我可以指定主题 topic_id 2 并且它会创建

   ID   Parent_Id Topic_Name
   7    Null      Topic 2
   8    7            Topic 3
   9    8               Topic 4
   10   7            Topic 5

ID 会自动编号,因此无需构造它,但显然需要保留父 ID

我怎样才能实现上述目标?我需要展平数据并在每次插入后进行 3 次单独的插入记录 id 吗?

4

2 回答 2

2

您可以使用递归 CTE 来获取要插入的行。如果您使用merge添加行,您可以使用output来捕获生成的 ID 和旧 ID 之间的映射,该映射可用于更新Parent_ID插入行的列。

-- ID for topic to copy 
declare @ID int;
set @ID = 2;

-- Table to hold the inserted rows
declare @T table
(
  New_ID int,
  Old_ID int,
  Old_ParentID int
);

-- Add rows from recursive CTE using merge
with C as
(
  select T.ID, T.Parent_Id, T.Topic_Name
  from YourTable as T
  where T.ID = @ID
  union all
  select T.ID, T.Parent_Id, T.Topic_Name
  from YourTable as T
    inner join C 
      on C.ID = T.Parent_Id
)
merge YourTable
using C
on 0 = 1
when not matched then
  insert (Topic_Name) values (C.Topic_Name)
output inserted.ID,
       C.ID,
       C.Parent_Id
  into @T(New_ID, Old_ID, Old_ParentID);

-- Update Parent_Id for the new rows
update Y set
  Parent_Id = T2.New_ID
from @T as T1
  inner join @T as T2
    on T1.Old_ParentID = T2.Old_ID
  inner join YourTable as Y
    on T1.New_ID = Y.ID;

SE-数据

于 2012-10-03T11:53:28.293 回答
1

工作示例。理论上可行,但对于高事务量可能不够健壮,或者对于大表来说不够快。

--- sample table
create table tbl (
  id int identity primary key,
  parent_id int references tbl(id),
  topic_name varchar(100));
insert tbl values
(       Null,  'Topic 1'),
(       1,  '   Topic 2'),
(       2   ,  '       Topic 3'),
(       3   ,  '          Topic 4'),
(       2   ,  '       Topic 5'),
(       Null,  '    Topic 6'),
(       4,  '    Topic 4-3'),
(       7,  '    Topic 5-4')
;

--- script to duplicate a hierarchy branch
declare @inserttbl table (
  id int,
  parent_id int,
  topic_name varchar(100));
;with cte as (
  select id, parent_id, topic_name
  from tbl
  where id=2 -- or parameter
  union all
  select t.id, t.parent_id, t.topic_name
  from tbl t
  join cte c on t.parent_id=c.id
), cte2 as (
  select *,rn=row_number() over (order by id)
  from cte
), cte3 as (
  select rec.*, par.rn as parent_rn
  from cte2 rec
  left join cte2 par on par.id=rec.parent_id
)
insert @inserttbl
select cte3.rn,
       case when cte3.rn=1 then cte3.parent_id
            else cte3.parent_rn end,
       topic_name
from cte3;

insert tbl(topic_name)
select topic_name from @inserttbl order by id;

declare @delta int=scope_identity()-@@rowcount;

update t
set parent_id = i.parent_id + case when i.id=1 then 0
                              else @delta end
from tbl t
join @inserttbl i on t.id - @delta = i.id;

--- check results
select * from tbl;
于 2012-10-03T11:09:52.613 回答