错误:"INSERT EXEC语句不能嵌套." 和"不能在INSERT-EXEC语句中使用ROLLBACK语句." 怎么解决这个?

HAJ*_*JAJ 88 sql-server code-reuse sql-server-2008

我有三个存储过程Sp1,Sp2Sp3.

第一个(Sp1)将执行第二个(Sp2)并保存返回的数据@tempTB1,第二个将执行第三个(Sp3)并将数据保存到@tempTB2.

如果我执行Sp2它将工作,它将返回我的所有数据Sp3,但问题在于Sp1,当我执行它时,它将显示此错误:

INSERT EXEC语句不能嵌套

我试图改变它的位置,execute Sp2它显示另一个错误:

无法在INSERT-EXEC语句中使用ROLLBACK语句.

edd*_*ves 90

当尝试从存储过程链中"冒泡"数据时,这是一个常见问题.SQL Server中的限制是一次只能激活一个INSERT-EXEC.我建议查看如何在存储过程之间共享数据,这是一个关于解决此类问题的模式的非常全面的文章.

例如,解决方法可能是将Sp3转换为表值函数.

  • 你知道不允许它的技术原因是什么吗?我找不到这方面的任何信息。 (7认同)
  • 不幸的是,这通常不是一个选择。许多类型的重要信息只能从系统“存储过程”中“可靠”获得(因为在某些情况下,相应的管理视图包含不可靠/过时的数据;一个例子是“sp_help_jobactivity”返回的信息)。 (2认同)

小智 17

这是在SQL Server中执行此操作的唯一"简单"方法,没有一些巨大的复杂创建函数或执行的sql字符串调用,这两者都是可怕的解决方案:

  1. 创建一个临时表
  2. 将存储过程数据打开到其中

例:

INSERT INTO #YOUR_TEMP_TABLE
SELECT * FROM OPENROWSET ('SQLOLEDB','Server=(local);TRUSTED_CONNECTION=YES;','set fmtonly off EXEC [ServerName].dbo.[StoredProcedureName] 1,2,3')
Run Code Online (Sandbox Code Playgroud)

注意:您必须使用'set fmtonly off',并且您不能在openrowset调用内部为此添加动态sql,对于包含存储过程参数的字符串或表名称.这就是为什么你必须使用临时表而不是表变量,这本来是更好的,因为它在大多数情况下执行临时表.

  • 临时表和表变量以不同的方式存储它们的数据。表变量应该用于小结果集,因为查询优化器不维护表变量的统计信息。因此,对于大型数据集,使用临时表几乎总是更好。这是一篇关于它的不错的博客文章 http://www.mssqltips.com/sqlservertip/2825/sql-server-temp-table-vs-table-variable-performance-testing/ (2认同)
  • 为什么要在表变量上维护统计信息?重点是在 RAM 中创建一个临时表,该表将在查询完成后被销毁。根据定义,在此类表上创建的任何统计信息都不会被使用。一般来说,表变量中的数据尽可能保留在 RAM 中,这一事实使得在数据小于 SQL Server 可用 RAM 量的任何情况下,表变量的速度都比临时表快(目前 SQL Server 的内存池有 100GB 以上)服务器,几乎总是) (2认同)

小智 10

好的,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

*/
Run Code Online (Sandbox Code Playgroud)


小智 9

我解决这个问题的方法一直是使用单个散列临时表在任何被调用的procs范围内的原则.所以,我在proc参数中有一个选项开关(默认设置为off).如果打开它,则被调用的proc会将结果插入到调用proc中创建的临时表中.我认为在过去我已经更进一步,并在被调用的proc中放入一些代码来检查作用域中是否存在单个哈希表,如果是,则插入代码,否则返回结果集.似乎运行良好 - 在proc之间传递大数据集的最佳方式.

  • 我喜欢这个答案,我敢打赌,如果您提供和举例,您会获得更多的选票。 (3认同)

ain*_*art 6

这个技巧对我有用。

你在远程服务器上没有这个问题,因为在远程服务器上,最后一个插入命令等待上一个命令的结果执行。在同一台服务器上不是这样。

利用这种情况作为解决方法。

如果您有创建链接服务器的正确权限,请执行此操作。创建与链接服务器相同的服务器。

  • 在 SSMS 中,登录到您的服务器
  • 转到“服务器对象
  • 右键单击“链接服务器”,然后单击“新建链接服务器”
  • 在对话框中,给出链接服务器的任何名称:例如:THISSERVER
  • 服务器类型是“其他数据源”
  • 提供程序:Microsoft OLE DB Provider for SQL server
  • 数据源:你的IP,也可以只是一个点(.),因为它是localhost
  • 转到“安全”选项卡并选择第三个“使用登录的当前安全上下文进行”
  • 如果需要,您可以编辑服务器选项(第三个选项卡)
  • 按确定,您的链接服务器已创建

现在您在 SP1 中的 Sql 命令是

insert into @myTempTable
exec THISSERVER.MY_DATABASE_NAME.MY_SCHEMA.SP2
Run Code Online (Sandbox Code Playgroud)

相信我,即使您在 SP2 中有动态插入,它也能工作


Rom*_*n K 5

我发现一种解决方法是将其中一个 prods 转换为表值函数。我意识到这并不总是可能的,并引入了它自己的局限性。但是,我总是能够找到至少一个程序是一个很好的候选者。我喜欢这个解决方案,因为它没有对解决方案引入任何“黑客”。


Geo*_*ald 5

我在尝试将 Stored Proc 的结果导入临时表时遇到了这个问题,并且 Stored Proc 作为其自身操作的一部分插入到临时表中。问题是 SQL Server 不允许同一进程同时写入两个不同的临时表。

接受的 OPENROWSET 答案工作正常,但我需要避免在我的过程中使用任何动态 SQL 或外部 OLE 提供程序,所以我走了一条不同的路线。

我发现的一种简单解决方法是将存储过程中的临时表更改为表变量。它的工作方式与临时表完全相同,但不再与我的其他临时表插入冲突。

只是为了阻止评论,我知道你们中的一些人即将写,警告我不要将表变量作为性能杀手......我能告诉你的是,在 2020 年,不要害怕表变量会带来好处。如果这是 2008 年,并且我的数据库托管在具有 16GB RAM 并运行 5400RPM 硬盘的服务器上,我可能会同意您的看法。但现在是 2020 年,我有一个 SSD 阵列作为我的主要存储设备和数百个内存。我可以将我整个公司的数据库加载到一个表变量中,并且仍然有足够的 RAM 可用。

表变量重新出现在菜单上!