SQL Output子句是否可能返回未插入的列?


123

我已经对数据库进行了一些修改,并且需要将旧数据迁移到新表中。为此,我需要填充一个表(ReportOptions),该表从原始表(Practice)中获取数据,并填充第二个中间表(PracticeReportOption)。

ReportOption (ReportOptionId int PK, field1, field2...)
Practice (PracticeId int PK, field1, field2...)
PracticeReportOption (PracticeReportOptionId int PK, PracticeId int FK, ReportOptionId int FK, field1, field2...)

我进行了查询,以获取从练习到ReportOptions所需的所有数据,但是我无法填写中间表

--Auxiliary tables
DECLARE @ReportOption TABLE (PracticeId int /*This field is not on the actual ReportOption table*/, field1, field2...)
DECLARE @PracticeReportOption TABLE (PracticeId int, ReportOptionId int, field1, field2)

--First I get all the data I need to move
INSERT INTO @ReportOption
SELECT P.practiceId, field1, field2...
  FROM Practice P

--I insert it into the new table, but somehow I need to have the repation PracticeId / ReportOptionId
INSERT INTO ReportOption (field1, field2...)
OUTPUT @ReportOption.PracticeId, --> this is the field I don't know how to get
       inserted.ReportOptionId
  INTO @PracticeReportOption (PracticeId, ReportOptionId)
SELECT field1, field2
  FROM @ReportOption

--This would insert the relationship, If I knew how to get it!
INSERT INTO @PracticeReportOption (PracticeId, ReportOptionId)
SELECT PracticeId, ReportOptionId
  FROM @ReportOption

如果我可以在OUTPUT子句上引用不在目标表上的字段,那将很棒(我认为我不能,但是我不确定)。关于如何满足我的需求的任何想法?


1
您可以在OUTPUT子句中返回插入行的表的任何列。因此,即使您没有为INSERT语句中的给定列提供值,您仍然可以在OUTPUT子句中指定该列。但是,您不能从其他表返回SQL变量或列。
marc_s 2012年

2
@marc_s感谢您的答复,但目标表中没有我需要的字段(我需要PracticeId,该字段不在ReportOption上)
Alejandro B.

Answers:


191

您可以使用MERGE而不是插入来做到这一点:

所以取代这个

INSERT INTO ReportOption (field1, field2...)
OUTPUT @ReportOption.PracticeId, --> this is the field I don't know how to get
       inserted.ReportOptionId
  INTO @PracticeReportOption (PracticeId, ReportOptionId)
SELECT field1, field2
  FROM @ReportOption

MERGE INTO ReportOption USING @ReportOption AS temp ON 1 = 0
WHEN NOT MATCHED THEN
    INSERT (field1, field2)
    VALUES (temp.Field1, temp.Field2)
    OUTPUT temp.PracticeId, inserted.ReportOptionId, inserted.Field1, inserted.Field2
    INTO @PracticeReportOption (PracticeId, ReportOptionId, Field1, Field2);

关键是要使用在合并搜索条件中永远不会为真的谓词(1 = 0),因此您将始终执行插入操作,但可以访问源表和目标表中的字段。


这是我用来测试的全部代码:

CREATE TABLE ReportOption (ReportOptionID INT IDENTITY(1, 1), Field1 INT, Field2 INT)
CREATE TABLE Practice (PracticeID INT IDENTITY(1, 1), Field1 INT, Field2 INT)
CREATE TABLE PracticeReportOption (PracticeReportOptionID INT IDENTITY(1, 1), PracticeID INT, ReportOptionID INT, Field1 INT, Field2 INT)

INSERT INTO Practice VALUES (1, 1), (2, 2), (3, 3), (4, 4)


MERGE INTO ReportOption r USING Practice p ON 1 = 0
WHEN NOT MATCHED THEN
    INSERT (field1, field2)
    VALUES (p.Field1, p.Field2)
    OUTPUT p.PracticeId, inserted.ReportOptionId, inserted.Field1, inserted.Field2
    INTO PracticeReportOption (PracticeId, ReportOptionId, Field1, Field2);

