21

我有一个脚本,它使用s 和s中的子句T-SQL实现一些同步逻辑。OUTPUTMERGEINSERT

现在我在它上面添加一个日志记录层,我想添加第二个OUTPUT子句将值写入报告表。

我可以OUTPUT在我的声明中添加第二个子句MERGE

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;

这有效,但只要我尝试添加目标

INTO @report_table;

我之前收到以下错误消息INTO

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

我在这里找到了一个类似的问题,但它并没有进一步帮助我,因为我要插入的字段不会在两个表之间重叠,而且我不想修改工作同步逻辑(如果可能的话)。

更新:

在Martin Smith的回答之后,我有了另一个想法并重新编写了我的查询如下:

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

不幸的是,这种方法也不起作用,在运行时会输出以下错误消息:

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

因此,绝对没有办法OUTPUT在单个 DML 语句中包含多个子句。

4

4 回答 4

18

不可能。见语法

合并语句有

[ <output_clause> ]

方括号显示它可以有一个可选的输出子句。语法是

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

这个子句可以同时有anOUTPUT INTO和an,OUTPUT但不能有两个相同的。

如果允许多个语法将有[ ,...n ]

于 2013-06-18T10:11:32.003 回答
5

马丁·史密斯是对的,一个语句中不可能有两个OUTPUT INTO子句MERGE,但他也是对的,可以有一个OUTPUT INTO子句和一个OUTPUT子句。OUTPUT INTO将其结果集直接插入给定的表中,OUTPUT并将结果集返回给调用者。

因此,您可以将语句包装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

用法

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

这会将行插入sync_tablereport_table.

如果您检查执行计划,您会看到它INSERT ... EXEC在幕后创建了一个临时表(另请参见Adam Machanic的 The Hidden Costs of INSERT EXEC)。

于 2016-01-03T04:25:54.303 回答
4

OUTPUT子句允许一个可选择的列表。虽然这不允许多个结果集,但它确实允许一个结果集处理所有操作。

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

我自己忽略了这一点,直到前几天,当我需要知道为该行采取的操作不希望下游有复杂的逻辑时。这意味着你在这里有更多的自由。我做了类似于以下的事情,这使我能够以简单的方式使用输出:

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;

您最终会得到如下结果集:

| MergeAction | rowId | col1 | col2 |
| INSERT      | 3     | 1    | new  |
| UPDATE      | 1     | 2    | foo  |
| DELETE      | 2     | 3    | bar  |
于 2018-11-07T16:23:33.213 回答
1

很抱歉复活一个旧线程,但我刚刚遇到了这个问题并使用了一个实用而不是技术的解决方案,并且可能会或可能不会很明显。

正如已经讨论过的,MERGE 并不是为此而设计的。INSERT_INTO...EXEC 解决方案是一个很好的解决方法,但是我正在处理的特定存储过程已经足够复杂了。

因此,为了让下一个必须处理此代码的人保持简单,我只使用了两个 MERGE 语句……一个执行插入,一个执行更新。毕竟,没有法律规定您只能使用一种。我在日志记录表中添加了一个“操作”列,其中我有 MERGE 语句根据它在做什么插入“插入”或“更新”。

性能不会受到足够的影响,尤其是因为这不是用户进程。

提示:先更新,再插入。否则,当您进行第一次加载时,您会为导入的每一行获得一个插入记录和一个更新记录。

于 2017-07-14T19:22:00.240 回答