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

Ale*_* B. 113 sql sql-server

我对我的数据库做了一些修改,我需要将旧数据迁移到新表.为此,我需要填充表(ReportOptions)从原始表(练习)中获取数据,并填写第二个中间表(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...)
Run Code Online (Sandbox Code Playgroud)

我提出了一个查询来获取从Practice转到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
Run Code Online (Sandbox Code Playgroud)

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

提前致谢.

Gar*_*thD 172

您可以使用MERGE而不是insert 来执行此操作:

所以取代这个

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
Run Code Online (Sandbox Code Playgroud)

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);
Run Code Online (Sandbox Code Playgroud)

关键是在merge语句中使用永远不会为真(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 
Run Code Online (Sandbox Code Playgroud)

更多的阅读,以及我所知道的关于这个主题的所有来源就在这里

  • 被警告。我使用了一个合并语句,该语句在过去一年中随着使用量的增加而增长。我们开始在保存期间超时,结果是因为合并语句总是锁定表,我们每 4 分钟锁定 35-160 秒的表。我必须重建几个合并语句以使用插入/更新并将它们更新的行数限制为每次插入/更新 500 以避免表锁定。我估计这个非常重要的表每天被锁定近 2 1/2 小时,这导致了从缓慢保存到超时的所有问题。 (4认同)
  • 谢谢,这行得通!我打算使用一个假的临时字段,但这更优雅。 (2认同)
  • 同样,对许多人来说,一个破坏交易的因素是,MERGE中有很多未修复的错误,这些错误会在怪异的条件下出现。例如,请参阅Aaron Bertrand的[本文](https://www.mssqltips.com/sqlservertip/3074/use-caution-with-sql-servers-merge-statement/)。Microsoft拒绝修复其中的一些,而我的秘密怀疑是MS不推荐使用它们的整个[MS Connect](http://connect.microsoft.com)服务,只是试图使我们忘记MERGE语句中的所有错误。他们不想修复。 (2认同)

Val*_*Val 13

也许使用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
Run Code Online (Sandbox Code Playgroud)

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

  • 这不能解决我的问题。我需要将我的“插入”“输出”到输出“表”,其中包含来自目标“表”的“身份”值以及来自源“表”的非“插入”值(PK) (顺便说一句,这样我就可以(在不同的批次中)使用该输出“Table”用“Identity”值填充源“Table”中的“Column”)。如果没有“合并”,我想我必须:a)开始“交易”,b)获取下一个“身份”,c)用计算出的“身份”插入临时“表”,d)设置“身份插入”, e) 从临时“Table”“插入”到目标“Table”,f) 清除“Identity_Insert”。 (3认同)