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

时间:2008-09-30 22:08:44

标签: sql-server

我正在尝试构建一个映射表,以将表中新行的ID与它们从中复制的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的那个)

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

  

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

是否有人使用此功能?

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

5 个答案:

答案 0 :(得分:21)

您可以在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)。创建一个名为eg的表#OUTPUT并添加“INTO #OUTPUT;”在OUTPUT子句的末尾将保存记录。

答案 1 :(得分:13)

我已经确认问题是您只能使用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

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

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

答案 2 :(得分:6)

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

好的,发现它,这是一个论坛帖子,说明为什么它不起作用: MSDN forums

答案 3 :(得分:0)

我想我找到了解决这个问题的方法,遗憾的是它涉及一个临时表,但至少它会阻止创建一个可怕的游标:) 你需要做的是在你正在复制记录的表中添加一个额外的列,并给它一个'uniqueidentifer'类型。

然后声明一个临时表:

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,为了你的愉快:)

我希望这有助于某人...

答案 4 :(得分:0)

(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