How to execute SP in for each and union all results? - sql-server

I have a table with certain records, let's call this the Priority table. For each record in this table I need to execute a stored procedure called GetProductsBasedOnPriorityRecord which returns records from a Product table.
All these results need to be returned as one UNION without duplicates.
I'm able to write the following C# pseodo code, but I have no idea on how to do this in SQL:
//DECLARE resultVariable
var products = new List<Product>();
//SELECT * FROM Priority and FOREACH over this
foreach(var prio in Priority) {
//EXEC GetProductsBasedOnPriorityRecord and UNION this on the resultVariable
products.Union(GetProductsBasedOnPriorityRecord(prio));
}
//RETURN resultVariable
return products;

You should not be writing SQL code in loops if you need it to perform even relatively well. But this is what I would do in this case.
-- build some variables
declare #prio table (prio varchar(100) not null)
declare #singlePrio varchar(50)
declare #sqlcmd varchar(50)
-- assuming that the output of the stored procedure is just a table of INTs
declare #output table (outputID int not null)
-- build the list to loop over
insert into #prio (prio)
select * from [Priority]
while exists (select 1 from #prio)
begin
select #singlePrio = (select top 1 prio from #prio)
--
insert into #output exec GetProductsBasedOnPriorityRecord #singlePrio
--
delete from #prio where prio = #singlePrio
end
-- using distinct because you want a 'union' but didnt say 'union all'
select distinct outputID from #output

Well you should be able to do it with a cursor. It's not always the best way. I assume that you are forced to use the existing stored procedure.
Example tables
CREATE TABLE [dbo].[Products](
[ProductID] [int] IDENTITY(1,1) NOT NULL,
[ProductName] [nchar](10) NULL,
[PriorityID] [int] NULL)
CREATE TABLE [dbo].[Priority](
[PriorityID] [int] IDENTITY(1,1) NOT NULL,
[PriorityCode] [nchar](10) NULL)
(with PKs etc).
Sample stored procedure:
ALTER PROCEDURE [dbo].[GetProductsBasedOnPriorityRecord]
-- Add the parameters for the stored procedure here
#PriorityID int = 0
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
SELECT * FROM Products WHERE PriorityID = #PriorityID
END
Then something like this:
DECLARE #PriorityID INT
DECLARE #table TABLE (ProductID INT, ProductName NCHAR(10), PriorityID INT)
DECLARE cur CURSOR FOR SELECT PriorityID FROM Priority
OPEN cur
FETCH NEXT FROM cur INTO #PriorityID
WHILE ##FETCH_STATUS = 0
BEGIN
INSERT INTO #table EXEC GetProductsBasedOnPriorityRecord #PriorityID
FETCH NEXT FROM cur INTO #PriorityID
END
CLOSE cur
DEALLOCATE cur
SELECT DISTINCT * FROM #table
Of course this makes some assumptions about your tables. Also (of course) a synthetic example like this is actually showing a long-winded way of doing something very simple.

Try this one -
DECLARE #SQL NVARCHAR(MAX)
IF OBJECT_ID (N'tempdb.dbo.#temp') IS NOT NULL
DROP TABLE #temp
CREATE TABLE #temp (outputID INT NOT NULL)
SELECT #SQL = (
SELECT CHAR(13) + '
INSERT INTO #temp(outputID)
EXEC dbo.GetProductsBasedOnPriorityRecord '''
+ prio + ''''
FROM dbo.[Priority]
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)')
PRINT #SQL
EXEC sys.sp_executesql #SQL
SELECT DISTINCT outputID
FROM #temp

Related

How to Pass Parameter as a List In a Stored procedure in a dynamic Insert Query

