如何复制 self-referenced table 的行

How to duplicate rows of self-referenced table

假设我们有一个像这样的self-referencedtable

CREATE TABLE Month
(
  Id int IDENTITY(1,1)  PRIMARY KEY,
  Title char(128)
)
CREATE TABLE Entity
(
 Id int IDENTITY(1,1)  PRIMARY KEY,
 MonthId int FOREIGN KEY REFERENCES Month(Id),
 Name char(128),
 ParentId int FOREIGN KEY REFERENCES Entity(Id),
)

我想将某个 MonthId 的所有行复制到另一个 MonthId。重复的 parentId 也应该更新,实体及其 parents 应该在同一个月。

例如假设我们有

Id        MonthId    Name     ParentId
------------------------------------
1         1          name1     null
2         1          name11    1
3         1          name3     null 
4         1          name31    3
5         1          name311   4

将 monthId=1 行复制到 monthId=2 后,结果应如下所示:

Id        MonthId    Name     ParentId
------------------------------------
1         1          name1     null
2         1          name11    1
3         1          name3     null 
4         1          name31    3
5         1          name311   4
newId1    2          name1     null
newId2    2          name11    newId1
newId3    2          name3     null 
newId4    2          name31    newId3
newId5    2          name311   newId4

newId 是 DBMS 生成的值。

注意:我使用 Sql-Server 2012 作为 DBMS。

如果我们可以依赖 Entity.Name 对于给定的 Entity.MonthId 是唯一的,那么它可以在 2 SQL 语句中完成:

SQLFiddle

-- copy records, but don't set the ParentId yet.
INSERT INTO Entity (MonthId, Name, ParentId)
SELECT 2, Name, null
FROM Entity
WHERE MonthId = 1;

-- set the ParentId in the 2nd step.
UPDATE e
SET e.ParentId = (
  SELECT parentNew.Id
  FROM Entity innerOld
  JOIN Entity parentOld
    ON parentOld.Id = innerOld.ParentId
  JOIN Entity parentNew
    ON parentNew.MonthId = e.MonthId
   AND parentNew.Name = parentOld.Name
  WHERE innerOld.MonthId = 1
    AND innerOld.Name = e.Name
)
FROM Entity e
WHERE e.MonthId = 2;

这种方法的一个优点是它不会对 Id 值的生成方式做出任何假设。

这在没有任何假设的情况下工作正常:

DECLARE @baseMonthId int = 1
DECLARE @newMonthId int = 2

DECLARE @newRows TABLE(id int, orig_id int)

MERGE INTO Entity
USING (
  SELECT Id, Name, ParentId FROM Entity WHERE MonthId = @baseMonthId
) AS cf
ON 1 = 0
WHEN NOT MATCHED THEN
  INSERT(MonthId, Name, ParentId) Values(@newMonthId, cf.Name, cf.ParentId)
OUTPUT inserted.Id, cf.Id INTO @newRows(id, orig_id);

UPDATE Entity
SET Parentid = 
  ( 
    SELECT 
      nr.id
    FROM @newRows nr
      WHERE nr.orig_id = Entity.ParentId
   )
WHERE MonthId = @newMonthId;

结果: