【问题标题】:What columns can be used in OUTPUT INTO clause?OUTPUT INTO 子句中可以使用哪些列?
【发布时间】:2008-09-30 22:08:43
【问题描述】:

我正在尝试构建一个映射表,以将表中新行的 ID 与从中复制它们的行关联起来。 OUTPUT INTO 子句似乎非常适合,但它的行为似乎不符合文档。

我的代码:

DECLARE @Missing TABLE (SrcContentID INT PRIMARY KEY )
INSERT INTO @Missing 
    ( SrcContentID ) 
SELECT cshadow.ContentID
    FROM Private.Content AS cshadow
    LEFT JOIN Private.Content AS cglobal ON cshadow.Tag = cglobal.Tag
    WHERE cglobal.ContentID IS NULL 

PRINT 'Adding new content headers'
DECLARE @Inserted TABLE (SrcContentID INT PRIMARY KEY, TgtContentID INT )
INSERT INTO Private.Content 
    ( Tag, Description, ContentDate, DateActivate, DateDeactivate, SortOrder, CreatedOn, IsDeleted, ContentClassCode, ContentGroupID, OrgUnitID ) 
    OUTPUT cglobal.ContentID, INSERTED.ContentID INTO @Inserted (SrcContentID, TgtContentID)
SELECT Tag, Description, ContentDate, DateActivate, DateDeactivate, SortOrder, CreatedOn, IsDeleted, ContentClassCode, ContentGroupID, NULL 
    FROM Private.Content AS cglobal
    INNER JOIN @Missing AS m ON cglobal.ContentID = m.SrcContentID

导致错误消息:

Msg 207, Level 16, State 1, Line 34
Invalid column name 'SrcContentID'.

(第 34 行是带有 OUTPUT INTO 的行)

实验表明,只有实际存在于 INSERT 目标中的行才能在 OUTPUT INTO 中选择。但这与在线书籍中的文档相矛盾。关于OUTPUT Clause的文章有一个描述类似用法的示例E:

OUTPUT INTO 子句返回值 从正在更新的表中 (WorkOrder) 以及来自产品 桌子。产品表用于 FROM 子句指定行 更新。

有人用过这个功能吗?

(与此同时,我已经重写了我的代码以使用游标循环来完成这项工作,但这很丑,我仍然很好奇)