I have a scenario that #PRIVILEGEID will have multiple value and #ROLEID will be same every time for those privilege Id.
So I have to insert the data into table.
Below is the Code For Procedure:-
ALTER PROCEDURE [dbo].[ADD_ROLE] (
#ROLENAME varchar(50),
#PRIVILEGEID int )
AS
BEGIN
DECLARE #QUERY varchar(1000);
DECLARE #ROLEID int;
SET #ROLEID = ( SELECT Max(ROLEID)
FROM ( SELECT
Max(CREATED_DATE) AS MAX_DATE,
ROLEID
FROM ROLE
WHERE ROLENAME = #ROLENAME
--(ROlename can be changed dynamically, take 'Manager' as example as of now.)
AND CREATED_DATE IS NOT NULL
GROUP BY ROLEID ) X );
--PRINT #ROLEID
SET #QUERY = 'INSERT INTO [ROLES_PRIVILEGES] (ROLEID,PRIVILEGEID) VALUES (''' + Cast(#ROLEID AS varchar) + ''',''' + Cast(#PRIVILEGEID AS varchar) + ''')';
EXECUTE ( #QUERY );
END;
Now #PRIVILEGEID will have a dynamic list of multiple values for the fixed #ROLEID and My issue is I can pass only one #PRIVILEGEID at one time.
Exec ADD_ROLE 'BACKOFFICE',#PRIVILEGEID #PRIVILEGEID=[2, 3, 4, 5, 6]
-- (How to pass multiple value for PRIVILEGEID )
I have tried While loop also but not sure how to implement it.
Please help.
Here is an example where I take a comma delimited string and dump into a table. This process works by converting it into xml and using xmlqry to parse. I like it because I feel its easier to read and looks less convoluted.
Let me know if it works for you.
USE yourDatabase
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[_parsedelimited]
(
#str varchar(max)
)
AS
BEGIN
if object_id('tempdb..#Emails') is not null drop table #Emails;
CREATE TABLE #Emails (Email varchar(2500));
Declare #x XML;
select #x = cast('<A>'+ replace(#str,',','</A><A>')+ '</A>' as xml);
INSERT INTO #Emails (Email)
select
t.value('.', 'varchar(50)') as inVal
from #x.nodes('/A') as x(t);
SELECT * FROM #Emails;
drop table #Emails;
END
GO
Consider the following:
declare #role varchar(20) = 'BACKOFFICE',
#PRIVILEGEID varchar(100) = '2,3,4,5,6';
select #role, *
from string_split(#PRIVILEGEID, ',');
Using that select, you should be able to insert it into whatever table you'd like.

Execute Procedure with Parameters in Sql Table

I am having a procedure with two parameters and a table with two columns defined as para1 and para2.
Now, there are around 5000 rows in a table and I want to run the procedure with using the columns values are input parameters.
I can run the procedure with static parameters but don't know how to do it using parameters in table.
You have two options - looping or using dynamic T-SQL statement.
Here is example using the latter:
DECLARE #DataSource TABLE
(
[Param01] INT
,[Param02] VARCHAR(12)
);
INSERT INTO #DataSource ([Param01], [Param02])
VALUES (1, 'a2')
,(2, 'a2')
,(3, 'a4')
,(4, 'a0');
DECLARE #DynamictTSQLStatement NVARCHAR(MAX);
SELECT #DynamictTSQLStatement =
STUFF
(
(
SELECT CHAR(10) + 'EXEC [dbo].[my_stored_procedured] #Param01 = ' + CAST([Param01] AS VARCHAR(12)) + ', #Param02 = "' + [Param02] + '";'
FROM #DataSource
FOR XML PATH(''), TYPE
).value('.', 'VARCHAR(MAX)')
,1
,1
,''
);
EXEC sp_executesql #DynamictTSQLStatement;
For a loop, the things are pretty the same as in all language. Read a row, get current parameters values, execute the routine and continue with the next row until all of the are read.
I used below codes and it works
create procedure [procedurename]
as
declare #field1 varchar(15)
declare #field2 varchar(15)
declare cur CURSOR LOCAL for
select field1, field2 from donor_update_list a
open cur
fetch next from cur into #field1, #field2
while ##FETCH_STATUS = 0
BEGIN
--execute your sproc on each row
exec uspYourSproc #field1, #field2
fetch next from cur into #field1, #field2
END
close cur
deallocate cur
just create your procedure like:
CREATE PROCEDURE s_proc_name #p1 nvarchar(30) = NULL, #p2 nvarchar(60) = NULL
AS
--your code
after that you can for example create a string by doing a query on your table like:
select 's_proc_name ', columnP1, ' ', columnP2
from tableName
This will return an output with you possible input.
after that you can execute the lines you want manually just copy pasting or you can put this in a temp table like:
select 's_proc_name ', columnP1, ' ', columnP2
into #tmp
from tableName
and execute programmatically each line just by using a loop on the table and execute the output string with EXEC(#record)

Creating view from multiple databases linked by columns with db name

Hi I need to create a view or stored procedure that combines data and returns a result set from 3 different databases on the same server using a column that holds a schema (db) name.
For Example on the first DB I have this table:
CREATE TABLE [dbo].[CloudUsers](
ID int IDENTITY(1,1) NOT NULL,
Username nvarchar(50) NULL,
MainDB nvarchar(100) NULL
) ON [PRIMARY]
Each CloudUser has a separate DB so next now I need to fetch the data from the User database using the MainDB name. The data I need is always 1 row cause I'm using aggregate functions / query.
So in the User MainDB let's say I have this table.
CREATE TABLE [dbo].[CLIENT](
ID int NOT NULL,
Name nvarchar(50) NULL,
ProjectDBName [nvarchar](100) NULL
CreationDate datetime NULL
) ON [PRIMARY]
And I query like:
select min(CreationDate) from MainDB.Client
The same Idea for the Client I need to fetch even more data from a 3rd database that points to the Client ProjectDBName. Again it's aggregate data:
select Count(id) as TotalTransactions from ProjectDBName.Journal
My final result should have records from all databases. It's readonly data that I need for statistics.
Final result set example:
CloudUsers.Username, MainDB->CreationDate, ProjectDBName->TotalTransaction
How can I achieve that ?
This is not easy - and without a schema and sample data, I can't give you a precise answer.
You need to iterate through each client, and use dynamic SQL to execute a the query against the mainDB and projectDB join. You can either do that in one gigantic "union" query, or by creating a temporary table and inserting the data into that temporary table, and then selecting from the temp table at the end of the query.
For you who are curious of how to solve this issue I have found my own solution using some cursors + dynamic and a simple table variable, enjoy.
ALTER PROCEDURE CloudAnalysis as
DECLARE #objcursor cursor
DECLARE #innercursor cursor
DECLARE #userid int
,#maindb nvarchar(100)
,#clientid int
,#name nvarchar(50)
,#projdb nvarchar(100)
,#stat nvarchar(50)
,#sql nvarchar(max)
,#vsql nvarchar(max)
,#rowcount int
DECLARE #result table(userid int,clientid int,maindb nvarchar(100),name nvarchar(50),projdb nvarchar(100),stat nvarchar(50))
SET #objcursor = CURSOR FORWARD_ONLY STATIC FOR SELECT c.id,c.maindb,u.client_id FROM dbo.ClientUsers c join dbo.UserClients u on c.id = u.user_id open #objcursor
FETCH NEXT FROM #objcursor INTO #userid,#maindb,#clientid
WHILE (##FETCH_STATUS=0)
BEGIN
IF (EXISTS (SELECT name
FROM master.dbo.sysdatabases
WHERE ('[' + name + ']' = #maindb
OR name = #maindb)))
BEGIN
set #sql = N'SELECT #name = c.name,#projdb=c.ProjectDBName FROM ' + #maindb + '.dbo.CLIENT c WHERE c.id = ' + cast(#clientid as nvarchar)
EXECUTE sp_executesql #sql, N'#name NVARCHAR(50) OUTPUT,#projdb NVARCHAR(100) OUTPUT',
#name = #name OUTPUT
,#projdb = #projdb OUTPUT
SELECT #rowcount = ##ROWCOUNT
IF #rowcount > 0
BEGIN
--print ' client: ' + cast(#clientid as nvarchar)+
--':' + #name + ' projdb: ' + #projdb
IF (EXISTS (SELECT name
FROM master.dbo.sysdatabases
WHERE ('[' + name + ']' = #projdb
OR name = #projdb)))
BEGIN
SET #sql = N'SELECT #stat = j.stat FROM ' + #projdb + '.dbo.JournalTransaction j'
EXECUTE sp_executesql #sql
,N'#stat NVARCHAR(50) OUTPUT'
,#stat = #stat OUTPUT
END
INSERT INTO #result (userid,clientid,maindb,name,projdb,stat)
VALUES (#userid,#clientid,#maindb,#name,#projdb,#stat)
END
END
FETCH NEXT FROM #objcursor INTO #userid,#maindb,#clientid
END
CLOSE #objcursor
DEALLOCATE #objcursor
SELECT * FROM #result

Is it possible to read data in another session's temporary table?

We're maintaining (and occasionally debugging) a large in-house system. The system has 20+ databases, and a number of servers interfacing to other systems, processing data, etc. Not all is in-house developed - i.e. we don't always have access to source code.
At one place, we can see the system creating a #temp table - and then, in the next step, failing due to a data-error. We can see the existence of the #temp table in Management Studio - it exists in tempdb --> Temporary Tables as something like
#MyStuff________________________________________________________________________________________________________000000A65029
Obviously, the context menu here doesn't offer the full functionality (with Create table, select top 1000, etc.) - but only Reportsand Refresh.
I can find the table in sys.objects, sys.tables and even its column definition in sys.columns.
The question is: Is it in any way possible to access the data in the table? We can break execution so as to ensure that the table stays in scope, so the table vanishing shouldn't be an issue. This is not something that is to be done regularly or in code - it's more or less a one-shot deal. (I hope).
Unwieldy but you can examine the tables pages from an admin logon.
Get object id;
select object_id from tempdb.sys.tables where name like '#mystuff%'
Get a list of allocated pages;
dbcc ind('tempdb', <object id>, -1)
for each of the PageFID / PagePID (file/page IDs)
dbcc traceon(3604);
dbcc page(tempdb, <PageFID>, <PagePID>, 3) with tableresults
If I create #mystuff from another session I can see in a dbcc page view from my own session:
Slot 0 Offset 0x60 Length 18 Slot 0 Column 1 Offset 0xb Length 7 Length (physical) 7 myFieldName MyValue
If you prefix a temporary table name with two octothorpes, e.g. ##mystuff, it creates a global temporary table that exists outside session scope.
That does require you to be able to alter the query text, which may or may not be accessible in this specific case.
Based on Alex K's answer I wrote up a script. I didn't actually end up with all the rows from the table, so there's likely room for improvement.
Disclaimer: As with any code you find online... understand it before you run it and use at your own risk. Ends with DBCC TRACEOFF(3604).
/* Run this first to get your object_id. In the case of > 1, choose one based on date and rowcount. */
DECLARE #tableName NVARCHAR(100) = '#conv'
SELECT #tableName TableName,
o.object_id,
SUM(p.rows) TableRows,
o.Create_Date CreateDatetime,
o.name TempDBFullName
FROM tempdb.sys.objects o
LEFT JOIN tempdb.sys.partitions p ON o.object_id = p.object_id
AND p.index_id <= 1
WHERE o.name LIKE #tableName+'\_\_\_%' ESCAPE '\'
GROUP BY o.name, o.object_id,o.Create_Date
--(-1074969413)
GO
/* Run this using the object_id from above to get table contents */
DECLARE
#object_id INT = -1074969413, --from above
#tableName NVARCHAR(100),
#rows BIGINT,
#created DATETIME,
#msg NVARCHAR(MAX),
--Set this to NULL for all pages
#max_pages INT = 2 --set this to restrict how many pages are read. Must be > 1.
IF #max_pages < 2
RAISERROR('You''re going to need some more pages there, Sport. Try at least 2.',16,1)
RAISERROR('
**** This code uses an undocumented feature and is for informational purposes only. Do not assume results are complete or correct. ****
',0,1)
SET NOCOUNT ON
--declare #tablename nvarchar(100), #msg nvarchar(max), #rows bigint, #created datetime
SELECT #rows = SUM(rows) FROM tempdb.sys.partitions WHERE object_id = #object_id AND index_id <= 1
SELECT #rows = ISNULL(#rows,0)
SELECT #tableName = SUBSTRING(o.name,1,CHARINDEX('____',o.name,1)-1),
#created = o.create_date
FROM tempdb.sys.objects o
WHERE o.object_id = #object_id
SELECT #msg = 'Object name '+QUOTENAME(#tableName)+' is expected to contain up to '+CAST(#rows AS NVARCHAR(20))+' rows and was created # '+CONVERT(NVARCHAR,#created,113)+'.'
RAISERROR(#msg,0,1) WITH NOWAIT
DROP TABLE IF EXISTS #dbccind
CREATE TABLE #dbccind(PageFID NVARCHAR(100),PagePID NVARCHAR(100),IAMFID NVARCHAR(100),IAMPID NVARCHAR(100),ObjectID NVARCHAR(100),IndexID NVARCHAR(100),PartitionNumber NVARCHAR(100),PartitionID NVARCHAR(100),iam_chain_Type NVARCHAR(100),PageType NVARCHAR(100),IndexLevel NVARCHAR(100),NextPageFID NVARCHAR(100),NextPagePID NVARCHAR(100),PrevPageFID NVARCHAR(100),PrevPagePID NVARCHAR(100))
DECLARE #SQL NVARCHAR(MAX) = N'dbcc ind(''tempdb'', '+CAST(#object_id AS NVARCHAR(20))+', -1) WITH NO_INFOMSGS'
--Get a list of pages for this object
INSERT #dbccind
EXEC sp_executesql #SQL
--add an iterative counter for following loop
ALTER TABLE #dbccind ADD ID INT IDENTITY NOT NULL
--select '#dbccind' [#dbccind], * FROM #dbccind
--DECLARE #SQL nvarchar(max), #msg nvarchar(max)
DROP TABLE IF EXISTS #TempTableUnpivoted
CREATE TABLE #TempTableUnpivoted(ParentObject NVARCHAR(100), Object NVARCHAR(100), Field NVARCHAR(100), Value NVARCHAR(1000))
DBCC TRACEON(3604) WITH NO_INFOMSGS;
--Use DBCC PAGE to dump TEMPDB pages to a table as name/value pairs
IF #max_pages IS NOT NULL
BEGIN
SELECT #msg = 'Max pages set. This process will read (at most) '+CAST(#max_pages AS NVARCHAR(20))+' data page(s).'
RAISERROR(#msg,0,1) WITH NOWAIT
END
DECLARE #i INT = 1, #j INT = (SELECT MAX(id) FROM #dbccind)
WHILE #i <= #j
AND (#i <= #max_pages OR #max_pages IS NULL)
BEGIN
SELECT #SQL = 'INSERT #TempTableUnpivoted EXEC SP_EXECUTESQL N''DBCC PAGE(TEMPDB, '+CAST(PageFID AS NVARCHAR(20))+', '+CAST(PagePID AS NVARCHAR(20))+', 3) WITH TABLERESULTS, NO_INFOMSGS'''
FROM #dbccind
WHERE id = #i
SELECT #msg = 'Reading page '+CAST(#i AS NVARCHAR(20))+' of '+CAST(#j AS NVARCHAR(20))+' # '+CONVERT(NVARCHAR,GETDATE(),113)
RAISERROR(#msg,0,1) WITH NOWAIT
--raiserror(#sql,0,1) with nowait
IF #SQL IS NULL RAISERROR('Unable to read pages.',16,1) WITH NOWAIT
EXEC SP_EXECUTESQL #SQL
SELECT #i += 1--, #SQL = NULL, #msg = NULL
END
IF #max_pages <= #i
BEGIN
SELECT #msg = 'Max pages reached. This process has read data from up to '+CAST(#max_pages AS NVARCHAR(20))+' data page(s).'
RAISERROR(#msg,0,1) WITH NOWAIT
END
--SELECT '#TempTableUnpivoted' [#TempTableUnpivoted], * FROM #TempTableUnpivoted
--get a list of fields from the page results. Assume all occur withing the first 1000 results.
SELECT #msg = 'Identify fields # '+CONVERT(NVARCHAR,GETDATE(),113)
RAISERROR(#msg,0,1) WITH NOWAIT
DROP TABLE IF EXISTS #TableFields
CREATE TABLE #TableFields(FieldName NVARCHAR(100) NOT NULL PRIMARY KEY, ColumnId INT NOT NULL)
INSERT #TableFields(FieldName,ColumnId)
SELECT DISTINCT Field,ColumnId
FROM (SELECT TOP 1000 *, SUBSTRING(Object,CHARINDEX('Column ',Object,1)+7,CHARINDEX(' Offset ',Object,1)-CHARINDEX('Column ',Object,1)-7) ColumnId
FROM #TempTableUnpivoted
WHERE ParentObject LIKE 'Slot %'
AND Object LIKE 'Slot%Column%offset%length%') a
--Set up variables to hold a list of column names
SELECT #msg = 'Compile list of field names # '+CONVERT(NVARCHAR,GETDATE(),113)
RAISERROR(#msg,0,1) WITH NOWAIT
--declare #sql nvarchar(max)
DECLARE #columnlist NVARCHAR(MAX) = (SELECT STRING_AGG(QUOTENAME(FieldName,''''),',') WITHIN GROUP ( ORDER BY ColumnId) FROM #TableFields)
DECLARE #columnlistquoted NVARCHAR(MAX) = (SELECT STRING_AGG(QUOTENAME(FieldName),',') WITHIN GROUP ( ORDER BY ColumnId) FROM #TableFields)
DECLARE #columnlistTyped NVARCHAR(MAX) = (SELECT STRING_AGG(QUOTENAME(FieldName),' nvarchar(1000),') WITHIN GROUP ( ORDER BY ColumnId) FROM #TableFields)+' nvarchar(1000)'
IF #columnlist IS NULL OR #columnlistquoted IS NULL OR #columnlistTyped IS NULL
BEGIN
SELECT #columnlist [#columnlist], #columnlistquoted [#columnlistquoted], #columnlistTyped [#columnlistTyped]
RAISERROR('Unable to compile columns. You might need to read more pages to get a solid list. Try incrementing #max_pages or clear #max_pages to do a full read.',16,1) WITH NOWAIT
END
--Create a list of unpivoted name/value pairs (NVP) for just the columns we need. This _may_ be able to be cut out with implicit restrictions in the pivot.
SELECT #msg = 'Reduce unpivoted data to name/value pairs # '+CONVERT(NVARCHAR,GETDATE(),113)
RAISERROR(#msg,0,1) WITH NOWAIT
DROP TABLE IF EXISTS #TempTableNVP
CREATE TABLE #TempTableNVP(ParentObject NVARCHAR(100),Field NVARCHAR(100),Value NVARCHAR(1000))
SELECT #SQL = N'
SELECT ParentObject,Field,Value
FROM #TempTableUnpivoted
WHERE Field in ('+#columnlist+')'
INSERT #TempTableNVP(ParentObject,Field,Value)
EXEC sp_executesql #SQL
--Pivot data to final form to match temp table
SELECT #msg = 'Add fields to working table 1 # '+CONVERT(NVARCHAR,GETDATE(),113)
RAISERROR(#msg,0,1) WITH NOWAIT
DROP TABLE IF EXISTS #TempTable
CREATE TABLE #TempTable(id INT IDENTITY NOT NULL)
SELECT #SQL = 'ALTER TABLE #TempTable ADD '+#columnlistTyped
IF #SQL IS NULL
RAISERROR('Unable to alter working table 1.',16,1) WITH NOWAIT
--raiserror(#sql,0,1) with nowait
EXEC sp_executesql #SQL
--select '#TempTable' [#TempTable], * from #TempTable
SELECT #msg = 'Pivot data to get original table # '+CONVERT(NVARCHAR,GETDATE(),113)
RAISERROR(#msg,0,1) WITH NOWAIT
SELECT #SQL = 'INSERT #TempTable('+#columnlistquoted+')
SELECT '+#columnlistquoted+'
FROM (SELECT Field,Value,ParentObject FROM #TempTableNVP) a
PIVOT (MIN(Value) FOR Field IN ('+#columnlistquoted+')) PVT'
--raiserror(#sql,0,1) with nowait
IF #SQL IS NULL RAISERROR('Unable to populate working table 1.',16,1) WITH NOWAIT
EXEC sp_executesql #SQL
--Return results
SELECT #msg = 'Return results # '+CONVERT(NVARCHAR,GETDATE(),113)
RAISERROR(#msg,0,1) WITH NOWAIT
--SELECT * FROM #TempTable
SELECT #SQL = 'SELECT '+#columnlistquoted+' FROM #TempTable'
EXEC sp_executesql #SQL
DBCC TRACEOFF(3604) WITH NO_INFOMSGS;

Dynamic SQL results into temp table in SQL Stored procedure

The code is as follows:
ALTER PROCEDURE dbo.pdpd_DynamicCall
#SQLString varchar(4096) = null
AS
Begin
create TABLE #T1 ( column_1 varchar(10) , column_2 varchar(100) )
insert into #T1
execute ('execute ' + #SQLString )
select * from #T1
End
The problem is that I want to call different procedures that can give back different columns.
Therefore I would have to define the table #T1 generically.
But I don't know how.
Can anyone help me on this problem?
Try:
SELECT into #T1 execute ('execute ' + #SQLString )
And this smells real bad like an sql injection vulnerability.
correction (per #CarpeDiem's comment):
INSERT into #T1 execute ('execute ' + #SQLString )
also, omit the 'execute' if the sql string is something other than a procedure
You can define a table dynamically just as you are inserting into it dynamically, but the problem is with the scope of temp tables. For example, this code:
DECLARE #sql varchar(max)
SET #sql = 'CREATE TABLE #T1 (Col1 varchar(20))'
EXEC(#sql)
INSERT INTO #T1 (Col1) VALUES ('This will not work.')
SELECT * FROM #T1
will return with the error "Invalid object name '#T1'." This is because the temp table #T1 is created at a "lower level" than the block of executing code. In order to fix, use a global temp table:
DECLARE #sql varchar(max)
SET #sql = 'CREATE TABLE ##T1 (Col1 varchar(20))'
EXEC(#sql)
INSERT INTO ##T1 (Col1) VALUES ('This will work.')
SELECT * FROM ##T1
Hope this helps,
Jesse
Be careful of a global temp table solution as this may fail if two users use the same routine at the same time as a global temp table can be seen by all users...
create a global temp table with a GUID in the name dynamically. Then you can work with it in your code, via dyn sql, without worry that another process calling same sproc will use it. This is useful when you dont know what to expect from the underlying selected table each time it runs so you cannot created a temp table explicitly beforehand. ie - you need to use SELECT * INTO syntax
DECLARE #TmpGlobalTable varchar(255) = 'SomeText_' + convert(varchar(36),NEWID())
-- select #TmpGlobalTable
-- build query
SET #Sql =
'SELECT * INTO [##' + #TmpGlobalTable + '] FROM SomeTable'
EXEC (#Sql)
EXEC ('SELECT * FROM [##' + #TmpGlobalTable + '] ')
EXEC ('DROP TABLE [##' + #TmpGlobalTable + ']')
PRINT 'Dropped Table ' + #TmpGlobalTable
INSERT INTO #TempTable
EXEC(#SelectStatement)
Try Below code for creating temp table dynamically from Stored Procedure Output using T-SQL
declare #ExecutionName varchar(1000) = 'exec [spname] param1,param2 '
declare #sqlStr varchar(max) = ''
declare #tempTableDef nvarchar(max) =
(
SELECT distinct
STUFF(
(
SELECT ','+a.[name]+' '+[system_type_name]
+'
' AS [text()]
FROM sys.dm_exec_describe_first_result_set (#ExecutionName, null, 0) a
ORDER BY a.column_ordinal
FOR XML PATH ('')
), 1, 1, '') tempTableDef
FROM sys.dm_exec_describe_first_result_set (#ExecutionName, null, 0) b
)
IF ISNULL(#tempTableDef ,'') = '' RAISERROR( 'Invalid SP Configuration. At least one column is required in Select list of SP output.',16,1) ;
set #tempTableDef='CREATE TABLE #ResultDef
(
' + REPLACE(#tempTableDef,'
','') +'
)
INSERT INTO #ResultDef
' + #ExecutionName
Select #sqlStr = #tempTableDef +' Select * from #ResultDef '
exec(#sqlStr)
DECLARE #EmpGroup INT =3 ,
#IsActive BIT=1
DECLARE #tblEmpMaster AS TABLE
(EmpCode VARCHAR(20),EmpName VARCHAR(50),EmpAddress VARCHAR(500))
INSERT INTO #tblEmpMaster EXECUTE SPGetEmpList #EmpGroup,#IsActive
SELECT * FROM #tblEmpMaster
CREATE PROCEDURE dbo.pdpd_DynamicCall
AS
DECLARE #SQLString_2 NVARCHAR(4000)
SET NOCOUNT ON
Begin
--- Create global temp table
CREATE TABLE ##T1 ( column_1 varchar(10) , column_2 varchar(100) )
SELECT #SQLString_2 = 'INSERT INTO ##T1( column_1, column_2) SELECT column_1 = "123", column_2 = "MUHAMMAD IMRON"'
SELECT #SQLString_2 = REPLACE(#SQLString_2, '"', '''')
EXEC SP_EXECUTESQL #SQLString_2
--- Test Display records
SELECT * FROM ##T1
--- Drop global temp table
IF OBJECT_ID('tempdb..##T1','u') IS NOT NULL
DROP TABLE ##T1
End
Not sure if I understand well, but maybe you could form the CREATE statement inside a string, then execute that String? That way you could add as many columns as you want.

Resources