OUTPUT INTO 子句中可以使用哪些列?

Posted

技术标签:

【中文标题】OUTPUT INTO 子句中可以使用哪些列?【英文标题】:What columns can be used in OUTPUT INTO clause? 【发布时间】: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 子句指定行 更新。

有人用过这个功能吗?

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

【问题讨论】:

【参考方案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 子句的末尾将保存记录。

【讨论】:

MERGE 语句是我最好的新朋友 :) 这里有很好的解释和例子:sqlblog.com/blogs/jamie_thomson/archive/2010/01/06/… 把这个留在这里:mssqltips.com/sqlservertip/3074/…【参考方案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

【讨论】:

以上是关于OUTPUT INTO 子句中可以使用哪些列?的主要内容,如果未能解决你的问题,请参考以下文章

如果语句包含没有 INTO 子句的 OUTPUT 子句,则 DML 语句不能有任何启用的触发器

如果语句包含没有 INTO 子句的 OUTPUT 子句,则 DML 语句的目标表“RECEIPT”不能有任何启用的触发器

使用 insert into select 将源列添加到输出子句

简单选择给了我:PLS-00428:需要一个 INTO 子句[重复]

使用 RETURNING INTO 子句和 BULK COLLECT 时如何返回整行

选择列表中的列无效,因为该列没有包含在聚合函数或 GROUP BY 子句中