【问题标题】:How to duplicate rows of self-referenced table如何复制自引用表的行
【发布时间】:2015-07-12 06:34:34
【问题描述】:

假设我们有一个像这样的自引用表

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 也应该更新,实体和他们的父母应该在同一个月。

例如假设我们有

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。

【问题讨论】:

  • 旁注:不要在您的name 列中使用char(128)!这将使 all 您的姓名长度为 128 个字符 - 较短的姓名用空格填充到定义的长度!这太糟糕了,浪费空间 - 使用 varchar(128) 代替它只使用所需的空间
  • 这只是一个例子,我在实际应用中使用 nvarchar(128),因为我需要 Unicode 字符。
  • 我能想到一种有点麻烦的方法,用旧的 id 和旧的父 id 将记录插入到临时表中,然后用正确的值将它们插入到主表中,但我敢肯定它可以以更直接的方式完成,所以我不会将其作为答案发布。
  • A.你确定你的表定义是合法的(特别是Month(Id) )?如果是这样,您是否只关心ParentId 列?
  • @Amit,我更正了架构定义。主要部分是parentId。

标签: sql sql-server sql-server-2012 hierarchical-data self-reference


【解决方案1】:

无需任何假设即可正常工作:

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;

结果:

【讨论】:

    【解决方案2】:

    如果我们可以依赖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 值的生成方式做出任何假设。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-06-20
      • 2012-04-17
      • 2011-02-16
      • 1970-01-01
      • 1970-01-01
      • 2017-05-10
      • 2014-08-10
      • 1970-01-01
      相关资源
      最近更新 更多