Calling two levels of stored procedure - sql-server

In SQL Server 2014 I have two stored procedures. One calls the other with the INSERT command.
I want to call the upper stored procedure to get the result table of it. However, I get an error:
Msg 213, Level 16, State 7, Procedure HelloWorld, Line 23
Column name or number of supplied values does not match table definition.
This is an example of my completed code:
1st stored procedure:
ALTER PROCEDURE [dbo].[HelloWorld_2]
#aaa VARCHAR(20)
AS
BEGIN
SELECT 1 AS col1, #aaa AS col2
END
2nd stored procedure (it calls the 1st stored procedure)
ALTER PROCEDURE [dbo].[HelloWorld]
#aaa VARCHAR(20)
AS
BEGIN
DECLARE #temp TABLE(col1 INT, col2 VARCHAR(20))
INSERT INTO #temp
EXEC [HelloWorld_2] #aaa
SELECT *, 'patata'
FROM #temp
END
SQL that I need to execute (it calls the 2nd stored procedure)
DECLARE #temp TABLE(col1 INT, col2 VARCHAR(20), col3 VARCHAR(20))
INSERT INTO #temp
EXEC HelloWorld 3
The return values has to be a table with 3 columns and 1 row. But this error appears:
Column name or number of supplied values does not match table definition
However the temp table is correct with the column order and values...

Thanks for all replies.
Finally, I create a function of 'HelloWorld_2', so the stored procedure 'HelloWorld' and the SQL code works correctly.

Related

Insert into distinct columns from a stored procedure

How to insert into a temp table that is all ready created inside of a stored procedure
ALTER PROCEDURE [dbo].[Report_1]
BEGIN
CREATE TABLE #Temp
(
col1 INT,
col2 INT,
col3 VARCHAR(50)
)
INSERT INTO #Temp
EXEC [spSelection] #ID
..do stuff
..do stuff
..do stuff
SELECT * FROM #temp
END
The problem I am having is, I will use this stored procedure (spSelection) in the future and if I change this stored procedure to get more columns for a different stored procedure, then Report_1 will fail.
So I need a way to dynamically create the table or be able to only select distinct columns from the output of exec [spSelection] #ID or have Report_1 be able to read from a temp table created in spSelection.
I have tried to use a global and that will not work because it can be used by other stored procedure at the same time, if I create a dynamic SQL.
#sql ='
create table #Temp(
col1 int,col2 int,col3 varchar(50)
) ' exec sp_executesql #sql
I can not access the #temp table outside of the quotes
One alternative is to change your SP to make the insert inside the SP:
ALTER PROCEDURE [spSelection]
AS
BEGIN
-- Validate that your temporary table was created (the insert will fail otherwise)
IF OBJECT_ID('tempdb..#Temp') IS NULL
BEGIN
RAISERROR ('The table #Temp must be created before executing this SP', 16, 1)
RETURN
END
..do stuff
..do stuff
..do stuff
INSERT INTO #Temp (
col1,
col2,
col3)
SELECT
/*Columns*/
END
GO
ALTER PROCEDURE [dbo].[Report_1]
BEGIN
CREATE TABLE #Temp
(
col1 INT,
col2 INT,
col3 VARCHAR(50)
)
EXEC [spSelection] #ID -- Will insert into #Temp
..do stuff
..do stuff
..do stuff
SELECT * FROM #temp
END
This approach will fail if you eventually add new columns to the #Temp table and insert them inside the SP without updating the CREATE TABLE in every SP that calls it.
There is no definitive solution here, please read this excellent paper about all possible ways to share data between SP, with pros and cons of each (the solution I posted here is listed as 4. Using a table).
Instead of creating a stored procedure for selecting results, you can create a view and use SELECT INTO clause to dynamically create temp table at run time.
You can not use stored procedure in select statement.

SQL Insert along with matching audit at same time from app

