INSERT EXEC Statement cannot be nested [duplicate] - sql

This question already has answers here:
Errors: "INSERT EXEC statement cannot be nested." and "Cannot use the ROLLBACK statement within an INSERT-EXEC statement." How to solve this?
(13 answers)
Closed 8 years ago.
I have three Procedures MainProcedure,Procedure1,Procedure2
1) In Procedure1 I just have a select statement ,
2) In Procedure2 am calling the Procedure1 and inserting the Output to a #table
3) In the main Procedure I am calling the Procedure2 and iam trying to insert the Output to a #table which throws an error
Msg 8164, Level 16, State 1, Procedure Procedure2, Line 10
An INSERT EXEC statement cannot be nested.
I can resolve this using Openrowset where I need to use specify Server Name ,is there any other way to solve this by not specifying the servername details
please find the sample procedure for reference
Create Proc Procedure1
As
Begin
Select 'Arun' Name, 'Pollachi' Place
Union
Select 'Vedaraj' Name, 'Devakottai' Place
End
Go
Create Proc Procedure2
As
Begin
Create Table #Table1
(
Name Varchar(50), Place Varchar(50)
)
INSERT #Table1
Exec Procedure1
SELECT 'Procedure2' [Source], * FROM #Table1
DROP TABLE #Table1
End
Go
Create Proc MainProcedure
As
Begin
Create Table #Table1
(
[Source] Varchar(50), Name Varchar(50), Place Varchar(50)
)
INSERT #Table1
Exec Procedure2
select * from #Table1
DROP TABLE #Table1
End
Go
can any one change my main procedure and make it to get executed
Thanks!!

Like you said, openrowset will work, but other than that the only ways I can think of would be:
Change both proc 1 and proc 2 to table based functions
Change proc 2 to a CLR and put all logic in there
Pass the tables around as table valued parameters
There's more info about the reasoning for this here:
https://connect.microsoft.com/SQLServer/feedback/details/294571/improve-insert-exec
http://dataeducation.com/revisiting-isnull-coalesce-and-the-perils-of-micro-optimization/

Related

Inserting into a user-defined table type via a dynamic query in SQL Server?