SELECT  *
FROM    PracticeReportOption

DROP TABLE ReportOption
DROP TABLE Practice
DROP TABLE PracticeReportOption 

阅读更多,我所知道的关于该主题的所有资源都在这里


2
谢谢,这可以解决问题!我本打算使用假的临时字段,但这要优雅得多。
Alejandro B.

1
优秀的!这个招是一粒金子!添加到集合的第一行!
Vadim Loboda

1
Suweet!我希望它不必偶尔使用有问题的MERGE命令,但否则就非常优雅。
Tab Alleman'1

4
被警告。我使用了一个合并语句,该语句在过去一年中随着使用量的增长而增长。我们在保存期间开始出现超时,结果是由于merge语句始终锁定表,因此每4分钟就有35-160秒的表锁定时间。我必须重建几个合并语句以使用插入/更新,并将它们更新的行数限制为每个插入/更新500,以避免表锁定。我估计这个非常重要的表每天将被锁定近2 1/2小时,这导致从慢速保存到超时的所有问题。
CubeRoot

3
同样,对许多人来说,一个破坏交易的因素是,MERGE中有很多未修复的错误,这些错误会在怪异的条件下出现。例如,请参阅Aaron Bertrand的这篇文章。Microsoft拒绝修复其中的一些,我的秘密怀疑是MS弃用了它们的整个MS Connect服务,只是试图使我们忘记他们不想修复的MERGE语句中的所有错误。
逆向工程师,

14

也许使用MS SQL Server 2005或更低版本的人会发现此答案很有用。


MERGE仅适用于SQL Server 2008或更高版本。休息一下,我发现了另一个解决方法,它使您能够创建映射表。

这是SQL 2005解析度的样子:

DECLARE @ReportOption TABLE (ReportOptionID INT IDENTITY(1, 1), Field1 INT, Field2 INT)
DECLARE @Practice TABLE(PracticeID INT IDENTITY(1, 1), Field1 INT, Field2 INT)
DECLARE @PracticeReportOption TABLE(PracticeReportOptionID INT IDENTITY(1, 1), PracticeID INT, ReportOptionID INT, Field1 INT, Field2 INT)

INSERT INTO @Practice (Field1, Field2) VALUES (1, 1)
INSERT INTO @Practice (Field1, Field2) VALUES (2, 2)
INSERT INTO @Practice (Field1, Field2) VALUES (3, 3)
INSERT INTO @Practice (Field1, Field2) VALUES (4, 4)

INSERT INTO @ReportOption (field1, field2)
    OUTPUT INSERTED.ReportOptionID, INSERTED.Field1, INSERTED.Field2 INTO @PracticeReportOption (ReportOptionID, Field1, Field2)
    SELECT Field1, Field2 FROM @Practice ORDER BY PracticeID ASC;


WITH CTE AS ( SELECT PracticeID, ROW_NUMBER() OVER ( ORDER BY PracticeID ASC ) AS ROW FROM @Practice )
UPDATE M SET M.PracticeID = S.PracticeID 
    FROM @PracticeReportOption AS M
    JOIN CTE AS S ON S.ROW = M.PracticeReportOptionID

    SELECT * FROM @PracticeReportOption

主要技巧是用源表和目标表中的有序数据填充映射表两次。有关更多详细信息,请参见:在SQL Server 2005中使用OUTPUT合并插入的数据


1
这不会解决我的问题。我需要OutputInsert的一个输出Table,其包括Identity从目标值Table与非Insert从源-ed值(PK) Table(顺便说一句,所以我可以然后(在不同的批次)使用该输出Table来填充ColumnTable带有Identity值的来源)。如果没有a Merge,我想我必须:a)开始Transaction,b)接下来Identity,c)插入已Table计算的temp Identity,d)设置Identity_Insert,e)从temp Insert插入目标,f)clear 。TableTableIdentity_Insert
汤姆(Tom)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.