【问题标题】:How to correlate inserted IDs with selected source row IDs?如何将插入的 ID 与选定的源行 ID 相关联?
【发布时间】:2015-08-12 12:42:23
【问题描述】:

我正在尝试对一组规范化程度不高的分层表中的一系列引用行执行一些复杂的复制操作。不幸的是,这些表使用代理键,但在层次结构中存在一些实例,其中某些表具有代理键但在表中没有有效的唯一性属性。因此,当我在每个分层表中插入复制的行时,在某些情况下我无法避免重复记录,因为我无法正确区分哪些是有效的重复行。遗憾的是,我无法更改此数据库的结构。

我使用的技术是基于 SELECT 语句的结果进行 INSERT,其中 select 中的每一行将形成源实体层次结构的行连接在一起;从这个连接结果中,复制的内容被取出并插入到代表目标层次结构的行中。在 join 语句中,使用代理键 ID。例如:

INSERT INTO [PersonTable]
SELECT [sourcePerson].[Content], [destParent].[Id]
FROM [GrandParentTable] [sourceGrandParent]
JOIN [ParentTable] [sourceParent] ON [sourceParent].[GrandParentId] = [sourceGrandParent].[Id]
JOIN [PersonTable] [sourcePerson] ON [sourcePerson].[ParentId] = [sourceParent].[Id]
JOIN [GrandParentTable] [destGrandParent] ON [destGrandParent].[Name] = 'CopyTo'
JOIN [ParentTable] [destParent] ON [destParent].[GrandParentId] = [destGrandParent].[Id]
WHERE [sourceGrandParent].[Name] = 'CopyFrom'

我的问题是这个假设的表 [PersonTable] 没有足够的列或约束来唯一标识其中的行,所以如果我想随后从另一个表 [ChildTable] 复制行,我会得到重复的行执行后续查询:

INSERT INTO [ChildTable]
SELECT [sourceChild].[Content], [destPerson].[Id]
FROM [GrandParentTable] [sourceGrandParent]
JOIN [ParentTable] [sourceParent] ON [sourceParent].[GrandParentId] = [sourceGrandParent].[Id]
JOIN [PersonTable] [sourcePerson] ON [sourcePerson].[ParentId] = [sourceParent].[Id]
JOIN [ChildTable] [sourceChild] ON [sourceChild].[PersonId] = [sourcePerson].[Id]
JOIN [GrandParentTable] [destGrandParent] ON [destGrandParent].[Name] = 'CopyTo'
JOIN [ParentTable] [destParent] ON [destParent].[GrandParentId] = [destGrandParent].[Id]
JOIN [PersonTable] [destPerson] ON [destPerson].[ParentId] = [destParent].[Id]
WHERE [sourceGrandParent].[Name] = 'CopyFrom'

我希望解决此问题的一种方法是以某种方式将复制的行 ID 与源 ID 关联起来,并将它们存储在可以替换这些连接的临时表中。我想为此使用 OUTPUT 子句,以便我可以获取插入行的 [Id] 值,但即使您应该能够从连接表中输出任何内容似乎是明智的,但事实并非如此。

假设下一个示例已经存在一个我们用有效 ID 对填充的表:

CREATE TABLE #tempParentIds
(
    [destParentId] int
    [sourceParentId] int
)

我们尝试使用以下语句填充层次结构中的下一层:

CREATE TABLE #tempPersonIds
(
    [destPersonId] int
    [sourcePersonId] int
)
INSERT INTO [PersonTable] ([Content], [ParentId])
OUTPUT INSERTED.[Id], [sourcePerson].[Id] INTO #tempPersonIds
SELECT [sourcePerson].[Content], [destPerson].[ParentId]
FROM #tempParentIds [tempParent]
JOIN [PersonTable] [sourcePerson] ON [sourcePerson].[ParentID] = [tempParent].[sourceParentId]
JOIN [PersonTable] [destPerson] ON [destPerson].[ParentId] = [tempParent].[destParentId]

但是,该语句不会执行,因为 [sourcePerson] 在 OUTPUT 子句中不可访问,因此我们无法跟踪源行和从中复制的目标行。

除了从插入行的列之外获取信息之外,还有其他方法可以从插入行获取信息吗?如果这是可能的,我很想知道怎么做,但如果我根据我的问题描述以错误的方式解决这个问题,请随时让我直截了当。

【问题讨论】:

  • 我会考虑一个触发器。所以每次我插入一行时,我都会插入一个临时表。

标签: sql sql-server tsql


【解决方案1】:

这是诀窍:

INSERT 语句不能 OUTPUT 不在 INSERTED 中的源字段,但 MERGE 语句可以。

merge into dbo.PersonTable as target
    using 
    (
        SELECT
            sourcePerson.ID, 
            sourcePerson.Content, 
            destPerson.ParentId,
        FROM 
            #tempParentIds tempParent
            JOIN PersonTable sourcePerson ON sourcePerson.ParentID = tempParent.sourceParentId
            JOIN PersonTable destPerson ON destPerson.ParentId = tempParent.destParentId
    ) 
    as source on source.ID * (-1) = target.ID 

when not matched by target then                                                     
    insert (Content, ParentId)
    values (source.Content, source.ParentId)

output inserted.ID, source.ID
into #tempPersonIds ( destPersonId, sourcePersonId);

【讨论】:

  • 这是一个很棒的提示。更多喜欢合并命令的理由。谢谢!
猜你喜欢
  • 1970-01-01
  • 2018-09-27
  • 2013-09-27
  • 2017-12-17
  • 2016-03-04
  • 1970-01-01
  • 2016-06-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多