当前位置: 首页 > 面试题库 >

复制层次结构数据时保留父子关系

郗亦
2023-03-14
问题内容

我们有一个表,该表表示与实体(称为项目)关联的值树,其中ParentID列引用行的父级的id列。id列是一个自动递增的IDENTITY列和主键。根节点的ParentID为0。

我们希望能够克隆给定项目的数据,并使生成的ParentID引用复制值的相应新ID,其方式应满足示例中所述的限制。

例如,在下表中复制ProjectID 611的数据:

    id      ProjectID    Value         ParentID
--------------------------------------------------
     1      611           Animal        0
     2      611           Frog          1
    13      611           Cow           1
    14      611           Jersey Cow    13
    25      611           Plant         0
    29      611           Tree          25
    31      611           Oak           29

应导致:

    id      ProjectID    Value         ParentID
--------------------------------------------------
     1      611           Animal        0
     2      611           Frog          1
    13      611           Cow           1
    14      611           Jersey Cow    13
    25      611           Plant         0
    29      611           Tree          25
    31      611           Oak           29
    32      612           Animal        0
    33      612           Frog          32
    34      612           Cow           32
    35      612           Jersey Cow    34
    36      612           Plant         0
    37      612           Tree          36
    38      612           Oak           37

限制:

  • 解决方案必须适用于 SQL Server 2005 。也就是说,我们不能使用MERGE(alas)。
  • 我们不愿意对id或它们与ParentID的比较做出假设;例如,该解决方案原则上应适用于唯一ID / ID。
  • 我们不希望在表中添加其他列。(我当前的解决方案添加了一个“ OldId”列,该列在复制行时由复制过程设置。因此,我目前正在使用INSERT-SELECT和UPDATE-FROM的组合,将ParentId列上的OldId列加入以获得新ID 。)我们不希望所有带有OldId列的层次结构表都只是为了支持此复制操作。
  • 解决方案必须合理执行;我最初的解决方案是一组复杂的递归函数html" target="_blank">调用,并一次处理一个循环。我很快放弃了那条路线!

问题答案:

CTE与可以很好地配合使用MERGE,但是在SQL Server 2005中是有问题的。对于先前引起误解的注释,我们深表歉意。

下面显示了如何克隆项目(具有多棵树)并修正父项以将新森林与旧森林分开。请注意,它不依赖于ID的任何特定排列,例如,它们不必是密集的,单调递增的…。

-- Sample data.
declare @Projects as Table
  ( Id Int Identity, ProjectId Int, Value VarChar(16), ParentId Int Null );
insert into @Projects ( ProjectId, Value, ParentId ) values
  ( 611, 'Animal', 0 ),
  ( 611, 'Frog', 1 ),
  ( 611, 'Cow', 1 ),
  ( 611, 'Jersey Cow', 3 ),
  ( 611, 'Plant', 0 ),
  ( 611, 'Tree', 5 ),
  ( 611, 'Oak', 6 );
-- Display the raw data.
select * from @Projects;

-- Display the forest.
with IndentedProjects ( Id, ProjectId, Value, ParentId, Level, Path ) as
  ( -- Start with the top level rows.
  select Id, ProjectId, Value, ParentId, 0, Convert( VarChar(1024), Right( '000' + Convert( VarChar(4), Id ), 4 ) )
    from @Projects
    where ParentId = 0
  union all
  -- Add the children one level at a time.
  select P.Id, P.ProjectId, P.Value, P.ParentId, IP.Level + 1, Convert( VarChar(1024), IP.Path + '<' + Right( '000' + Convert( VarChar(4), P.Id ), 4 ) )
    from IndentedProjects as IP inner join
      @Projects as P on P.ParentId = IP.Id
  )
  select Space( Level * 2 ) + Value as [IndentedValue], Id, ProjectId, Value, ParentId, Level, Path
    from IndentedProjects
    order by Path;

-- Clone the project.
declare @OldProjectId as Int = 611;
declare @NewProjectId as Int = 42;
declare @Fixups as Table ( OldId Int, [NewId] Int );
begin transaction -- With suitable isolation since the hierarchy will be invalid until we apply the fixups!
insert into @Projects
  output Inserted.ParentId, Inserted.Id
    into @Fixups
  select @NewProjectId, Value, Id -- Note that we save the old Id in the new ParentId.
    from @Projects as P
    where ProjectId = @OldProjectId;
-- Apply the fixups.
update PNew
  set ParentId = IsNull( FNew.[NewId], 0 )
  -- Output the fixups just to show what is going on.
  output Deleted.Id, Deleted.ParentId as [ParentIdBeforeFixup], Inserted.ParentId as [ParentIdAfterFixup]
  from @Fixups as F inner join
    @Projects as PNew on PNew.Id = F.[NewId] inner join -- Rows we need to fix.
    @Fixups as FOld on FOld.OldId = PNew.ParentId inner join
    @Projects as POld on POld.Id = FOld.OldId left outer join
    @Fixups as FNew on FNew.OldId = POld.ParentId;
commit transaction;

-- Display the forest.
with IndentedProjects ( Id, ProjectId, Value, ParentId, Level, Path ) as
  ( -- Start with the top level rows.
  select Id, ProjectId, Value, ParentId, 0, Convert( VarChar(1024), Right( '000' + Convert( VarChar(4), Id ), 4 ) )
    from @Projects
    where ParentId =0
  union all
  -- Add the children one level at a time.
  select P.Id, P.ProjectId, P.Value, P.ParentId, IP.Level + 1, Convert( VarChar(1024), IP.Path + '<' + Right( '000' + Convert( VarChar(4), P.Id ), 4 ) )
    from IndentedProjects as IP inner join
      @Projects as P on P.ParentId = IP.Id
  )
  select Space( Level * 2 ) + Value as [IndentedValue], Id, ProjectId, Value, ParentId, Level, Path
    from IndentedProjects
    order by Path;


 类似资料:
  • 我正在尝试从本地主机将大量文件和文件夹复制到docker映像构建中。 文件如下所示: 我在试着做这样的副本: 但是,所有文件都放在/files/中,Docker中是否有办法保留子目录结构以及将文件复制到其目录中?

  • 我使用ProGuard混淆了我的代码。在代码中,我希望用下面的代码从指定的包中读取所有类。 com/test/ui/controller/b.class com/test/ui/controller/d.类 当我用一个未混淆的jar运行相同的代码时,下面是输出。包级别保存在jar中。你知道我如何告诉Proguard通过保持包级别来创建jar吗? com/test/ui/controller/d.类

  • 如果我有这样一门课: 获取节点的所有父层次结构的最简单/最优雅的方法是什么? 类似于:

  • 我们正在进行一场关于如何设计RESTendpoint的辩论。基本上可以归结为这个做作的例子。 假设我们有: 其中演员A是同一个演员。 这种分歧最终源于使用ember.js,它期望有一个特定的层次结构--不希望有多种方式来访问相同的数据(最终,这将真正导致少量的代码重复)。可以将ember.js映射为使用/actors/a,因此没有严格的技术限制,这实际上更多地是一个哲学问题。 我环顾四周,找不到任

  • 我想枢轴的数据帧像: 进入具有如下分层列的数据帧: 我尝试了,但它只会给我一个这样的框架: 如何更改列的层次结构?

  • 问题内容: 我正在尝试将多个文件和文件夹从本地主机复制到Docker映像构建中。 这些文件是这样的: 我正在尝试这样制作副本: 但是,所有文件都放在/ files /中,Docker中是否可以保留子目录结构以及将文件复制到其目录中? 问题答案: 使用以下Dockerfile从COPY中删除星号: 结构在那里: