错误:“INSERT EXEC 语句不能嵌套。”和“不能在 INSERT-EXEC 语句中使用 ROLLBACK 语句。”怎么解决这个问题呢?
我有三个存储过程 Sp1
、Sp2
和 Sp3
。
第一个 (Sp1
) 将执行第二个 (Sp2
) 并将返回的数据保存到 @tempTB1
中,第二个将执行第三个一个 (Sp3
) 并将数据保存到 @tempTB2
中。
如果我执行 Sp2
它将起作用,并且它将返回我来自 Sp3
的所有数据,但问题出在 Sp1
中,当我执行它会显示这个错误:
INSERT EXEC 语句不能嵌套
我尝试更改 execute Sp2
的位置,它显示另一个错误:
不能使用ROLLBACK语句 在 INSERT-EXEC 语句中。
I have three stored procedures Sp1
, Sp2
and Sp3
.
The first one (Sp1
) will execute the second one (Sp2
) and save returned data into @tempTB1
and the second one will execute the third one (Sp3
) and save data into @tempTB2
.
If I execute the Sp2
it will work and it will return me all my data from the Sp3
, but the problem is in the Sp1
, when I execute it it will display this error:
INSERT EXEC statement cannot be nested
I tried to change the place of execute Sp2
and it display me another error:
Cannot use the ROLLBACK statement
within an INSERT-EXEC statement.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(15)
当尝试从存储过程链中“冒泡”数据时,这是一个常见问题。 SQL Server 中的一项限制是一次只能有一个 INSERT-EXEC 处于活动状态。我建议查看如何在存储过程之间共享数据,这是一篇关于工作模式的非常详尽的文章围绕此类问题。
例如,解决方法是将 Sp3 转换为表值函数。
This is a common issue when attempting to 'bubble' up data from a chain of stored procedures. A restriction in SQL Server is you can only have one INSERT-EXEC active at a time. I recommend looking at How to Share Data Between Stored Procedures which is a very thorough article on patterns to work around this type of problem.
For example a work around could be to turn Sp3 into a Table-valued function.
这是在 SQL Server 中执行此操作的唯一“简单”方法,无需使用一些巨大的复杂创建函数或执行 sql 字符串调用,这两种方法都是糟糕的解决方案:
示例:
注意:您必须使用“set fmtonly off”,并且不能在 openrowset 调用中向其中添加动态 sql,无论是对于包含存储过程参数的字符串还是表名。这就是为什么您必须使用临时表而不是表变量,这会更好,因为它在大多数情况下执行临时表。
This is the only "simple" way to do this in SQL Server without some giant convoluted created function or executed sql string call, both of which are terrible solutions:
EXAMPLE:
Note: You MUST use 'set fmtonly off', AND you CANNOT add dynamic sql to this either inside the openrowset call, either for the string containing your stored procedure parameters or for the table name. Thats why you have to use a temp table rather than table variables, which would have been better, as it out performs temp table in most cases.
好的,受到 jimhark 的鼓励,这里是旧的单哈希表方法的一个示例:-
OK, encouraged by jimhark here is an example of the old single hash table approach: -
我解决这个问题的方法一直是使用单个哈希临时表在任何调用的过程范围内的原则。因此,我在 proc 参数中有一个选项开关(默认设置为关闭)。如果打开此功能,被调用的过程会将结果插入到调用过程中创建的临时表中。我认为过去我更进一步,在被调用的过程中放入一些代码来检查范围内是否存在单个哈希表,如果存在则插入代码,否则返回结果集。似乎工作得很好 - 在进程之间传递大数据集的最佳方式。
My work around for this problem has always been to use the principle that single hash temp tables are in scope to any called procs. So, I have an option switch in the proc parameters (default set to off). If this is switched on, the called proc will insert the results into the temp table created in the calling proc. I think in the past I have taken it a step further and put some code in the called proc to check if the single hash table exists in scope, if it does then insert the code, otherwise return the result set. Seems to work well - best way of passing large data sets between procs.
这个技巧对我有用。
在远程服务器上不会出现此问题,因为在远程服务器上,最后一个插入命令会等待上一个命令的结果执行。在同一台服务器上则不然。
利用这种情况寻求解决方法。
如果您拥有创建链接服务器的正确权限,请执行此操作。
创建与链接服务器相同的服务器。
,SP1 中的 Sql 命令是
相信我,即使您在 SP2 中动态插入,它也可以工作
This trick works for me.
You don't have this problem on remote server, because on remote server, the last insert command waits for the result of previous command to execute. It's not the case on same server.
Profit that situation for a workaround.
If you have the right permission to create a Linked Server, do it.
Create the same server as linked server.
now your Sql command in the SP1 is
Believe me, it works even you have dynamic insert in SP2
我发现一种解决方法是将其中一个产品转换为表值函数。我意识到这并不总是可行,并介绍了其自身的局限性。然而,我总能找到至少一个适合此目的的程序。我喜欢这个解决方案,因为它不会给解决方案带来任何“黑客”。
I found a work around is to convert one of the prods into a table valued function. I realize that is not always possible, and introduces its own limitations. However, I have been able to always find at least one of the procedures a good candidate for this. I like this solution, because it doesn't introduce any "hacks" to the solution.
当我尝试将存储过程的结果导入到临时表中,并且该存储过程作为其自身操作的一部分插入到临时表中时,我遇到了这个问题。问题是 SQL Server 不允许同一进程同时写入两个不同的临时表。
接受的 OPENROWSET 答案工作正常,但我需要避免在我的流程中使用任何动态 SQL 或外部 OLE 提供程序,因此我走了不同的路线。
我发现的一个简单的解决方法是将存储过程中的临时表更改为表变量。它的工作方式与临时表完全相同,但不再与我的其他临时表插入冲突。
只是为了结束我知道你们中的一些人即将写的评论,警告我不要将表变量视为性能杀手......我只能对你说的是,在 2020 年它会带来红利不害怕表变量。如果这是 2008 年,并且我的数据库托管在具有 16GB RAM 并运行 5400RPM HDD 的服务器上,我可能会同意您的观点。但现在是 2020 年了,我有一个 SSD 阵列作为我的主存储,还有数百GB RAM。我可以将整个公司的数据库加载到一个表变量中,并且仍然有足够的 RAM 可用。
表变量又回到了菜单上!
I encountered this issue when trying to import the results of a Stored Proc into a temp table, and that Stored Proc inserted into a temp table as part of its own operation. The issue being that SQL Server does not allow the same process to write to two different temp tables at the same time.
The accepted OPENROWSET answer works fine, but I needed to avoid using any Dynamic SQL or an external OLE provider in my process, so I went a different route.
One easy workaround I found was to change the temporary table in my stored procedure to a table variable. It works exactly the same as it did with a temp table, but no longer conflicts with my other temp table insert.
Just to head off the comment I know that a few of you are about to write, warning me off Table Variables as performance killers... All I can say to you is that in 2020 it pays dividends not to be afraid of Table Variables. If this was 2008 and my Database was hosted on a server with 16GB RAM and running off 5400RPM HDDs, I might agree with you. But it's 2020 and I have an SSD array as my primary storage and hundreds of gigs of RAM. I could load my entire company's database to a table variable and still have plenty of RAM to spare.
Table Variables are back on the menu!
我建议阅读这篇整篇文章。以下是该文章中与您的问题最相关的部分:
Msg 3915,Level 16,State 0,Procedure SalesByStore,Line 9 无法在 INSERT-EXEC 语句中使用 ROLLBACK 语句。
I recommend to read this entire article. Below is the most relevant section of that article that addresses your question:
Msg 3915, Level 16, State 0, Procedure SalesByStore, Line 9 Cannot use the ROLLBACK statement within an INSERT-EXEC statement.
我对两个或多个存储过程中的重复代码也有同样的问题和担忧。我最终为“模式”添加了一个附加属性。这允许公共代码存在于一个存储过程中,并且模式引导存储过程的流程和结果集。
I had the same issue and concern over duplicate code in two or more sprocs. I ended up adding an additional attribute for "mode". This allowed common code to exist inside one sproc and the mode directed flow and result set of the sproc.
将输出存储到静态表怎么样?虽然
它并不理想,但它非常简单,您不需要重写所有内容。
更新:
以前的解决方案不适用于并行查询(异步和多用户访问),因此现在我使用
嵌套
spGetData
存储过程内容的临时表what about just store the output to the static table ? Like
its not ideal, but its so simple and you don't need to rewrite everything.
UPDATE:
the previous solution does not work well with parallel queries (async and multiuser accessing) therefore now Iam using temp tables
nested
spGetData
stored procedure content向内部 sp 声明一个输出游标变量:
然后向要返回的选择声明一个游标 c 。
然后打开光标。
然后设置参考:
请勿关闭或重新分配。
现在,从外部 sp 调用内部 sp,并提供一个游标参数,如下所示:
一旦内部 sp 执行,您的
@cOUT
就可以获取了。循环然后关闭并释放。Declare an output cursor variable to the inner sp :
Then declare a cursor c to the select you want to return.
Then open the cursor.
Then set the reference:
DO NOT close or reallocate.
Now call the inner sp from the outer one supplying a cursor parameter like:
Once the inner sp executes, your
@cOUT
is ready to fetch. Loop and then close and deallocate.如果您能够使用其他相关技术(例如 C#),我建议使用带有 Transaction 参数的内置 SQL 命令。
我创建了一个简单的控制台应用程序来演示此功能,可以在此处找到:
https://github.com/hecked12/SQL-Transaction-Using-C- Sharp
简而言之,C# 允许您克服这一限制,您可以检查每个存储过程的输出并根据需要使用该输出,例如您可以将其提供给另一个存储过程。如果输出正常,则可以提交事务,否则,可以使用回滚来恢复更改。
If you are able to use other associated technologies such as C#, I suggest using the built in SQL command with Transaction parameter.
I've created a simple Console App that demonstrates this ability which can be found here:
https://github.com/hecked12/SQL-Transaction-Using-C-Sharp
In short, C# allows you to overcome this limitation where you can inspect the output of each stored procedure and use that output however you like, for example you can feed it to another stored procedure. If the output is ok, you can commit the transaction, otherwise, you can revert the changes using rollback.
就我而言,我将 SP1 调用到 SP2,其中“插入 #temptable”可用,并且进一步将 SP2 的输出尝试插入到 #temtable2,因为弹出“insert exec 语句无法嵌套”错误。
我通过将最终的 #temptable 插入放在 SP2 本身内部来解决这个问题。这样,如果我们调用 SP2,它会在执行结束时将数据插入到 #temptable2 中,因此在执行之外不需要额外的 INSERT INTO。
我回答这个问题是假设像我这样的人会从他的回答中得到帮助。
In my case, I was calling SP1 into SP2 where Insert into #temptable is available and further the output of SP2 I tried to insert into #temtable2 due to which "an insert exec statement cannot be nested" error poped up.
I fixed the issue by placing the final #temptable insertion inside the SP2 itself. So that If we call SP2, it will insert the data into #temptable2 at the end of the execution hene no additional INSERT INTO is not needed outside the execution.
I'm Answering this assuming someone like me will get assistance from his answer.
我发现有用的一个选项是返回 JSON 或 XML 的 OUT 参数。例如:
现在 Sp1 可以按如下方式获取数据:
如果输出的列是动态的,则使用输出可能会非常困难,但对于更简单的数据,它很方便。
An option I find useful is an OUT parameter that returns JSON or XML. For example:
Now Sp1 can get the data as follows:
If the columns of your output are dynamic, it may be very difficult to work with the output, but for simpler data it's handy.
在 SQL Server 2008 R2 上,表列不匹配导致回滚错误。当我修复由 insert-exec 语句填充的 sqlcmd 表变量以匹配存储过程返回的变量时,它消失了。它缺少 org_code。在 Windows cmd 文件中,它加载存储过程的结果并选择它。
On SQL Server 2008 R2, I had a mismatch in table columns that caused the Rollback error. It went away when I fixed my sqlcmd table variable populated by the insert-exec statement to match that returned by the stored proc. It was missing org_code. In a windows cmd file, it loads result of stored procedure and selects it.