如何复制 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 语句中完成:
-- 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;
结果:
假设我们有一个像这样的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 语句中完成:
-- 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;
结果: