错误:“INSERT EXEC语句不能嵌套”和“不能在INSERT-EXEC语句中使用ROLLBACK语句”如何解决这个问题?
我有三个存储过程Sp1
, Sp2
和Sp3
。
第一个( Sp1
)将执行第二个( Sp2
)并将返回的数据保存到@tempTB1
,第二个将执行第三个( Sp3
)并将数据保存到@tempTB2
。
如果我执行Sp2
,它将工作,并将从Sp3
返回我所有的数据,但问题出在Sp1
,当我执行它时,会显示以下错误:
INSERT EXEC语句不能嵌套
我试图改变execute Sp2
的地方,它显示我另一个错误:
在INSERT-EXEC语句中不能使用ROLLBACK语句。
当试图从一系列存储过程中“冒泡”数据时,这是一个常见的问题。 SQL Server中的限制是一次只能有一个INSERT-EXEC活动。 我build议查看如何在存储过程之间共享数据 ,这是一个非常彻底的模式文章,以解决这类问题。
例如,一个解决方法可能是将Sp3转换为Table-valued函数。
这是在SQL Server中执行此操作的唯一“简单”方法,没有一些巨大的复杂创build的函数或执行的sqlstring调用,这两者都是非常糟糕的解决scheme:
- 创build一个临时表
- 将您的存储过程数据放入其中
例:
INSERT INTO #YOUR_TEMP_TABLE SELECT * FROM OPENROWSET ('SQLOLEDB','Server=(local);TRUSTED_CONNECTION=YES;','set fmtonly off EXEC [ServerName].dbo.[StoredProcedureName] 1,2,3')
注意 :您必须使用'set fmtonly off',并且您不能在openrowset调用中为此添加dynamicsql,无论是包含存储过程参数的string还是表名。 这就是为什么你必须使用临时表,而不是表variables,这将是更好的,因为它在大多数情况下执行临时表。
我的这个问题的工作一直是使用单个哈希临时表的范围在任何被称为特效的原则。 所以,我有一个选项切换在proc参数(默认设置为closures)。 如果打开,被调用的proc会将结果插入到调用过程中创build的临时表中。 我认为在过去我已经更进了一步,把一些代码放在被调用的proc中,检查单个哈希表是否存在于范围内,如果是,则插入代码,否则返回结果集。 似乎工作得很好 – 在proc之间传递大数据集的最好方法。
好吧,由jimhark鼓励这里是一个旧的单哈希表方法的例子: –
CREATE PROCEDURE SP3 as BEGIN SELECT 1, 'Data1' UNION ALL SELECT 2, 'Data2' END go CREATE PROCEDURE SP2 as BEGIN if exists (select * from tempdb.dbo.sysobjects o where o.xtype in ('U') and o.id = object_id(N'tempdb..#tmp1')) INSERT INTO #tmp1 EXEC SP3 else EXEC SP3 END go CREATE PROCEDURE SP1 as BEGIN EXEC SP2 END GO /* --I want some data back from SP3 -- Just run the SP1 EXEC SP1 */ /* --I want some data back from SP3 into a table to do something useful --Try run this - get an error - can't nest Execs if exists (select * from tempdb.dbo.sysobjects o where o.xtype in ('U') and o.id = object_id(N'tempdb..#tmp1')) DROP TABLE #tmp1 CREATE TABLE #tmp1 (ID INT, Data VARCHAR(20)) INSERT INTO #tmp1 EXEC SP1 */ /* --I want some data back from SP3 into a table to do something useful --However, if we run this single hash temp table it is in scope anyway so --no need for the exec insert if exists (select * from tempdb.dbo.sysobjects o where o.xtype in ('U') and o.id = object_id(N'tempdb..#tmp1')) DROP TABLE #tmp1 CREATE TABLE #tmp1 (ID INT, Data VARCHAR(20)) EXEC SP1 SELECT * FROM #tmp1 */
我发现一个工作是将其中一个prod转换为一个表值函数。 我意识到这并不总是可能的,并介绍了自己的局限性。 不过,我总能find至less一个程序是一个很好的候选人。 我喜欢这个解决scheme,因为它不会引入任何“黑客”的解决scheme。
我有两个或更多sprocs重复的代码相同的问题和担心。 我结束了为“模式”添加一个额外的属性。 这允许公共代码存在于一个sproc内部,并且模式指向sproc的stream和结果集。
这个把戏适合我。
远程服务器上没有这个问题,因为在远程服务器上,最后一个insert命令等待上一个命令的执行结果。 这不是在同一台服务器上的情况。
利润的情况下的解决方法。
如果您有权创build链接服务器,请执行此操作。 创build与链接服务器相同的服务器。
- 在SSMS中,login到你的服务器
- 去“服务器对象
- 右键单击“Linked Servers”,然后点击“New Linked Server”
- 在对话框中,给出你的链接服务器的任何名称:例如:THISSERVER
- 服务器types是“其他数据源”
- 提供程序:用于SQL Server的Microsoft OLE DB提供程序
- 数据源:你的IP,它也可以是一个点(。),因为它是本地主机
- 转到选项卡“安全”,并select第三个“使用login的当前安全上下文”
- 如果需要,可以编辑服务器选项(第三个选项卡)
- 按确定,您的链接的服务器被创build
现在你在SP1中的Sql命令是
insert into @myTempTable exec THISSERVER.MY_DATABASE_NAME.MY_SCHEMA.SP2
相信我,它甚至可以在SP2中dynamic插入
那么把输出存储到静态表中呢? 喜欢
-- SubProcedure: subProcedureName --------------------------------- -- Save the value DELETE lastValue_subProcedureName INSERT INTO lastValue_subProcedureName (Value) SELECT @Value -- Return the value SELECT @Value -- Procedure -------------------------------------------- -- get last value of subProcedureName SELECT Value FROM lastValue_subProcedureName
它不是很理想,但它很简单,你不需要重写所有的东西。
更新 :以前的解决scheme不适用于并行查询(asynchronous和多用户访问),因此现在Iam使用临时表
-- A local temporary table created in a stored procedure is dropped automatically when the stored procedure is finished. -- The table can be referenced by any nested stored procedures executed by the stored procedure that created the table. -- The table cannot be referenced by the process that called the stored procedure that created the table. IF OBJECT_ID('tempdb..#lastValue_spGetData') IS NULL CREATE TABLE #lastValue_spGetData (Value INT) -- trigger stored procedure with special silent parameter EXEC dbo.spGetData 1 --silent mode parameter
嵌套的spGetData
存储过程内容
-- Save the output if temporary table exists. IF OBJECT_ID('tempdb..#lastValue_spGetData') IS NOT NULL BEGIN DELETE #lastValue_spGetData INSERT INTO #lastValue_spGetData(Value) SELECT Col1 FROM dbo.Table1 END -- stored procedure return IF @silentMode = 0 SELECT Col1 FROM dbo.Table1
声明一个输出游标variables到内部的sp:
@c CURSOR VARYING OUTPUT
然后声明一个游标c到你想要返回的select。 然后打开光标。 然后设置参考:
DECLARE c CURSOR LOCAL FAST_FORWARD READ_ONLY FOR SELECT ... OPEN c SET @c = c
不要closures或重新分配。
现在从外部调用内部的sp,提供一个游标参数,如:
exec sp_abc a,b,c,, @cOUT OUTPUT
内部sp一旦执行,您的@cOUT
就可以获取。 循环,然后closures并释放。
在SQL Server 2008 R2上,导致回滚错误的表列不匹配。 当我修复由insert-exec语句填充的sqlcmd表variables以匹配存储过程返回的值时,它就消失了。 这是缺lessorg_code。 在一个Windows cmd文件中,它加载存储过程的结果并select它。
set SQLTXT= declare @resets as table (org_id nvarchar(9), org_code char(4), ^ tin(char9), old_strt_dt char(10), strt_dt char(10)); ^ insert @resets exec rsp_reset; ^ select * from @resets; sqlcmd -U user -P pass -d database -S server -Q "%SQLTXT%" -o "OrgReport.txt"