I've got an app that will insert lines to table [P_R], which has first field being [PrimaryKey].
Now I have to add another table, [Actions] with fields [PrimaryKey],[P_R_PK],[User],[ActionTime].
When the app inserts a line to [P_R], I don't know what the PrimaryKey will be, but I have to simultaneously insert to [Actions] with the value in [P_R_PK] being the PrimaryKey I just added to [P_R]. How do I get this PrimaryKey value to [P_R_PK]?
For reference, I'm using a vb.net windows form with a SQL Server database.
If you're using a stored procedure to add the records to [P_R], you can call another stored procedure in the first that includes the primary key. For example:
CREATE PROC AddToP_R
#field1 varchar(10),
#field2...10
AS
BEGIN
declare #pk int --primary key that's created upon inserting
--insert into [P_R]
INSERT INTO [P_R]
VALUES (#field1,...)
--set the var we created to be the primary key
SET #pk = SCOPE_IDENTITY()
--call second proc
EXEC Second_Proc #pk
END
If you need other fields in the second stored procedure, include them in the first procedure parameter list.
Another way would be to a wrapper stored procedure that calls both the other two. For this to work, you would need an output variable in the first procedure to return the primary key. For example:
CREATE PROC AddWrapper
#fieldsforfirstproc...,
#fieldsforsecondproc...
AS
BEGIN
declare #outputVar int --primary key
EXEC firstproc #fieldsforfirstproc..., #outputvar output --adds the record to the first table and returns #outputvar as the primary key
EXEC secondproc #fieldsforsecondproc..., #outputvar --adds the record to the second table using #output var
END
I prefer the second option because it removes logic from the first procedure that doesn't need to be there. However, the first procedure would be slightly different to how I showed earlier.
CREATE PROC AddToP_R
#field1 varchar(10),
#field2...10,
#pk int OUTPUT --primary key that's created upon inserting
AS
BEGIN
--insert into [P_R]
INSERT INTO [P_R]
VALUES (#field1,...)
--set the var we created to be the primary key
SET #pk = SCOPE_IDENTITY()
END
You can retrieve it by using SELECT SCOPE_IDENTITY() after the INSERT.
For example:
DECLARE #T table (id int PRIMARY KEY IDENTITY, value int)
INSERT INTO #T (value) VALUES (2)
SELECT SCOPE_IDENTITY() new_pk
I would also consider doing it all in one stored procedure within a transaction. Given that you are inserting into more than one table, a transaction would allow you to roll back should anything go wrong.

Options besides table valued parameters for passing table variable?

We have many stored procedures that are used for reports. All these procedures follow the following format. In essence, the SP does work, and the final result is inserted into a #table variable:
ALTER procedure dbo.usp_GetComplexData
as
declare #MyTable table
(
Col1 varchar(20),
Col2 varchar(20)
)
-- Here the SP is doing lots of work with lots of tables.
-- The result inserted in #MyTable
SELECT Col1, Col2 from #MyTable
Now I need to send via email (in html format) the results of these stored procedures.
I also have SpCustomTable2HTML (found at Symantec) that converts any table into an html table. It doesn't need the table schema to do its work; it simply takes the table and returns an html table.
So here's the stored procedure:
ALTER procedure usp_sendHtmlReportViaEmail
as
DECLARE #HTML1 NVARCHAR(MAX)
IF OBJECT_ID('tempdb..#Results') IS NOT NULL
drop TABLE #results
select top 50 * into #results From MyTable
EXEC SpCustomTable2HTML '#results', #HTML1 OUTPUT, '', ''
EXEC sp_send_dbmail #profile_name='My profile',
#recipients='test#Example.com',
#subject='Test message',
#body_format = 'HTML',
#body=#HTML1
I would like to somehow call usp_sendHtmlReportViaEmail from usp_GetComplexData by sending it #MyTable as parameter. I was reading about table valued parameters, but that requires to create a TVP for each table that I would pass. I don't want to create a specific TVP for each table that will be passed to usp_sendHtmlReportViaEmail.
Are there any other options?
Thanks.
If you're determined to use SQL, then you should look into using a global temporary table. You have to make sure you clean up after your code executes to avoid using too many resources, but it might be a valid approach for you.
At the end of your usp_GetComplexData procedure, just insert the data into a ##TemporaryTable and use that as the parameter to usp_sendHtmlReportViaEmail. Since I don't know what exactly you do with the table variable, I won't replace it, but you could potentially replace it with the temporary table instead.
ALTER PROCEDURE usp_GetComplexData
AS BEGIN
DECLARE #MyTable TABLE
(/*... Columns ...*/);
-- Do complex data stuff...
SELECT
*
INTO
##MyTempTable
FROM
#MyTable;
EXECUTE usp_sendHtmlReportViaEmail '##MyTempTable';
SELECT
*
FROM
#MyTable;
END

Temp Table behavior with stored procedures

SQL Server 2008
I create a local temp table in a stored procedure that then calls another stored procedure that ALTERs the temp table by adding columns to it. SELECT * behaves as expected/desired in both the calling and the called stored procedure: the modified table columns are returned.
However, if I attempt to SELECT [added column], I get an error 'invalid column'.
CREATE PROCEDURE ProcA
AS
BEGIN
CREATE TABLE #BigTemp
(
BigTempId INT IDENTITY (1,1) NOT NULL
);
execute ProcB;
SELECT * FROM #BigTemp;
END
CREATE PROCEDURE ProcB
AS
BEGIN
ALTER TABLE #BigTemp
ADD ProductId int NULL;
SELECT * FROM #BigTemp;
END
Execution of ProcA returns
BigTempId ProductId
-----------------------
and
BigTempId ProductId
-----------------------
Now, if I modify ProcB:
ALTER PROCEDURE ProcB
AS
BEGIN
ADD ProductId int NULL;
SELECT ProductId FROM #BigTemp;
END
I'm rewarded with:
Invalid column name 'ProductId'
Now, in my actual objects I'm creating the ALTER/ADD statement dynamically and executing it with sp_executesql, but the behavior is the same. It sure doesn't make any sense to me. Ideas?
I am still not getting why this behavior with Temp tables.
But below code do the trick to over come it.
ALTER PROCEDURE PROCA
AS
BEGIN
CREATE TABLE #BIGTEMP
(
BIGTEMPID INT IDENTITY (1,1) NOT NULL
);
EXECUTE PROCB;
SELECT PRODUCTID FROM #BIGTEMP B
END
ALTER PROCEDURE PROCB
AS
BEGIN
ALTER TABLE #BIGTEMP
ADD PRODUCTID INT NULL;
SELECT * INTO #TEMP FROM #BIGTEMP
SELECT PRODUCTID FROM #TEMP
END
Issue is due to pre compilation . add WITH RECOMPILE or exec with recomile

Sql batch insert

I have a stored procedure usp_GetValues.
EXEC usp_GetValues '123' gives a list of names like :
Names
=======
Joy
Roy
Toy
I have another table, where I want to insert record like :
Insert into NewNameTable
Values ('HighSchool', Names, 'true')
Where name list will be coming from the stored procedure execution.
Is there any way to do this bulk insert?
Try this ...
CREATE TABLE #TestTable ([Names] NVARCHAR(256))
INSERT INTO #TestTable
EXEC usp_GetValues '123'
Insert into NewNameTable Select ('HighSchool', Names, 'true') from #TestTable
First store name values from executing stored procedure in temporary table and then insert into target table with default values.
DECLARE #tempNameTable table(names varchar(100))
INSERT INTO #tempNameTable
EXEC usp_GetValues '123'
INSERT INTO NewNameTable
SELECT 'High School',names,'true'
FROM #tempNameTable

Resources