简体   繁体   English

MERGE / INSERT / DELETE SQL命令中有多个OUTPUT子句?

[英]Multiple OUTPUT clauses in MERGE/INSERT/DELETE SQL commands?

I have a T-SQL script that implements some synchronization logic using OUTPUT clause in MERGE s and INSERT s. 我有一个T-SQL脚本,它使用MERGEINSERTOUTPUT子句实现一些同步逻辑。

Now I am adding a logging layer over it and I would like to add a second OUTPUT clause to write the values into a report table. 现在我在它上面添加一个日志层,我想添加第二个OUTPUT子句将值写入报表。

I can add a second OUTPUT clause to my MERGE statement: 我可以在我的MERGE语句中添加第二个OUTPUT子句:

MERGE TABLE_TARGET AS T
USING TABLE_SOURCE AS S
ON (T.Code = S.Code) 
WHEN MATCHED AND T.IsDeleted = 0x0
    THEN UPDATE SET ....
WHEN NOT MATCHED BY TARGET 
    THEN INSERT ....
OUTPUT inserted.SqlId, inserted.IncId
INTO @sync_table
OUTPUT $action, inserted.Name, inserted.Code;

And this works, but as long as I try to add the target 这是有效的,但只要我尝试添加目标

INTO @report_table;

I get the following error message before INTO : 我在INTO之前收到以下错误消息:

A MERGE statement must be terminated by a semicolon (;)

I found a similar question here , but it didn't help me further, because the fields I am going to insert do not overlap between two tables and I don't want to modify the working sync logic (if possible). 我在这里找到了一个类似的问题 ,但它对我没有帮助,因为我要插入的字段在两个表之间不重叠,我不想修改工作同步逻辑(如果可能的话)。

UPDATE: 更新:

After the answer by Martin Smith I had another idea and re-wrote my query as following: 马丁史密斯的回答后,我有了另一个想法并重新编写了我的查询如下:

INSERT INTO @report_table (action, name, code)
SELECT M.Action, M.Name, M.Code
FROM
(
MERGE TABLE_TARGET AS T
USING TABLE_SOURCE AS S
ON (T.Code = S.Code) 
WHEN MATCHED AND T.IsDeleted = 0x0
    THEN UPDATE SET ....
WHEN NOT MATCHED BY TARGET 
    THEN INSERT ....
OUTPUT inserted.SqlId, inserted.IncId
INTO @sync_table
OUTPUT $action as Action, inserted.Name, inserted.Code
) M

Unfortunately this approach did not work either, the following error message is output at runtime: 不幸的是,这种方法也不起作用,在运行时输出以下错误消息:

An OUTPUT INTO clause is not allowed in a nested INSERT, UPDATE, DELETE, or MERGE statement.

So, there is definitely no way to have multiple OUTPUT clauses in a single DML statement. 因此,在单个DML语句中肯定无法使用多个OUTPUT子句。

Not possible. 不可能。 See the grammar . 语法

The Merge statement has Merge语句有

[ <output_clause> ]

The square brackets show it can have an optional output clause. 方括号显示它可以有一个可选的输出子句。 The grammar for that is 这个语法是

<output_clause>::=
{
    [ OUTPUT <dml_select_list> INTO { @table_variable | output_table }
        [ (column_list) ] ]
    [ OUTPUT <dml_select_list> ]
}

This clause can have both an OUTPUT INTO and an OUTPUT but not two of the same. 该子句可以同时具有OUTPUT INTOOUTPUT但不能同时具有两个。

If multiple were allowed the grammar would have [ ,...n ] 如果允许多个语法,那么[ ,...n ]

Martin Smith is right, it is not possible to have two OUTPUT INTO clauses in one MERGE statement, but he is also right that it is possible to have one OUTPUT INTO and one OUTPUT clause. 马丁·史密斯是正确的,它是不可能有两个OUTPUT INTO一个条款MERGE语句,但他也是正确的,它可能有一个OUTPUT INTO和一个OUTPUT子句。 OUTPUT INTO inserts its result set directly into the given table and the simple OUTPUT returns result set to the caller. OUTPUT INTO将其结果集直接插入给定表中,简单OUTPUT将结果集返回给调用者。

So, you can wrap the MERGE statement into a stored procedure and then use INSERT ... EXEC to insert result set of the simple OUTPUT into a second table. 因此,您可以将MERGE语句包装到存储过程中,然后使用INSERT ... EXEC将简单OUTPUT结果集插入到第二个表中。

