Sql 获取错误“;INSERT EXEC语句不能嵌套";
我遇到了insert…exec的问题,无法在线找到有效的解决方案。我有一个从API检索数据的存储过程。它通过构建命令行、通过xp_cmdshell运行命令行并将输出捕获到表(使用insert…exec)来实现这一点 存储过程工作正常,并将所需数据格式化为一个漂亮的表 我现在正试图将其实现到我的数据库中,但这需要从许多其他存储过程中调用。他们需要能够看到初始存储过程的结果,但我遇到了一个“INSERT EXEC语句不能嵌套”错误,它不允许我捕获输出 我已经尝试了各种在线建议的解决方案,但到目前为止,没有一个有效。初始存储过程正在调用命令行,因此除了使用insert…..exec之外,我找不到任何其他方法来调用它并捕获输出,但我仍然需要格式化的输出 我已尝试将存储过程转换为函数,但无法运行xp\u cmdshell。我还研究了如何让初始存储过程将表作为输出参数返回(即使我使用类型创建它),但存储过程不允许这样做 我还研究了使用openset,但我需要能够将参数传递给存储过程,我认为openset不允许这样做。下一步我能试试什么 编辑:我把一个简单的例子放在一起,说明我正在尝试做什么。存储过程正在从命令行检索数据。我只是使用一个echo命令来伪造数据,但实际上,这个命令行正在调用一个API,并接收返回的JSON。然后将JSON格式化为SQL表并输出。由于这是一个API调用,因此如果没有insert…exec xp_cmdshell,我看不到任何其他方法来实现它,但这意味着我无法捕获存储过程的输出并使用它Sql 获取错误“;INSERT EXEC语句不能嵌套";,sql,sql-server,stored-procedures,Sql,Sql Server,Stored Procedures,我遇到了insert…exec的问题,无法在线找到有效的解决方案。我有一个从API检索数据的存储过程。它通过构建命令行、通过xp_cmdshell运行命令行并将输出捕获到表(使用insert…exec)来实现这一点 存储过程工作正常,并将所需数据格式化为一个漂亮的表 我现在正试图将其实现到我的数据库中,但这需要从许多其他存储过程中调用。他们需要能够看到初始存储过程的结果,但我遇到了一个“INSERT EXEC语句不能嵌套”错误,它不允许我捕获输出 我已经尝试了各种在线建议的解决方案,但到目前为止
create procedure usp_retrieveAPIdata
@inparameter int
as
begin
declare @APIcall varchar(200)
--this would normally be an API call, returning a JSON array
set @APICall='echo f1:"foo" & echo f2:"bar" & echo f1:"Hello" & echo f2:"World"'
declare @resulttable table
(outputfield varchar(100),ID int identity)
insert into @resulttable
exec xp_cmdshell @APICall
declare @formattedtable table
(field1 varchar(100),field2 varchar(100))
declare @rownum int =0
declare @field1 varchar(100)
declare @field2 varchar(100)
declare @currentfield varchar(100)
while exists (select * from @resulttable where ID>@rownum)
begin
set @rownum=@rownum+1
select @currentfield=outputfield from @resulttable where ID=@rownum
if @currentfield like 'f1%'
begin
set @field1=replace(@currentfield,'f1:','')
end
if @currentfield like 'f2%' and @rownum<>1
begin
set @field2=replace(@currentfield,'f2:','')
insert into @formattedtable (field1,field2) values (@field1,@field2)
end
end
select * from @formattedtable
end
go
declare @resulttable table (field1 varchar(100),field2 varchar(100))
insert into @resulttable
exec usp_retrieveAPIdata 1
创建过程usp_RetrieveAPI数据
@非参数整数
作为
开始
声明@APIcall varchar(200)
--这通常是一个API调用,返回一个JSON数组
设置@APICall='echo f1:“foo”&echo f2:“bar”&echo f1:“Hello”&echo f2:“World”'
声明@resultable表
(outputfield varchar(100),ID int identity)
插入@resultable
exec xp_cmdshell@APICall
声明@formattedtable表
(字段1 varchar(100),字段2 varchar(100))
声明@rownum int=0
声明@field1 varchar(100)
声明@field2 varchar(100)
声明@currentfield varchar(100)
存在时(从@resultable中选择*,其中ID>@rownum)
开始
设置@rownum=@rownum+1
从@resultable中选择@currentfield=outputfield,其中ID=@rownum
如果@currentfield类似于“f1%”
开始
设置@field1=replace(@currentfield,'f1:','')
结束
如果@currentfield像'f2%'和@rownum1
开始
设置@field2=replace(@currentfield,'f2:','')
插入@formattedtable(field1,field2)值(@field1,@field2)
结束
结束
从@formattedtable中选择*
结束
去
声明@resulttable(field1varchar(100),field2varchar(100))
插入@resultable
执行usp_retrieveAPIdata 1
这就是INSERT EXEC的问题
多年来,我已经多次遇到过这个问题。这里有几个选择——没有一个是完美的,每个都有它的优点/缺点,但应该有助于你越过终点线
示例程序:
USE tempdb
GO
-- Sample Procs
CREATE PROC dbo.proc1 @a INT, @b INT
AS
SELECT x.a, x.b
FROM (VALUES(@a,@b)) AS x(a,b)
CROSS JOIN (VALUES(1),(2),(3)) AS xx(x);
GO
CREATE PROC dbo.proc2 @a INT, @b INT
AS
DECLARE @x TABLE (a INT, b INT);
INSERT @x(a,b)
EXEC dbo.proc1 5,10;
SELECT x.a, x.b FROM @x AS x;
由于嵌套INSERT EXEC,此操作将失败:
DECLARE @a INT = 2, @b INT = 4;
DECLARE @t2 TABLE (a INT, b INT);
INSERT @t2(a,b)
EXEC dbo.proc2 5,10;
选项#1:提取存储过程逻辑并直接运行它
在这里,我只是从dbo.proc2获取逻辑并特别运行它
DECLARE @t2 TABLE (a INT, b INT);
DECLARE @a INT = 2, @b INT = 4;
INSERT @t2 (a,b)
-- Logic Extracted right out of dbo.proc1:
SELECT x.a, x.b
FROM (VALUES(@a,@b)) AS x(a,b)
CROSS JOIN (VALUES(1),(2),(3)) AS xx(x);
SELECT t2.* FROM @t2 AS t2;
选项#2-提取过程逻辑并将其作为动态SQL运行
DECLARE @t2 TABLE (a INT, b INT);
DECLARE @a INT = 2,
@b INT = 4;
DECLARE @SQL NVARCHAR(4000) = N'
SELECT x.a, x.b
FROM (VALUES(@a,@b)) AS x(a,b)
CROSS JOIN (VALUES(1),(2),(3)) AS xx(x);',
@ParmDefinition NVARCHAR(500) = N'@a INT, @b INT';
INSERT @t2
EXEC sys.sp_executesql @SQL, @ParmDefinition, @a=@a, @b=@b;
SELECT t2.* FROM @t2 AS t2; -- validation
选项#3-选项#2,程序代码直接来自元数据
DECLARE @t2 TABLE (a INT, b INT);
DECLARE @a INT = 2,
@b INT = 4;
DECLARE
@SQL NVARCHAR(4000) =
( SELECT SUBSTRING(f.P, CHARINDEX('SELECT',f.P),LEN(f.P))
FROM (VALUES(OBJECT_DEFINITION(OBJECT_ID('proc1')))) AS f(P)),
@ParmDefinition NVARCHAR(500) = N'@a INT, @b INT';
EXEC sys.sp_executesql @SQL, @ParmDefinition, @a=@a, @b=@b;
这里的缺点是解析出我需要的东西。我用一个SELECT子句开始的逻辑简化了我的例子,现实世界并非如此。与手动添加逻辑相比,这样做的好处是代码将是最新的。对proc的更改会自动更改您的逻辑(但也会中断代码)
选项4:全局临时表
我还没有真正尝试过这个,但它应该会起作用。您可以像这样重新编写proc(在我的示例中为proc2):
ALTER PROC dbo.proc2 @a INT, @b INT, @output BIT = 1
AS
IF OBJECT_ID('tempdb..##x','U') IS NOT NULL DROP TABLE ##x;
CREATE TABLE ##x(a INT, b INT);
INSERT ##x(a,b)
EXEC dbo.proc1 5,10;
IF @output = 1
SELECT x.a, x.b FROM ##x AS x;
GO
我正在用结果集填充一个全局临时表,然后添加一个选项来显示输出或不显示输出。当@output=0时,结果集将位于##x中,可以这样引用:
DECLARE @t2 TABLE (a INT, b INT);
EXEC dbo.proc2 5,10,0;
INSERT @t2(a,b)
SELECT * FROM ##x;
SELECT * FROM @t2;
我想我已经破解了。奇怪的是,你整个下午都在看SQL,然后当你清理一个鱼缸时,答案就出现了 我需要把我的狂欢一分为二。第一部分调用API,并以JSON数组的形式接收答案。JSON基本上是文本,因此我不应该将其转换为表,而应该作为NVARCHAR(MAX)返回调用存储过程 然后,调用存储过程可以调用第二个存储过程将此JSON格式化为表格式 由于第一个存储过程不返回表,SQL将不关心嵌套的Insert…exec,而第二个存储过程不使用cmdshell,它不需要Insert…exec,因此可以将结果接收到表中 下面是上面的示例,但存储过程被拆分为2
begin tran
go
create procedure usp_retrieveAPIdata
@inparameter int,
@resultstring varchar(max) output
as
begin
declare @APIcall varchar(200)
--this would normally be an API call, returning a JSON array
set @APICall='echo f1:"foo" & echo f2:"bar" & echo f1:"Hello" & echo f2:"World"'
declare @resulttable table
(outputfield varchar(100),ID int identity)
insert into @resulttable
exec xp_cmdshell @APICall
set @resultstring=''
select @resultstring=@resultstring + isnull(outputfield,'') + '¶' from @resulttable order by ID --using '¶' as a random row delimiter
end
go
create procedure usp_formatdata (@instring varchar(max))
as
begin
print @instring
declare @resulttable table
(outputfield varchar(100),ID int)
insert into @resulttable (outputfield,ID)
select value,idx+1 from dbo.fn_split(@instring,'¶');
declare @formattedtable table
(field1 varchar(100),field2 varchar(100))
declare @rownum int =0
declare @field1 varchar(100)
declare @field2 varchar(100)
declare @currentfield varchar(100)
while exists (select * from @resulttable where ID>@rownum)
begin
set @rownum=@rownum+1
select @currentfield=outputfield from @resulttable where ID=@rownum
if @currentfield like 'f1%'
begin
set @field1=replace(@currentfield,'f1:','')
end
if @currentfield like 'f2%' and @rownum<>1
begin
set @field2=replace(@currentfield,'f2:','')
insert into @formattedtable (field1,field2) values (@field1,@field2)
end
end
select field1,field2 from @formattedtable
end
go
declare @resulttable table (field1 varchar(100),field2 varchar(100))
declare @outstring varchar(max)
exec usp_retrieveAPIdata 110,@resultstring=@outstring output
insert into @resulttable
exec usp_formatdata @outstring
select * from @resulttable
rollback
开始传输
去
创建程序usp_retrieveAPIdata
@参数int,
@结果字符串varchar(最大)输出
作为
开始
声明@APIcall varchar(200)
--这通常是一个API调用,返回一个JSON数组
设置@APICall='echo f1:“foo”&echo f2:“bar”&echo f1:“Hello”&echo f2:“World”'
声明@resultable表
(outputfield varchar(100),ID int identity)
插入@resultable
exec xp_cmdshell@APICall
设置@resultstring=“”
从@resulttable中选择@resultstring=@resultstring+isnull(outputfield,)+''''^