我有一个T-SQL
脚本,使用OUTPUT
和MERGE
中的INSERT
子句实现了一些同步逻辑。
现在我在它上面添加一个日志记录层,我想添加第二个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 (;)
我找到a similar question here,但它对我没有帮助,因为我要插入的字段在两个表之间没有重叠,我不想修改工作同步逻辑(如果可能的话)。
更新
在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.
因此,在单个DML语句中绝对没有办法使用多个OUTPUT
子句。
答案 0 :(得分:16)
不可能。请参阅grammar。
Merge语句有
[ <output_clause> ]
方括号显示它可以有一个可选的输出子句。语法是
<output_clause>::=
{
[ OUTPUT <dml_select_list> INTO { @table_variable | output_table }
[ (column_list) ] ]
[ OUTPUT <dml_select_list> ]
}
此子句可以包含OUTPUT INTO
和OUTPUT
,但不能同时包含两个[ ,...n ]
。
如果允许多个语法,则语法将为{{1}}
答案 1 :(得分:3)
Martin Smith是对的,在一个OUTPUT INTO
声明中不可能有两个MERGE
条款,但他也是正确的 可能有一个{{}> {1}}和一个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_table
。
如果您检查执行计划,您会看到report_table
在幕后创建临时表(另请参阅The Hidden Costs of INSERT EXEC by
Adam Machanic)。
答案 2 :(得分:1)
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 |
答案 3 :(得分:0)
很抱歉复活旧线程,但我遇到了这个问题并使用了一个实用而非技术性的解决方案,可能会也可能不会很明显。
如前所述,MERGE不是为此而设计的。 INSERT_INTO ... EXEC解决方案是一个很好的解决方法,但我正在使用的特定存储过程已经非常复杂了。
因此,为了让下一个必须处理此代码的人保持简单,我只使用了两个MERGE语句...一个执行插入,一个执行更新。毕竟,没有法律规定你必须只使用一个。我在日志记录表中添加了一个“action”列,我将MERGE语句插入“插入”或“更新”,具体取决于它正在做什么。
性能并不需要担心,特别是因为这不是用户进程。
提示:首先进行更新,然后进行插入。否则,当您执行第一次加载时,您将为导入的每一行获得一条插入记录和一条更新记录。