I have a user-defined table type tyAnalysisNumbers. I need to populate my user defined data type within a stored procedure with a SELECT statement and I am struggling to get that working within my stored procedure.
The following ways I have tried do not work
DECLARE #MyTable tyAnalysisNumbers;
INSERT INTO #MyTable
EXEC ('SELECT * FROM ' + #someTable);
I get this error:
An INSERT EXEC statement cannot be nested
I am unsure how to insert into my custom table via a select statement.
Can anyone help me accomplish this?
An INSERT EXEC statement cannot be nested
Above error is self explanatory. Please look at below scenario:
For example, we have one procedure which inserts data in table type and return result.
CREATE PROCEDURE uspInsertData1
AS
BEGIN
DECLARE #MyTable tyAnalysisNumbers;
INSERT INTO #MyTable
EXEC ('SELECT * FROM someTable');
select * from #MyTable
END
Now, let's say we have another procedure which will call above procedure and again insert data in another table.
CREATE PROCEDURE uspInsertData2
AS
BEGIN
DECLARE #MyTable tyAnalysisNumbers;
INSERT INTO sometable
EXEC uspInsertData1
END
Now, if you execute 1st procedure it will work fine but if you execute second procedure you will get this error.
An INSERT EXEC statement cannot be nested.
Because now you have nested EXEC statements.
I suggest to finish your work in single stored procedure if possible.
Try it like this:
DECLARE #MyTable tyAnalysisNumbers;
SELECT * INTO #Temp FROM #MyTable;
DECLARE #tblName AS SYSNAME = (SELECT name FROM sys.tables WHERE name = #someTable);
EXEC ('INSERT INTO #Temp SELECT * FROM ' + #tblName);
This also addresses the SQL Injection problem.

Getting query result into table variable

So I have declared a query within a varchar variable like:
DECLARE #sql VARCHAR(MAX) = 'select * from table'
I've also declared a temp table #Results that matches the column structure of the query.
I then call:
INSERT INTO #Results
EXEC (#sql)
The error I get is:
An INSERT EXEC statement cannot be nested.
I would also like to add that the query executes fine without the part
INSERT INTO #Results
What could be causing it? Any tips will be greatly appreciated
You can't stack together more than 1 INSERT INTO + EXEC, this is a limitation from SQL Server.
DECLARE #Test TABLE (Number INT)
INSERT INTO #Test (Number)
EXEC ('
CREATE TABLE #InnerTable (SomeColumn INT);
INSERT INTO #InnerTable (SomeColumn)
EXEC (''SELECT 1'');
SELECT 1;')
Msg 8164, Level 16, State 1, Line 4 An INSERT EXEC statement cannot be
nested.
If we remove the inner INSERT INTO + EXEC...
DECLARE #Test TABLE (Number INT)
INSERT INTO #Test (Number)
EXEC ('
CREATE TABLE #InnerTable (SomeColumn INT);
SELECT 1;')
Succeed!
There are multiple ways to workaround this limitation, however most of them will require modifying the EXEC content. You can find an exhaustive explanation here. The one I usually use with my procedures is sharing a temporary table. You need to create a temporary table outside the EXEC then load it inside. The table is still accessible outside the EXEC scope because it was created outside.
IF OBJECT_ID('tempdb..#Test') IS NOT NULL
DROP TABLE #Test
CREATE TABLE #Test (Number INT)
EXEC ('INSERT INTO #Test (Number) SELECT 1')
SELECT * FROM #Test
Downside of this approach is that the EXEC part might fail if the temporary table wasn't created or it was created with wrong column names or data types. Keep in mind that this won't work with variable tables, they need to be temporary (at least) to be accessible inside the EXEC scope.

How can I assign an exec result to multiple sql variables?

I got the returned data after executing a stored procedure named [s_sspCallSharePointWebService] in SSMS
DECLARE #sWebAddr AS NVARCHAR(MAX),
#bAPISuccess AS NVARCHAR(10),
#objSharePointXML AS XML
SET #sWebAddr = 'http://...'
EXEC [s_sspCallSharePointWebService] #sWebAddr, #bAPISuccess OUTPUT, #objSharePointXML OUTPUT
I was wondering how do I save
the data in variables instead of returning a table below.
---------------------------------------
| (No column name) | (No column name) |
---------------------------------------
| True | <XMLRoot>.... |
---------------------------------------
Additionally, I cannot insert the data into a temp table here because I had the result of EXEC sp_OAGetProperty #objecttoken, 'responseText' in [s_sspCallSharePointWebService], and then already inserted the response text into a temp table.
That would cause an error:
An INSERT EXEC statement cannot be nested
It is very ugly (for me), but this can be done using temporary tables. The temporary table is visible to all routines in the current scope.
So, you can have nesting like this:
EXEC SP1
...
EXEC SP2
...
EXEC SP3
...
and let's say EXEC SP2 is returning a row set executing a SELECT statement. So, the question is how to materialized it?
CREATE PROCEDURE dbo.SP1
AS
BEGIN;
SELECT 'SP1' AS [Caller];
EXEC dbo.SP2;
END;
GO
CREATE PROCEDURE dbo.SP2
AS
BEGIN;
SELECT 'SP2' AS [Caller];
INSERT INTO #TEST
SELECT 1;
END;
GO
CREATE TABLE #TEST
(
[ID] INT
);
EXEC dbo.SP1;
SELECT *
FROM #TEST;
The idea is to create the temporary table in the outer scope and the procedure to be sure that this table is already created and populate it. Then, use the table in the initial caller.
It's ugly for me, because if this table is not defined the routine will cause an error:
Msg 208, Level 16, State 0, Procedure dbo.SP2, Line 8 [Batch Start
Line 0] Invalid object name '#TEST'.
and it can be difficult to debug in some cases.
Instead of doing that, you can just use a table variable, and then SELECT from it to assign. Here's an example with a simple proc that returns a single row:
USE tempdb;
GO
CREATE OR ALTER PROC dbo.ReturnsOneRow
AS
BEGIN
SELECT 1 AS FirstColumn, 'Hello' AS SecondColumn, 4 AS ThirdColumn;
END;
GO
EXEC dbo.ReturnsOneRow;
GO
DECLARE #FirstColumn int;
DECLARE #SecondColumn varchar(20);
DECLARE #ThirdColumn int;
DECLARE #Row TABLE
(
FirstColumn int,
SecondColumn varchar(20),
ThirdColumn int
);
INSERT #Row
(
FirstColumn, SecondColumn, ThirdColumn
)
EXEC dbo.ReturnsOneRow;
SELECT TOP(1) #FirstColumn = r.FirstColumn,
#SecondColumn = r.SecondColumn,
#ThirdColumn = r.ThirdColumn
FROM #Row AS r;
-- Check the values
SELECT #FirstColumn, #SecondColumn, #ThirdColumn;
The table variable will be way better than a temp table in this situation, and you don't have issues with its visibility elsewhere, or having to clean it up (it's gone at the end of the batch).

Calling a stored procedure from another procedure

Using this table variable:
DECLARE #ReturnValue VARCHAR
DECLARE #OUT_MAIN_ERROR VARCHAR
DECLARE #Result VARCHAR(50)
BEGIN
DECLARE #TableVariable TABLE (result VARCHAR(50))
INSERT INTO #TableVariable
EXEC [dbo].[DRIVEPOOL2]
SELECT result
FROM #TableVariable
END
Using temp table:
DECLARE #ReturnValue VARCHAR
DECLARE #OUT_MAIN_ERROR VARCHAR
DECLARE #Result VARCHAR(50)
BEGIN
CREATE TABLE #kola(result VARCHAR(50))
INSERT INTO #kola
EXEC [dbo].[DRIVEPOOL2]
SELECT *
FROM #kola
DROP TABLE #kola
END
I get error:
Msg 8164, Level 16, State 1, Procedure DRIVEPOOL2, Line 45 [Batch Start Line 3]
An INSERT EXEC statement cannot be nested.
I have tried with both temp table and table variable, both are throwing the error that the INSERT EXEC statement can't be nested.
Drive Pool Procedure for Reference - Github
Seems to be duplicate with this thread: INSERT EXEC Statement cannot be nested
This error occurs when calling a stored procedure and inserting the
result of the stored procedure into a table or table variable (INSERT
... EXECUTE) and the stored procedure being called already contains an
INSERT ... EXECUTE statement within its body.
Read more about this error here:http://www.sql-server-helper.com/error-messages/msg-8164.aspx
You can try OPENROWSET to overcome this problem
INSERT INTO #TableVariable ( col1, col2, col3,... )
SELECT SP.*
FROM OPENROWSET('SQLNCLI', '[Your connection string]','EXECUTE [dbo].[DRIVEPOOL2]') AS SP

T-SQL - Easier way to use stored procedure data

I have a bunch of stored procedures that I call all of the time. I can't change the procedures themselves due to another program that makes use of them. Most of them do NOT have outputs, but have something like this to return value:
SELECT Duration = dbo.fn_Duration(#intPD, #dtmStart, dtmEnd, null)
Currently I am taking every stored procedure and returning it into a table like this:
CREATE TABLE #ResultTable(ret float)
INSERT INTO #ResultTable
EXEC sp_Duration #ID
SELECT #Duration = ret
FROM #ResultTable
Now - because I have 13 stored procedures, I'm doing this over and over (as well as running a delete from #ResultTable) and then pushing this to a table as sort of a "roll up" of data. The purpose is to have 1 location reading the data at a specified interval instead of running the stored procedures over and over from multiple web pages.
I would like to do it more briefly than I am doing it (which is approximately 4 lines of code for each stored procedure. I was hoping to do it more like this:
INSERT INTO #FullResultsTable
EXEC sp_Duration #ID, EXEC sp_GetID #Name, ......
However - that does not seem possible :(
Any ideas on how I can do this better WITHOUT changing the stored procedures themselves?
You cant nest INSERT EXEC commands e.g.
INSERT INTO #Table
EXEC procedure1
EXEC procedure2
would just run the first on in the insert and then run the second as a stand alone command, likewise
INSERT INTO #Table
EXEC procedure1
UNION
EXEC procedure2
would error on the UNION
If you want all the data in one table then you can run multi INSERT INTO EXEC commands to populate the same table. e.g.
CREATE PROCEDURE #TEST1 AS
(
SELECT 1
)
GO
CREATE PROCEDURE #TEST2 AS
(
SELECT 1
)
GO
CREATE PROCEDURE #TEST3 AS
(
SELECT 1
)
GO
CREATE TABLE #TESTTABLE (ID INT)
INSERT INTO #TESTTABLE
EXEC #TEST1
INSERT INTO #TESTTABLE
EXEC #TEST2
INSERT INTO #TESTTABLE
EXEC #TEST3
SELECT * FROM #TESTTABLE
DROP PROCEDURE #TEST1
DROP PROCEDURE #TEST2
DROP PROCEDURE #TEST3
DROP TABLE #TESTTABLE
This would return the results:
ID
1
1
1