Stored procedure parameter contains multiple values - sql-server

My stored procedure parameter contains more than 2 values (eg: create stored procedure recentAssetList #recentAssetList = (id1,id2,..)) then with these parameter how can I get data from a table?

SQL Server doesn't support that logic. Here are some options:
1. Split them amongst many parameters
create procedure yourProc
#FirstParam varchar(10),
#SecondParam varchar(10)
as
-- etc.
go
If some of these parameters may be null you can do this:
create procedure yourProc
#FirstParam varchar(10) = null,
#SecondParam varchar(10) = null
as
select *
from yourTable
where
((#FirstParam is null) or (SomeCol1 = #FirstParam)) and
((#SecondParam is null) or (SomeCol2 = #SecondParam))
go
2. Pass a read only table
create type yourTableData
as table
(
id int not null
)
go
create procedure yourProc
#yourInput yourTableData readonly
as
select *
from yourTable
where id in
(
select id
from #yourInput
)
go

You can use splittion function like that and pass the values in comcatenated string:
CREATE function [dbo].[csv2tbl](#list nvarchar(max), #delimiter nvarchar(10))
returns #res table ([index] int PRIMARY KEY, col nvarchar(max))
AS BEGIN
with tbl_for_csv as
(
select 0 as [index] ,left(#list + #delimiter+#delimiter,charindex(#delimiter,#list + #delimiter+#delimiter) -1)as col,
right(#list + #delimiter+#delimiter,len(#list + #delimiter+#delimiter) - charindex(#delimiter,#list + #delimiter+#delimiter)) as Str
union all
select [index]+1, left(Str,charindex(#delimiter,Str) - 1)as Col,
right(Str,len(Str) - charindex(#delimiter,Str)) from tbl_for_csv
where len(right(Str,len(Str) - charindex(#delimiter,Str))) > 0
)
INSERT #res
select [index], col from tbl_for_csv option (MAXRECURSION 0);
return;
END
In sql2008+ you can pass values to SP thru user defined table types variable (see #Shark answer)

Related

combine #sql query with temp table in SQL [duplicate]

In my stored procedure I declared two table variables on top of my procedure. Now I am trying to use that table variable within a dynamic sql statement but I get this error at the time of execution of that procedure. I am using Sql Server 2008.
This is how my query looks like,
set #col_name = 'Assoc_Item_'
+ Convert(nvarchar(2), #curr_row1);
set #sqlstat = 'update #RelPro set '
+ #col_name
+ ' = (Select relsku From #TSku Where tid = '
+ Convert(nvarchar(2), #curr_row1) + ') Where RowID = '
+ Convert(nvarchar(2), #curr_row);
Exec(#sqlstat);
And I get the following errors,
Must declare the table variable "#RelPro".
Must declare the table variable "#TSku".
I have tried to take the table outside of the string block of dynamic query but to no avail.
On SQL Server 2008+ it is possible to use Table Valued Parameters to pass in a table variable to a dynamic SQL statement as long as you don't need to update the values in the table itself.
So from the code you posted you could use this approach for #TSku but not for #RelPro
Example syntax below.
CREATE TYPE MyTable AS TABLE
(
Foo int,
Bar int
);
GO
DECLARE #T AS MyTable;
INSERT INTO #T VALUES (1,2), (2,3)
SELECT *,
sys.fn_PhysLocFormatter(%%physloc%%) AS [physloc]
FROM #T
EXEC sp_executesql
N'SELECT *,
sys.fn_PhysLocFormatter(%%physloc%%) AS [physloc]
FROM #T',
N'#T MyTable READONLY',
#T=#T
The physloc column is included just to demonstrate that the table variable referenced in the child scope is definitely the same one as the outer scope rather than a copy.
Your EXEC executes in a different context, therefore it is not aware of any variables that have been declared in your original context. You should be able to use a temp table instead of a table variable as shown in the simple demo below.
create table #t (id int)
declare #value nchar(1)
set #value = N'1'
declare #sql nvarchar(max)
set #sql = N'insert into #t (id) values (' + #value + N')'
exec (#sql)
select * from #t
drop table #t
You don't have to use dynamic SQL
update
R
set
Assoc_Item_1 = CASE WHEN #curr_row = 1 THEN foo.relsku ELSE Assoc_Item_1 END,
Assoc_Item_2 = CASE WHEN #curr_row = 2 THEN foo.relsku ELSE Assoc_Item_2 END,
Assoc_Item_3 = CASE WHEN #curr_row = 3 THEN foo.relsku ELSE Assoc_Item_3 END,
Assoc_Item_4 = CASE WHEN #curr_row = 4 THEN foo.relsku ELSE Assoc_Item_4 END,
Assoc_Item_5 = CASE WHEN #curr_row = 5 THEN foo.relsku ELSE Assoc_Item_5 END,
...
from
(Select relsku From #TSku Where tid = #curr_row1) foo
CROSS JOIN
#RelPro R
Where
R.RowID = #curr_row;
You can't do this because the table variables are out of scope.
You would have to declare the table variable inside the dynamic SQL statement or create temporary tables.
I would suggest you read this excellent article on dynamic SQL.
http://www.sommarskog.se/dynamic_sql.html
Well, I figured out the way and thought to share with the people out there who might run into the same problem.
Let me start with the problem I had been facing,
I had been trying to execute a Dynamic Sql Statement that used two temporary tables I declared at the top of my stored procedure, but because that dynamic sql statment created a new scope, I couldn't use the temporary tables.
Solution:
I simply changed them to Global Temporary Variables and they worked.
Find my stored procedure underneath.
CREATE PROCEDURE RAFCustom_Room_GetRelatedProducts
-- Add the parameters for the stored procedure here
#PRODUCT_SKU nvarchar(15) = Null
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
IF OBJECT_ID('tempdb..##RelPro', 'U') IS NOT NULL
BEGIN
DROP TABLE ##RelPro
END
Create Table ##RelPro
(
RowID int identity(1,1),
ID int,
Item_Name nvarchar(max),
SKU nvarchar(max),
Vendor nvarchar(max),
Product_Img_180 nvarchar(max),
rpGroup int,
Assoc_Item_1 nvarchar(max),
Assoc_Item_2 nvarchar(max),
Assoc_Item_3 nvarchar(max),
Assoc_Item_4 nvarchar(max),
Assoc_Item_5 nvarchar(max),
Assoc_Item_6 nvarchar(max),
Assoc_Item_7 nvarchar(max),
Assoc_Item_8 nvarchar(max),
Assoc_Item_9 nvarchar(max),
Assoc_Item_10 nvarchar(max)
);
Begin
Insert ##RelPro(ID, Item_Name, SKU, Vendor, Product_Img_180, rpGroup)
Select distinct zp.ProductID, zp.Name, zp.SKU,
(Select m.Name From ZNodeManufacturer m(nolock) Where m.ManufacturerID = zp.ManufacturerID),
'http://s0001.server.com/is/sw11/DG/' +
(Select m.Custom1 From ZNodeManufacturer m(nolock) Where m.ManufacturerID = zp.ManufacturerID) +
'_' + zp.SKU + '_3?$SC_3243$', ep.RoomID
From Product zp(nolock) Inner Join RF_ExtendedProduct ep(nolock) On ep.ProductID = zp.ProductID
Where zp.ActiveInd = 1 And SUBSTRING(zp.SKU, 1, 2) <> 'GC' AND zp.Name <> 'PLATINUM' AND zp.SKU = (Case When #PRODUCT_SKU Is Not Null Then #PRODUCT_SKU Else zp.SKU End)
End
declare #curr_row int = 0,
#tot_rows int= 0,
#sku nvarchar(15) = null;
IF OBJECT_ID('tempdb..##TSku', 'U') IS NOT NULL
BEGIN
DROP TABLE ##TSku
END
Create Table ##TSku (tid int identity(1,1), relsku nvarchar(15));
Select #curr_row = (Select MIN(RowId) From ##RelPro);
Select #tot_rows = (Select MAX(RowId) From ##RelPro);
while #curr_row <= #tot_rows
Begin
select #sku = SKU from ##RelPro where RowID = #curr_row;
truncate table ##TSku;
Insert ##TSku(relsku)
Select distinct top(10) tzp.SKU From Product tzp(nolock) INNER JOIN
[INTRANET].raf_FocusAssociatedItem assoc(nolock) ON assoc.associatedItemID = tzp.SKU
Where (assoc.isActive=1) And (tzp.ActiveInd = 1) AND (assoc.productID = #sku)
declare #curr_row1 int = (Select Min(tid) From ##TSku),
#tot_rows1 int = (Select Max(tid) From ##TSku);
If(#tot_rows1 <> 0)
Begin
While #curr_row1 <= #tot_rows1
Begin
declare #col_name nvarchar(15) = null,
#sqlstat nvarchar(500) = null;
set #col_name = 'Assoc_Item_' + Convert(nvarchar(2), #curr_row1);
set #sqlstat = 'update ##RelPro set ' + #col_name + ' = (Select relsku From ##TSku Where tid = ' + Convert(nvarchar(2), #curr_row1) + ') Where RowID = ' + Convert(nvarchar(2), #curr_row);
Exec(#sqlstat);
set #curr_row1 = #curr_row1 + 1;
End
End
set #curr_row = #curr_row + 1;
End
Select * From ##RelPro;
END
GO
I don't think that is possible (though refer to the update below); as far as I know a table variable only exists within the scope that declared it. You can, however, use a temp table (use the create table syntax and prefix your table name with the # symbol), and that will be accessible within both the scope that creates it and the scope of your dynamic statement.
UPDATE: Refer to Martin Smith's answer for how to use a table-valued parameter to pass a table variable in to a dynamic SQL statement. Also note the limitation mentioned: table-valued parameters are read-only.
Here is an example of using a dynamic T-SQL query and then extracting the results should you have more than one column of returned values (notice the dynamic table name):
DECLARE
#strSQLMain nvarchar(1000),
#recAPD_number_key char(10),
#Census_sub_code varchar(1),
#recAPD_field_name char(100),
#recAPD_table_name char(100),
#NUMBER_KEY varchar(10),
if object_id('[Permits].[dbo].[myTempAPD_Txt]') is not null
DROP TABLE [Permits].[dbo].[myTempAPD_Txt]
CREATE TABLE [Permits].[dbo].[myTempAPD_Txt]
(
[MyCol1] char(10) NULL,
[MyCol2] char(1) NULL,
)
-- an example of what #strSQLMain is : #strSQLMain = SELECT #recAPD_number_key = [NUMBER_KEY], #Census_sub_code=TEXT_029 FROM APD_TXT0 WHERE Number_Key = '01-7212'
SET #strSQLMain = ('INSERT INTO myTempAPD_Txt SELECT [NUMBER_KEY], '+ rtrim(#recAPD_field_name) +' FROM '+ rtrim(#recAPD_table_name) + ' WHERE Number_Key = '''+ rtrim(#Number_Key) +'''')
EXEC (#strSQLMain)
SELECT #recAPD_number_key = MyCol1, #Census_sub_code = MyCol2 from [Permits].[dbo].[myTempAPD_Txt]
DROP TABLE [Permits].[dbo].[myTempAPD_Txt]
Using Temp table solves the problem but I ran into issues using Exec so I went with the following solution of using sp_executesql:
Create TABLE #tempJoin ( Old_ID int, New_ID int);
declare #table_name varchar(128);
declare #strSQL nvarchar(3072);
set #table_name = 'Object';
--build sql sting to execute
set #strSQL='INSERT INTO '+#table_name+' SELECT '+#columns+' FROM #tempJoin CJ
Inner Join '+#table_name+' sourceTbl On CJ.Old_ID = sourceTbl.Object_ID'
**exec sp_executesql #strSQL;**

how to dynamically find and replace the function text

I have 800+ functions in my database. I would need to modify their source databases dynamically and create snapshots.
example of the function:
create function [schema1].[funTest1] (#param1 varchar(50))
returns table as
return
(
select * from [curr_database1].[schema1].[funTest1](#param1)
union
select * from [curr_database2].[schema1].[funTest1](#param1)
)
I want to change the script as:
create or alter function [schema1].[funTest1] (#param1 varchar(50))
returns table as return
(
select * from [new_database2].[schema1].[funTest1](#param1)
union
select * from [new_database3].[schema1].[funTest1](#param1)
)
basically, I got all the functions script using the sys.syscomments. I'm looking for an option to find and replace the database dynamically to create the snapshots.
How can I get it? Thank you!
Here is the sample code that I have developed for sharing. All the database in the functions starts with the same text(for ex. "curr"). Please share your thoughts. Thanks in advance!
create or alter proc test_proc as
begin
set nocount on
-- this piece of code has the new databases
if object_id('tempdb..#dbNames') is not null drop table #dbNames
create table #dbNames (dbName varchar(1000), id int)
insert into #dbNames(dbName, id) values ('new_database2', 1),('new_database3', 2)
insert into #dbNames(dbName, id) values ('new_database8', 3),('new_database9', 4)
-- this one has the sample functions
if object_id('tempdb..#dbFunctions') is not null drop table #dbFunctions
create table #dbFunctions (funText nvarchar(max))
insert into #dbFunctions (funText) values('create function [schema1].[funTest1] (#param1 varchar(50))
returns table as
return
(
select * from [curr_database1].[schema1].[funTest1](#param1)
union
select * from [curr_database2].[schema1].[funTest1](#param1)
)'),
('create function [schema2].[funTest2] (#param1 varchar(50), #param2 varchar(100))
returns table as
return
(
select * from [curr_database4].[schema2].[funTest2](#param1, #param2)
union
select * from [curr_database5].[schema2].[funTest2](#param1, #param2)
)')
-- declare variables and assign value for #frmStr variable (for testing purposes)
declare #str nvarchar(max)
declare #dbName varchar(100)
declare #frmStr varchar(100) = '[curr_database1]'
-- get the total count of the databases and the functions to iterate and replace the string
declare #dbCnt int = (select count(id) from #dbNames)
declare #fnCnt int = (select count(*) from #dbFunctions)
while #dbCnt > 0
begin
set #dbname = (select dbname from #dbnames where id = #dbcnt)
while #fnCnt > 0
begin
-- this is where I would need to replace the code
select #str = replace(funText, #frmStr, #dbName) from #dbFunctions
select #str
set #fnCnt = #fnCnt - 1
end
set #dbCnt = #dbCnt - 1
end
end
Your actual goal isn't clear, but to answer the question you asked, you can use REPLACE functions in the query to syscomments that you used to get the code in the first place:
REPLACE(
REPLACE([FunctionTextColumn],'curr_database1','new_database2')
,'curr_database2','new_database3'
)

How to separate variables and values, then insert in a table?

Problem
A stored procedure is receiving list of variables and values, and the delimiter. This stored procedure needs to insert those in a table.
--Example table
create table #tempo
(
Variable1 int,
Variable2 int,
Variable3 int
)
These are the parameters to the stored procedure:
declare #variableList varchar(100)
declare #valueList varchar(100)
declare #separator char(1)
set #variableList = 'Variable1#Variable2#Variable3'
set #valueList = '1111#2222#3333'
set #separator = '#'
Result
What I want to achieve is this:
select * from #tempo
+---------+---------+---------+
|Variable1|Variable2|Variable3|
+---------+---------+---------+
|1111 |2222 |3333 |
+---------+---------+---------+
One way to do it
I can use a loop and build dynamic SQL but I want to avoid it. Other than the obvious reasons for not using dynamic SQL, the loop structure is hard to maintain, explain and testing can become an issue too.
Ideal way
I am thinking about a more elegant way to do this, for example with string_split or coalesce etc. But cannot figure out a way without using dynamic SQL or loops.
If you always have same set of column names then it is very easy to do with pivoting, but if columns are changing then you can use the same script with dynamically adjusted list of variables, provided as a parameter or from direct reading from temp table:
INSERT INTO #tempo SELECT *
FROM (
SELECT [value], rv = 'Variable' + CAST(Row_Number() OVER ( ORDER BY (SELECT 1)) as VARCHAR)
FROM STRING_SPLIT(#valueList,#separator)
) AS src
PIVOT (MAX([value]) FOR rv IN (Variable1,Variable2,Variable3)) AS pvt;
You can always try pivoting out the data. This is just the select, but could easily have an insert wrapped into it.
We use a split string with a row ID to allow matching of two split data sets. Function is :
CREATE FUNCTION [dbo].[Split] (#RowData NVARCHAR(MAX), #SplitOn NVARCHAR(5))
RETURNS #RtnValue TABLE (Id INT IDENTITY(1, 1), Data NVARCHAR(100))
AS
BEGIN
DECLARE #Cnt INT;
SET #Cnt = 1;
WHILE (CHARINDEX(#SplitOn, #RowData) > 0)
BEGIN
INSERT INTO #RtnValue (Data)
SELECT Data = LTRIM(RTRIM(SUBSTRING(#RowData, 1, CHARINDEX(#SplitOn, #RowData) - 1)));
SET #RowData = SUBSTRING(#RowData, CHARINDEX(#SplitOn, #RowData) + 1, LEN(#RowData));
SET #Cnt = #Cnt + 1;
END;
INSERT INTO #RtnValue (Data)
SELECT Data = LTRIM(RTRIM(#RowData));
RETURN;
END;
You can then join the two sets together to give some key value pairs, and from there pivot out the data to give the format you requested. If you replace the last select with a select from any of the previous cte's then you can see how the logic unfolds.
DECLARE #variableList VARCHAR(100);
DECLARE #valueList VARCHAR(100);
DECLARE #separator CHAR(1);
SET #variableList = 'Variable1,Variable2,Variable3';
SET #valueList = '1111, 2222, 3333';
SET #separator = ',';
WITH cteVar AS (SELECT Id, Data FROM dbo.Split(#variableList, #separator) )
, cteVal AS (SELECT Id, Data FROM dbo.Split(#valueList, #separator) )
, cteData AS
(SELECT cteVar.Data VariableData
, cteVal.Data ValueData
FROM cteVar
JOIN cteVal ON cteVal.Id = cteVar.Id)
, ctePivot AS
(SELECT *
FROM cteData
PIVOT ( MAX(ValueData)
FOR VariableData IN ([Variable1], [Variable2], [Variable3])) AS PivotTable)
SELECT *
FROM ctePivot;
This is quite a long approach to it but hopefully it well help you understand the steps involved. Its worth looking at the Pivot function in general anyway, its well documented.

INSERT INTO with exec with multiple result sets

SQL Server allows me to insert the returned result set of a stored procedure as:
DECLARE #T TABLE (
ID int,
Name varchar(255),
Amount money)
INSERT INTO #T
exec dbo.pVendorBalance
This works as long as the stored procedure only returns 1 result set.
Is there a way to make this work if the stored procedure returns several result sets?
E.g.
DECLARE #T1 (...)
DECLARE #T2 (...)
INSERT INTO #T1 THEN INTO #T2
exec dbo.pVendorBalance
One workaround to this problem is using OUTPUT parameters (JSON/XML) instead of resultsets.
CREATE TABLE tab1(ID INT, Name NVARCHAR(10), Amount MONEY);
INSERT INTO tab1(ID, Name, Amount)
VALUES (1, 'Alexander', 10),(2, 'Jimmy', 100), (6, 'Billy', 20);
CREATE PROCEDURE dbo.pVendorBalance
AS
BEGIN
-- first resultset
SELECT * FROM tab1 WHERE ID <=2;
-- second resultset
SELECT * FROM tab1 WHERE ID > 5;
END;
Version with OUT params:
CREATE PROCEDURE dbo.pVendorBalance2
#resultSet1 NVARCHAR(MAX) OUT,
#resultSet2 NVARCHAR(MAX) OUT
AS
BEGIN
SELECT #resultSet1 = (SELECT * FROM tab1 WHERE ID <=2 FOR JSON AUTO),
#resultSet2 = (SELECT * FROM tab1 WHERE ID > 5 FOR JSON AUTO);
END;
And final call:
DECLARE #r1 NVARCHAR(MAX), #r2 NVARCHAR(MAX);
EXEC dbo.pVendorBalance2 #r1 OUT, #r2 OUT;
-- first resultset as table
SELECT *
INTO #t1
FROM OpenJson(#r1)
WITH (ID int '$.ID', [Name] NVARCHAR(50) '$.Name',Amount money '$.Amount');
-- second resultset as table
SELECT *
INTO #t2
FROM OpenJson(#r2)
WITH (ID int '$.ID', [Name] NVARCHAR(50) '$.Name',Amount money '$.Amount');
SELECT * FROM #t1;
SELECT * FROM #t2;
DBFiddle Demo
EDIT:
Second approach is to use tSQLt.ResultSetFilter CLR function (part of tSQLt testing framework):
The ResultSetFilter procedure provides the ability to retrieve a single result set from a statement which produces multiple result sets.
CREATE TABLE #DatabaseSize (
database_name nvarchar(128),
database_size varchar(18),
unallocated_space varchar(18)
);
CREATE TABLE #ReservedSpaceUsed (
reserved VARCHAR(18),
data VARCHAR(18),
index_size VARCHAR(18),
unused VARCHAR(18)
);
INSERT INTO #DatabaseSize
EXEC tSQLt.ResultSetFilter 1, 'EXEC sp_spaceused';
INSERT INTO #ReservedSpaceUsed
EXEC tSQLt.ResultSetFilter 2, 'EXEC sp_spaceused';
SELECT * FROM #DatabaseSize;
SELECT * FROM #ReservedSpaceUsed;
No. But there is more of a work around since you cannot do an insert into with a procedure that returns multiple results with a different number of columns.
If you are allowed to modify the stored procedure, then you can declare temp tables outside of the procedure and populate them within the stored procedure. Then you can do whatever you need with them outside of the stored procedure.
CREATE TABLE #result1(Each column followed by data type of first result.);
----Example: CREATE TABLE #result1(Column1 int, Column2 varchar(10))
CREATE TABLE #result2(Each column followed by data type of second result.);
EXEC pVendorBalance;
SELECT * FROM #result1;
SELECT * FROM #result2;
I had a similar requirement, and ended up using the a CLR function which you can read about here (it's the answer with the InsertResultSetsToTables method, by user Dan Guzman):
https://social.msdn.microsoft.com/Forums/sqlserver/en-US/da5328a7-5dab-44b3-b2b1-4a8d6d7798b2/insert-into-table-one-or-multiple-result-sets-from-stored-procedure?forum=transactsql
You need to create a SQL Server CLR project in Visual Studio to get going. I had a project already written by a co-worker that I could just expand, but if you're starting from scratch, try reading this guide:
http://www.emoreau.com/Entries/Articles/2015/04/SQL-CLR-Integration-in-2015-year-not-product-version.aspx
If you've succeeded in writing and publishing the CLR project to the database, here is an example of using it I wrote:
-- declare a string with the SQL you want to execute (typically an SP call that returns multiple result sets)
DECLARE #sql NVARCHAR(MAX)
SET #sql = 'exec usp_SomeProcedure #variable1 = ' + #variable1 + '...' -- piece together a long SQL string from various parameters
-- create temp tables (one per result set) to hold the output; could also be actual tables (non-temp) if you want
CREATE TABLE #results_1(
[CustomerId] INT, [Name] varchar(500), [Address] varchar(500)
);
CREATE TABLE #results_2(
[SomeId] UNIQUEIDENTIFIER, [SomeData] INT, [SomethingElse] DateTime
);
-- on the exemplary 'CustomerDatabase' database, there is an SP (created automatically by the SQL CLR project deployment process in Visual Studio) which performs the actual call to the .NET assembly, and executes the .NET code
-- the CLR stored procedure CLR_InsertResultSetsToTables executes the SQL defined in the parameter #sourceQuery, and outputs multiple result sets into the specified list of tables (#targetTableList)
EXEC CustomerDatabase.dbo.CLR_InsertResultSetsToTables #sourceQuery = #sql, #targetTableList = N'#results_1,#results_2';
-- The output of the SP called in #sql is now dumped in the two temp tables and can be used for whatever in regular SQL
SELECT * FROM #results_1;
SELECT * FROM #results_2;
We can do it in the following way
Consider the input SP (which returns 2 tables as output) as usp_SourceData
Alter the usp_SourceData to accept a parameter as 1 and 2
Adjust the SP in a way that when
usp_SourceData '1' is executed it will return first table
and when
usp_SourceData '2' is executed it will return second table.
Actually stored procedures can return multiple result sets, or no result sets, it's pretty arbitrary. Because of this, I don't know of any way to navigate those results from other SQL code calling a stored procedure.
However, you CAN use the returned result set from a table-valued user defined function. It's just like a regular UDF, but instead of returning a scalar value you return a query result. Then you can use that UDF like any other table.
INSERT INTO #T SELECT * FROM dbp.pVendorBalanceUDF()
http://technet.microsoft.com/en-us/library/ms191165(v=sql.105).aspx
DROP TABLE ##Temp
DECLARE #dtmFrom VARCHAR(60) = '2020-12-01 00:00:00', #dtmTo VARCHAR(60) = '2020-12-02 23:59:59.997',#numAdmDscTransID VARCHAR(60) =247054
declare #procname nvarchar(255) = 'spGetCashUnpaidBills',
#procWithParam nvarchar(255) = '[dbo].[spGetCashUnpaidBills] #dtmFromDate= ''' +#dtmFrom+ ''' ,#dtmToDate= ''' +#dtmTo+''',#numCompanyID=1,#numAdmDscTransID='+ #numAdmDscTransID +',#tnyShowIPCashSchemeBills=1',
#sql nvarchar(max),
#tableName Varchar(60) = 'Temp'
set #sql = 'create table ##' + #tableName + ' ('
begin
select #sql = #sql + '[' + r.name + '] ' + r.system_type_name + ','
from sys.procedures AS p
cross apply sys.dm_exec_describe_first_result_set_for_object(p.object_id, 0) AS r
where p.name = #procname
set #sql = substring(#sql,1,len(#sql)-1) + ')'
execute (#sql)
execute('insert ##' + #tableName + ' exec ' + #procWithParam)
end
SELECT *FROM ##Temp
If the both result sets have same number of columns then
insert into #T1 exec dbo.pVendorBalance
will insert the union of both data set into #T1.
If not
Then edit dbo.pVendorBalance and insert results into temporary tables and in outer stored proc, select from those temporary tables.
Another way(If you need it), you can try
SELECT * into #temp
from OPENROWSET('SQLNCLI', 'Server=(local)\\(instance);Trusted_Connection=yes;',
'EXEC dbo.pVendorBalance')
it will take first dataset.

Can I send array of parameter to store procedure?

I have User table, it has UserId uniqueidentifier, Name varchar and IsActive bit.
I want to create store procedure to set IsActive to false for many user, for example, if I want to deactive 2 users, I want to send Guid of those users to store procedure (prefer as array). I want to know how can I do it?
P.S. I'm working on Microsoft SQL Azure
Along the same lines than Elian, take a look at XML parameters. Generally speaking you should have a cleaner/safer implementation using xml than parsing a list of strings. Click here for a code example
Here is a solution I used a while ago and that was working fine.
Send the list of guid you want to deactive merged into a comma separated string to the sp.
Then in the sp, you first convert this string into a table thanks to a table-valued function.
Here is a sample with bigint, but you can easily modify it so that it works with guid
Step 1 : the table-valued function
CREATE FUNCTION [dbo].[BigIntListToTable] (
#list VARCHAR(max)
)
RETURNS
#tbl TABLE
(
nval BIGINT NOT NULL
) AS
BEGIN
DECLARE #nPos INT
DECLARE #nNextPos INT
DECLARE #nLen INT
SELECT #nPos = 0, #nNextPos = 1
WHILE #nNextPos > 0
BEGIN
SELECT #nNextPos = CHARINDEX(',', #list, #nPos + 1)
SELECT #nLen = CASE WHEN #nNextPos > 0
THEN #nNextPos
ELSE LEN(#list) + 1
END - #nPos - 1
INSERT #tbl (nval)
VALUES (CONVERT(BIGINT, SUBSTRING(#list, #nPos + 1, #nLen)))
SELECT #nPos = #nNextPos
END
RETURN
END
Step 2 : the stored proc
CREATE PROCEDURE [dbo].[spMySP]
#IdList VARCHAR(max)
AS
BEGIN
SET NOCOUNT ON;
SET ROWCOUNT 0
UPDATE dbo.YourTable
SET isActive = 0
FROM dbo.YourTable
INNER JOIN dbo.BigIntListToTable(#IdList) l
ON dbo.YourTable.id = l.nval
END

Resources