【问题讨论】:

    标签: sql-server


    【解决方案1】:

    您可以在 Sql Server 2008 中使用 MERGE 来完成此操作。示例代码如下:

    --drop table A
    create table A (a int primary key identity(1, 1))
    insert into A default values
    insert into A default values
    
    delete from A where a>=3
    
    -- insert two values into A and get the new primary keys
    MERGE a USING (SELECT a FROM A) AS B(a)
    ON (1 = 0) -- ignore the values, NOT MATCHED will always be true
    WHEN NOT MATCHED THEN INSERT DEFAULT VALUES -- always insert here for this example
    OUTPUT $action, inserted.*, deleted.*, B.a; -- show the new primary key and source data
    

    结果是

    INSERT, 3, NULL, 1
    INSERT, 4, NULL, 2
    

    即对于每一行,新的主键 (3, 4) 和旧的主键 (1, 2)。创建一个名为例如的表#OUTPUT 并添加“ INTO #OUTPUT;”在 OUTPUT 子句的末尾将保存记录。

    【讨论】:

    【解决方案2】:

    我已验证问题在于您只能使用 INSERTED 列。文档似乎表明您可以使用from_table_name,但我似乎无法让它工作(无法绑定多部分标识符“m.ContentID”。):

    TRUNCATE TABLE main
    
    SELECT *
    FROM incoming
    
    SELECT *
    FROM main
    
    DECLARE @Missing TABLE (ContentID INT PRIMARY KEY)
    INSERT INTO @Missing(ContentID) 
    SELECT incoming.ContentID
    FROM incoming
    LEFT JOIN main
        ON main.ContentID = incoming.ContentID
    WHERE main.ContentID IS NULL
    
    SELECT *
    FROM @Missing
    
    DECLARE @Inserted TABLE (ContentID INT PRIMARY KEY, [Content] varchar(50))
    INSERT INTO main(ContentID, [Content]) 
    OUTPUT INSERTED.ContentID /* incoming doesn't work, m doesn't work */, INSERTED.[Content] INTO @Inserted (ContentID, [Content])
    SELECT incoming.ContentID, incoming.[Content] 
    FROM incoming
    INNER JOIN @Missing AS m
        ON m.ContentID = incoming.ContentID
    
    SELECT *
    FROM @Inserted
    
    SELECT *
    FROM incoming
    
    SELECT *
    FROM main
    

    显然,from_table_name 前缀只允许在 DELETEUPDATE(或 2008 年的 MERGE)上使用 - 我不知道为什么:

    • from_table_name

    是一个列前缀,用于指定包含在DELETEUPDATE 语句的FROM 子句中的表,用于指定要更新或删除的行。

    如果正在修改的表也在FROM 子句中指定,则对该表中列的任何引用都必须使用INSERTEDDELETED 前缀进行限定。

    【讨论】:

      【解决方案3】:

      我遇到了和你完全一样的问题,我感觉到你的痛苦...... 据我所知,没有办法将 from_table_name 前缀与 INSERT 语句一起使用。 我确信这有一个可行的技术原因,我很想知道它到底是什么。

      好的,找到了,这里有一篇关于为什么它不起作用的论坛帖子: MSDN forums

      【讨论】:

      • 它提供了解决方案的线索。合并声明!然后@Simon 设法提供语法。不错!
      【解决方案4】:

      我想我找到了解决这个问题的方法,遗憾的是它涉及一个临时表,但至少它可以防止创建一个可怕的游标 :) 您需要做的是在要从中复制记录的表中添加一个额外的列,并为其指定一个“唯一标识符”类型。

      然后声明一个临时表:

      DECLARE @tmptable TABLE (uniqueid uniqueidentifier, original_id int, new_id int)
      

      像这样将数据插入到您的临时表中:

      insert into @tmptable
      (uniqueid,original_id,new_id)
      select NewId(),id,0 from OriginalTable
      

      继续进行真正的插入到原始表中:

      insert into OriginalTable
      (uniqueid)
      select uniqueid from @tmptable
      

      现在将新创建的标识值添加到您的临时表中:

      update @tmptable
      set new_id = o.id
      from OriginalTable o inner join @tmptable tmp on tmp.uniqueid = o.uniqueid
      

      现在您有一个查找表,将新 id 和原始 id 保存在一条记录中,供您使用 :)

      我希望这对某人有所帮助...

      【讨论】:

      • 谢谢,但如果我需要跟踪的密钥是唯一标识符,这只是一个可行的解决方案。我要解决的整个问题是跟踪分配给新行的 IDENTITY 值。
      【解决方案5】:

      (MS) 如果正在修改的表也在 FROM 子句中指定,则对该表中列的任何引用都必须使用 INSERTED 或 DELETED 前缀进行限定。

      在您的示例中,您不能在 OUTPUT 中使用 cglobal 表,除非它是 INSERTED.column_name 或 DELETED.column_name:

      INSERT INTO Private.Content 
          (Tag) 
          OUTPUT cglobal.ContentID, INSERTED.ContentID 
          INTO @Inserted (SrcContentID, TgtContentID)
      SELECT Tag
          FROM Private.Content AS cglobal
          INNER JOIN @Missing AS m ON cglobal.ContentID = m.SrcContentID
      

      对我有用的是一个简单的别名表,如下所示:

      INSERT INTO con1 
          (Tag) 
          OUTPUT **con2**.ContentID, INSERTED.ContentID 
          INTO @Inserted (SrcContentID, TgtContentID)
      SELECT Tag
          FROM Private.Content con1
          **INNER JOIN Private.Content con2 ON con1.id=con2.id**
          INNER JOIN @Missing AS m ON con1.ContentID = m.SrcContentID
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-09-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多