Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/68.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql 获取错误“;INSERT EXEC语句不能嵌套";_Sql_Sql Server_Stored Procedures - Fatal编程技术网

Sql 获取错误“;INSERT EXEC语句不能嵌套";

Sql 获取错误“;INSERT EXEC语句不能嵌套";,sql,sql-server,stored-procedures,Sql,Sql Server,Stored Procedures,我遇到了insert…exec的问题,无法在线找到有效的解决方案。我有一个从API检索数据的存储过程。它通过构建命令行、通过xp_cmdshell运行命令行并将输出捕获到表(使用insert…exec)来实现这一点 存储过程工作正常,并将所需数据格式化为一个漂亮的表 我现在正试图将其实现到我的数据库中,但这需要从许多其他存储过程中调用。他们需要能够看到初始存储过程的结果,但我遇到了一个“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,我看不到任何其他方法来实现它,但这意味着我无法捕获存储过程的输出并使用它

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,)+''''^