合并/插入/删除SQL命令中有多个输出子句?
我有一个合并/插入/删除SQL命令中有多个输出子句?,sql,sql-server,tsql,Sql,Sql Server,Tsql,我有一个T-SQL脚本,它使用MERGEs和INSERTs中的OUTPUT子句实现一些同步逻辑 现在,我在它上面添加了一个日志层,我想添加第二个OUTPUT子句来将值写入报告表 我可以在我的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 .... WH
T-SQL
脚本,它使用MERGE
s和INSERT
s中的OUTPUT
子句实现一些同步逻辑
现在,我在它上面添加了一个日志层,我想添加第二个OUTPUT
子句来将值写入报告表
我可以在我的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;
这是可行的,但只要我尝试添加目标
INTO @report_table;
我在进入之前收到以下错误消息:
A MERGE statement must be terminated by a semicolon (;)
我找到了,但它没有进一步帮助我,因为我要插入的字段在两个表之间没有重叠,我不想修改工作同步逻辑(如果可能的话)
更新:
在回答之后,我有了另一个想法,并重新编写了我的问题,如下所示:
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语句中绝对不可能有多个输出
子句。不可能。看
Merge语句已被删除
[ <output_clause> ]
[]
方括号显示它可以有一个可选的输出子句。这方面的语法是
<output_clause>::=
{
[ OUTPUT <dml_select_list> INTO { @table_variable | output_table }
[ (column_list) ] ]
[ OUTPUT <dml_select_list> ]
}
::=
{
[输出到{@table_variable | OUTPUT_table}
[(列清单)]]
[输出]
}
此子句可以同时具有输出到
和输出
,但不能同时具有两个
如果允许多个,语法将有
[,…n]
Martin Smith是正确的,在一个合并语句中不可能有两个输出到子句中,但他也正确地认为可以有一个输出到和一个输出到子句中OUTPUT-INTO
将其结果集直接插入给定的表中,简单的OUTPUT
将结果集返回给调用者
因此,您可以将MERGE
语句包装到存储过程中,然后使用INSERT。。。EXEC
将简单输出的结果集插入第二个表
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_表
和report_表
中插入行
如果您检查执行计划,您将看到INSERT。。。EXEC
在幕后创建一个临时表(另请参见
(亚当·麦卡尼奇)很抱歉,我重提了一个旧线程,但我刚刚遇到了这个问题,并使用了一个实用的解决方案,而不是技术性的,可能是也可能不是显而易见的
正如前面所讨论的,MERGE并不是为实现这一点而设计的。INSERT_INTO…EXEC解决方案是一个很好的解决方案,但我正在处理的特定存储过程已经足够复杂了
所以为了让下一个需要处理这段代码的人能够简单地处理这段代码,我只使用了两个MERGE语句……一个执行插入操作,另一个执行更新操作。毕竟,没有法律规定你只能使用一个。我在日志记录表中添加了一个“action”列,在其中插入MERGE语句“insert”或“Update”,具体取决于它所做的操作
性能不需要太多的关注,特别是因为这不是一个用户进程
提示:先更新,然后插入。否则,当您执行第一次加载时,您将为导入的每一行获得一条插入记录和一条更新记录 输出
子句允许选择列表。虽然这不允许多个结果集,但它允许一个结果集处理所有操作
<output_clause>::=
{
[ OUTPUT <dml_select_list> INTO { @table_variable | output_table }
[ (column_list) ] ]
[ OUTPUT <dml_select_list> ]
}
您将得到如下结果集:
| MergeAction | rowId | col1 | col2 |
| INSERT | 3 | 1 | new |
| UPDATE | 1 | 2 | foo |
| DELETE | 2 | 3 | bar |
是的,我在MSDN上看到了这一点,但希望能有一个黑客来克服这个限制。无论如何,谢谢你指出这一点!我想补充一点,您需要在输出之前先输入输出(正如语法括号所示)。我提到这一点只是为了防止有人两者的顺序都不正确,并且没有意识到为什么这对他们不起作用。可以将MERGE
包装到存储过程中,并使用INSERT。。。EXEC
将第二个输出的结果插入到第二个表中,如我在回答中所示。它有其缺点,但在某些情况下可能有用。避免EXEC和INSERT之间的中间临时表的“正常”方法是将代码转换为一个内嵌的表值函数。不幸的是,这些都不允许有副作用(比如插入!),但是,如果您愿意编写一个流式C#SQL CLR,您可以绕过它!这是一条很长的路,但一定要走!如何区分在@MergeResults中插入/更新?
| MergeAction | rowId | col1 | col2 |
| INSERT | 3 | 1 | new |
| UPDATE | 1 | 2 | foo |
| DELETE | 2 | 3 | bar |