我正在尝试构建一个映射表,以将表中新行的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子句指定要的行 更新
是否有人使用此功能?
(与此同时,我已经使用游标循环重写了我的代码以完成这项工作,但这很丑陋而且我仍然很好奇)
答案 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
前缀仅允许DELETE
或UPDATE
(或2008年MERGE
) - 我不确定原因:
from_table_name
是一个列前缀,用于指定FROM
或DELETE
语句的UPDATE
子句中包含的表,用于指定要更新或删除的行。
如果在FROM
子句中也指定了要修改的表,则必须使用INSERTED
或DELETED
前缀限定对该表中列的任何引用。
答案 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