CREATE PROCEDURE [dbo].[TestMerge]
AS
BEGIN
    SET NOCOUNT ON;
    SET XACT_ABORT ON;

    MERGE TABLE_TARGET AS T
    USING TABLE_SOURCE AS S
    ON (T.Code = S.Code) 
    WHEN MATCHED AND T.IsDeleted = 0x0
        THEN UPDATE SET ....
    WHEN NOT MATCHED BY TARGET 
        THEN INSERT ....
    OUTPUT inserted.SqlId, inserted.IncId
    INTO sync_table
    OUTPUT $action AS MergeAction, inserted.Name, inserted.Code;
END

Usage 用法

INSERT INTO report_table
EXEC [dbo].[TestMerge];

This will insert rows into sync_table and into report_table . 这会将行插入sync_tablereport_table

If you examine execution plan you'll see that INSERT ... EXEC creates a temporary table behind the scenes (see also The Hidden Costs of INSERT EXEC by Adam Machanic). 如果检查执行计划,您将看到INSERT ... EXEC在幕后创建一个临时表(另请参阅Adam Machanic 的INSERT EXEC的隐藏成本 )。

The OUTPUT clause allows for a selectable list. OUTPUT子句允许可选择的列表。 While this doesn't allow for multiple result sets, it does allow for one result set addressing all actions. 虽然这不允许多个结果集,但它确实允许一个结果集处理所有操作。

<output_clause>::=
{
    [ OUTPUT <dml_select_list> INTO { @table_variable | output_table }
        [ (column_list) ] ]
    [ OUTPUT <dml_select_list> ]
}

I overlooked this myself until just the other day, when I needed to know the action taken for the row didn't want to have complicated logic downstream. 我自己忽略了这一点,直到前几天,当我需要知道行所采取的行动不希望下游有复杂的逻辑时。 The means you have a lot more freedom here. 这意味着你在这里拥有更多的自由。 I did something similar to the following which allowed me to use the output in a simple means: 我做了类似于以下的事情,这使我能够以简单的方式使用输出:

DECLARE @MergeResults TABLE (
    MergeAction VARCHAR(50),
    rowId INT NOT NULL,
    col1 INT NULL,
    col2 VARCHAR(255) NULL
    )

MERGE INTO TARGET_TABLE AS t
    USING SOURCE_TABLE AS s
    ON t.col1 = s.col1
WHEN MATCHED
    THEN
        UPDATE
        SET [col2] = s.[col2]
WHEN NOT MATCHED BY TARGET
    THEN
        INSERT (
            [col1]
            ,[col2]
            )
        VALUES (
            [col1]
            ,[col2]
            )
WHEN NOT MATCHED BY SOURCE
    THEN
        DELETE
OUTPUT $action as MergeAction, 
    CASE $action 
        WHEN 'DELETE' THEN deleted.rowId 
        ELSE inserted.rowId END AS rowId,
    CASE $action 
        WHEN 'DELETE' THEN deleted.col1 
        ELSE inserted.col1 END AS col1,
    CASE $action 
        WHEN 'DELETE' THEN deleted.col2 
        ELSE inserted.col2 END AS col2
    INTO @MergeResults;

You'll end up with a result set like: 你最终得到的结果如下:

| MergeAction | rowId | col1 | col2 |
| INSERT      | 3     | 1    | new  |
| UPDATE      | 1     | 2    | foo  |
| DELETE      | 2     | 3    | bar  |

Sorry to resurrect an old thread, but I just ran into this issue and used a solution that's practical rather than technical, and may or may not be obvious. 很抱歉复活旧线程,但我遇到了这个问题并使用了一个实用而非技术的解决方案,可能会也可能不会很明显。

As already discussed, MERGE isn't designed to do this. 如前所述,MERGE并非旨在实现此目的。 The INSERT_INTO...EXEC solution is a good workaround, but the particular stored procedure on which I'm working is already complex enough. INSERT_INTO ... EXEC解决方案是一个很好的解决方法,但我正在使用的特定存储过程已经非常复杂了。

So to keep things simple for the next guy who has to work on this code, I just used two MERGE statements...one that does the inserting and one that does the updating. 因此,为了让下一个必须处理此代码的人保持简单,我只使用了两个MERGE语句...一个执行插入,另一个执行更新。 After all, there's no law that says you have to use only one. 毕竟,没有法律规定你必须只使用一个。 I added an "action" column to the logging table, into which I have the MERGE statement insert either "Insert" or "Update" depending on what it's doing. 我在日志记录表中添加了一个“action”列,我将MERGE语句插入“插入”或“更新”,具体取决于它正在做什么。

Performance doesn't take enough of a hit to worry about, especially since this isn't a user process. 性能并不需要担心,特别是因为这不是用户进程。

TIP: Do the update first, and the insert second. 提示:首先进行更新,然后进行插入。 Otherwise, when you do the first load, you'll get one insert record and one update record for every row you import. 否则,当您执行第一次加载时,您将为导入的每一行获得一条插入记录和一条更新记录。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM