How to create a temp table from a dynamic list - sql

I have a table FieldList (ID int, Title varchar(50)) and want to create a temp table with a column list for each record in FieldList with the column name = FieldList.Title and the type as varchar.
This all happens in a Stored Proc, and the temp table is returned to the client for reporting and data analysis.
e.g.
FieldList Table:
ID Title
1 City
2 UserSuppliedFieldName
3 SomeField
Resultant Temp table columns:
City UserSuppliedFieldName SomeField

You can use the following proc to do what you are wanting. It just requires that you:
Create the Temp Table before calling the proc (you will pass in the Temp Table name to the proc). This allows the temp table to be used in the current scope as Temp Tables created in Stored Procedures are dropped once that proc ends / returns.
Put just one field in the Temp Table; the datatype is irrelevant as the field will be dropped (you will pass in the field name to the proc)
[Be sure to change the proc name to whatever you like, but the temp proc name is used in the example that follows]
CREATE PROCEDURE #Abracadabra
(
#TempTableName SYSNAME,
#DummyFieldName SYSNAME,
#TestMode BIT = 0
)
AS
SET NOCOUNT ON
DECLARE #SQL NVARCHAR(MAX)
SELECT #SQL = COALESCE(#SQL + N', [',
N'ALTER TABLE ' + #TempTableName + N' ADD [')
+ [Title]
+ N'] VARCHAR(100)'
FROM #FieldList
ORDER BY [ID]
SET #SQL = #SQL
+ N' ; ALTER TABLE '
+ #TempTableName
+ N' DROP COLUMN ['
+ #DummyFieldName
+ N'] ; '
IF (#TestMode = 0)
BEGIN
EXEC(#SQL)
END
ELSE
BEGIN
PRINT #SQL
END
GO
The following example shows the proc in use. The first execution is in Test Mode that simply prints the SQL that will be executed. The second execution runs the SQL and the SELECT following that EXEC shows that the fields are what was in the FieldList table.
/*
-- HIGHLIGHT FROM "SET" THROUGH FINAL "INSERT" AND RUN ONCE
-- to setup the example
SET NOCOUNT ON;
--DROP TABLE #FieldList
CREATE TABLE #FieldList (ID INT, Title VARCHAR(50))
INSERT INTO #FieldList (ID, Title) VALUES (1, 'City')
INSERT INTO #FieldList (ID, Title) VALUES (2, 'UserSuppliedFieldName')
INSERT INTO #FieldList (ID, Title) VALUES (3, 'SomeField')
*/
IF (OBJECT_ID('tempdb.dbo.#Tmp') IS NOT NULL)
BEGIN
DROP TABLE #Tmp
END
CREATE TABLE #Tmp (Dummy INT)
EXEC #Abracadabra
#TempTableName = N'#Tmp',
#DummyFieldName = N'Dummy',
#TestMode = 1
-- look in "Messages" tab
EXEC #Abracadabra
#TempTableName = N'#Tmp',
#DummyFieldName = N'Dummy',
#TestMode = 0
SELECT * FROM #Tmp
Output from #TestMode = 1:
ALTER TABLE #Tmp ADD [City] VARCHAR(100), [UserSuppliedFieldName]
VARCHAR(100), [SomeField] VARCHAR(100) ; ALTER TABLE #Tmp DROP COLUMN
[Dummy] ;

Create this function and pass to it the list you require. It will generate a table for you temporarily that you can use in real-time with any other SQL query. I've provided an example as well.
CREATE FUNCTION [dbo].[fnMakeTableFromList]
(#List varchar(MAX), #Delimiter varchar(255))
RETURNS table
AS
RETURN (SELECT Item = CONVERT(VARCHAR, Item)
FROM (SELECT Item = x.i.value('(./text())[1]', 'varchar(max)')
FROM (SELECT [XML] = CONVERT(XML, '<i>' + REPLACE(#List, #Delimiter, '</i><i>') + '</i>').query('.')) AS a
CROSS APPLY [XML].nodes('i') AS x(i)) AS y
WHERE Item IS NOT NULL);
GO
And you can use it like this ...
Parm1 = a list in a string seperated by a delimiter
Parm2 = the delimiter character
SELECT *
FROM fnMakeTableFromList('a,b,c,d,e',',')
Result is a table ...
a
b
c
d
e

With previously created table "FieldList" with fields "ID" and "Title"...
step 1: create table. The field "First" is there just to have one field, we'll delete it at the end
create table #SOE (First varchar(50))
step 2: right click and select "Result To - > Result to text", then run query:
select 'alter table #SOE add ' + Title + ' varchar(50)' from dbo.FieldList
step 3: copy-paste result and execute it
step 4: delete first field
alter table #SOE drop column First
step 5: here is your table
select * from #SOE

Related

Reorder columns in final stored procedure SELECT / OUTPUT

I need to reorder columns in the final SELECT statement in a stored procedure. Column orders needs to be fetched from another table.
I have a solution based on dynamic SQL. Is there any better way to do it? I have around 100 columns to return with millions of rows for an Excel export. Is there any other performance optimized solution other than a dynamic query?
Please find sample code below for my current solution.
IF OBJECT_ID( 'tempdb..#TempColumns') IS NOT NULL
BEGIN
DROP TABLE #TempColumns
END
IF OBJECT_ID( 'tempdb..#TempColumnsOrder') IS NOT NULL
BEGIN
DROP TABLE #TempColumnsOrder
END
CREATE TABLE #TempColumns
(
ID INT IDENTITY,
FirstName VARCHAR(MAX),
LastName VARCHAR(MAX),
Gender VARCHAR(MAX)
)
INSERT INTO #TempColumns
VALUES ('ABC', 'DEF', 'MALE'), ('PR', 'ZA', 'FEMALE'), ('ERT', 'GFG', 'MALE')
CREATE TABLE #TempColumnsOrder
(
ID INT IDENTITY,
ColumnName VARCHAR(MAX),
ColumnOrder INT
)
INSERT INTO #TempColumnsOrder
VALUES ('FirstName', 3), ('LastName', 2), ('Gender', 1)
SELECT * FROM #TempColumns
SELECT * FROM #TempColumnsOrder
DECLARE #script VARCHAR(MAX)
SELECT #script = 'SELECT '
SELECT #script = #script + ColumnName + ','
FROM #TempColumnsOrder
ORDER BY ColumnOrder
PRINT #script
SELECT #script = SUBSTRING(RTRIM(#script), 1, LEN(RTRIM(#script)) - 1)
SELECT #script = #script + ' FROM #TempColumns'
EXEC (#script)
IF OBJECT_ID( 'tempdb..#TempColumns') IS NOT NULL
BEGIN
DROP TABLE #TempColumns
END
IF OBJECT_ID( 'tempdb..#TempColumnsOrder') IS NOT NULL
BEGIN
DROP TABLE #TempColumnsOrder
END
Thanks for reply, Is there any better way in Dynamic SQL other than what i did?
You can eliminate the unsupported string concatenation you are using, and modernize and simply the code:
DROP TABLE IF EXISTS #TempColumns
DROP TABLE IF EXISTS #TempColumnsOrder
CREATE TABLE #TempColumns
(
ID INT IDENTITY,
FirstName VARCHAR(MAX),
LastName VARCHAR(MAX),
Gender VARCHAR(MAX)
)
INSERT INTO #TempColumns
Values('ABC','DEF','MALE'),('PR','ZA','FEMALE'),('ERT','GFG','MALE')
CREATE TABLE #TempColumnsOrder
(
ID INT IDENTITY,
ColumnName VARCHAR(MAX),
ColumnOrder INT
)
INSERT INTO #TempColumnsOrder
Values('FirstName',3), ('LastName',2), ('Gender',1)
SELECT * FROM #TempColumns
SELECT * FROM #TempColumnsOrder
DECLARE #script VARCHAR(MAX) = concat(
'SELECT ',
(select STRING_AGG(QUOTENAME(ColumnName),', ') WITHIN GROUP (ORDER BY ColumnOrder)
FROM #TempColumnsOrder),
' FROM #TempColumns')
print #script
EXEC (#script)
DROP TABLE IF EXISTS #TempColumns
DROP TABLE IF EXISTS #TempColumnsOrder
SELECT #script = #script + ColumnName + ',' FROM #TempColumnsOrder
ORDER BY ColumnOrder
The behavior of aggregate string concatenation with the above technique is not guaranteed. The actual behavior is plan-dependent so you may not get the desired results.
In SQL Server 2017 and Azure SQL Database, STRING_AGG is the proper method:
SELECT STRING_AGG(ColumnName, ',') WITHIN GROUP(ORDER BY ColumnOrder)
FROM #TempColumnsOrder;
In older SQL Versions like SQL Server 2012, the best method is with XML PATH():
SELECT #script = #script +
STUFF((SELECT ',' + ColumnName
FROM #TempColumnsOrder
ORDER BY ColumnOrder
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'),1,1,'');
See this answer for details about how the above query works.

Dynamic SQL Result INTO Temporary Table

I need to store dynamic sql result into a temporary table #Temp.
Dynamic SQL Query result is from a pivot result, so number of columns varies(Not fixed).
SET #Sql = N'SELECT ' + #Cols + ' FROM
(
SELECT ResourceKey, ResourceValue
FROM LocaleStringResources where StateId ='
+ LTRIM(RTRIM(#StateID)) + ' AND FormId =' + LTRIM(RTRIM(#FormID))
+ ' AND CultureCode =''' + LTRIM(RTRIM(#CultureCode)) + '''
) x
pivot
(
max(ResourceValue)
for ResourceKey IN (' + #Cols + ')
) p ;'
--#Cols => Column Names which varies in number
Now I have to insert dynamic sql result to #Temp Table and use this #Temp Table with another existing table to perform joins or something else.
(#Temp table should exist there to perform operations with other existing tables)
How can I Insert dynamic SQL query result To a Temporary table?
Thanks
Can you please try the below query.
SET #Sql = N'SELECT ' + #Cols + '
into ##TempTable
FROM
(
SELECT ResourceKey, ResourceValue
FROM LocaleStringResources where StateId ='
+ LTRIM(RTRIM(#StateID)) + ' AND FormId =' + LTRIM(RTRIM(#FormID))
+ ' AND CultureCode =''' + LTRIM(RTRIM(#CultureCode)) + '''
) x
pivot
(
max(ResourceValue)
for ResourceKey IN (' + #Cols + ')
) p ;'
You can then use the ##TempTable for further operations.
However, do not forget to drop the ##TempTable at the end of your query as it will give you error if you run the query again as it is a Global Temporary Table
As was answered in (https://social.msdn.microsoft.com/Forums/sqlserver/en-US/144f0812-b3a2-4197-91bc-f1515e7de4b9/not-able-to-create-hash-table-inside-stored-proc-through-execute-spexecutesql-strquery?forum=sqldatabaseengine),
you need to create a #Temp table in advance:
CREATE TABLE #Temp(columns definition);
It seems that the task is impossible, if you know nothing about the dynamic list of columns in advance. But, most likely you do know something.
You do know the types of dynamic columns, because they come from PIVOT. Most likely, you know the maximum possible number of dynamic columns. Even if you don't, SQL Server has a limit of 1024 columns per (nonwide) table and there is a limit of 8060 bytes per row (http://msdn.microsoft.com/en-us/library/ms143432.aspx). So, you can create a #Temp table in advance with maximum possible number of columns and use only some of them (make all your columns NULLable).
So, CREATE TABLE will look like this (instead of int use your type):
CREATE TABLE #Temp(c1 int NULL, c2 int NULL, c3 int NULL, ..., c1024 int NULL);
Yes, column names in #Temp will not be the same as in #Cols. It should be OK for your processing.
You have a list of columns in your #Cols variable. You somehow make this list of columns in some external code, so when #Cols is generated you know how many columns there are. At this moment you should be able to generate a second list of columns that matches the definition of #Temp. Something like:
#TempCols = N'c1, c2, c3, c4, c5';
The number of columns in #TempCols should be the same as the number of columns in #Cols. Then your dynamic SQL would look like this (I have added INSERT INTO #Temp (#TempCols) in front of your code):
SET #Sql = N'INSERT INTO #Temp (' + #TempCols + N') SELECT ' + #Cols + N' FROM
(
SELECT ResourceKey, ResourceValue
FROM LocaleStringResources where StateId ='
+ LTRIM(RTRIM(#StateID)) + ' AND FormId =' + LTRIM(RTRIM(#FormID))
+ ' AND CultureCode =''' + LTRIM(RTRIM(#CultureCode)) + '''
) x
pivot
(
max(ResourceValue)
for ResourceKey IN (' + #Cols + ')
) p ;'
Then you execute your dynamic SQL:
EXEC (#Sql) OR sp_executesql #Sql
And then do other processing using the #Temp table and temp column names c1, c2, c3, ...
MSDN says:
A local temporary table created in a stored procedure is dropped
automatically when the stored procedure is finished.
You can also DROP the #Temp table explicitly, like this:
IF OBJECT_ID('tempdb..#Temp') IS NOT NULL
DROP TABLE #Temp'
All this T-SQL code (CREATE TABLE, EXEC, ...your custom processing..., DROP TABLE) would naturally be inside the stored procedure.
Alternative to create a temporary table is to use the subquery
select t1.name,t1.lastname from(select * from table)t1.
where "select * from table" is your dyanmic query. which will return result which you can use as temp table t1 as given in example .
IF OBJECT_ID('tempdb..##TmepTable') IS NOT NULL DROP TABLE ##TmepTable
CREATE TABLE ##TmepTable (TmpCol CHAR(1))
DECLARE #SQL NVARCHAR(max) =' IF OBJECT_ID(''tempdb..##TmepTable'') IS NOT
NULL DROP TABLE ##TmepTable
SELECT * INTO ##TmepTable from [MyTableName]'
EXEC sp_executesql #SQL
SELECT Alias.* FROM ##TmepTable as Alias
IF OBJECT_ID('tempdb..##TmepTable') IS NOT NULL DROP TABLE ##TmepTable
Here is step by step solution for your problem.
Check for your temporary tables if they exist, and delete them.
IF OBJECT_ID('tempdb..#temp') IS NOT NULL
DROP TABLE #temp
IF OBJECT_ID('tempdb..##abc') IS NOT NULL
DROP TABLE ##abc
Store your main query result in first temp table (this step is for simplicity and more readability).
SELECT *
INTO #temp
FROM (SELECT ResourceKey, ResourceValue
FROM LocaleStringResources
where StateId ='+ LTRIM(RTRIM(#StateID)) + ' AND FormId =' + LTRIM(RTRIM(#FormID))
+ ' AND CultureCode =' + LTRIM(RTRIM(#CultureCode)) + ') AS S
Write below query to create your pivot and store result in another temp table.
DECLARE #str NVARCHAR(1000)
DECLARE #sql NVARCHAR(1000)
SELECT #str = COALESCE(#str+',', '') + ResourceKey FROM #temp
SET #sql = N'select * into ##abc from (select ' + #str + ' from (SELECT ResourceKey, ResourceValue FROM #temp) as A
Pivot
(
max(ResourceValue)
for ResourceKey in (' + #str + ')
)as pvt) as B'
Execute below query to get the pivot result in your next temp table ##abc.
EXECUTE sp_executesql #sql
And now you can use ##abc as table where-ever you want like
select * from ##abc
Hope this will help you.

Getting a temporary table returned from dynamic SQL in SQL Server 2005, and parsing

So I was requested to make a few things.... (it is Monday morning and for some reason this whole thing is turning out to be really hard for me to explain so I am just going to try and post a lot of my code; sorry) Oh - the table idea has to stay. Anything else can be changed but the idea of this table and the parsed field was not my idea but it is my responsibility to execute and make work.
Edit: Sorry the post is long. I do suggest a few possible solutions throughout but my problem is ultimately how everything is returned in a dynamically defined table/table variable from something (sp,view,function,anything...). Please take a minute to read the whole post...
First, I needed a table:
CREATE TABLE TICKET_INFORMATION (
TICKET_INFO_ID INT IDENTITY(1,1) NOT NULL,
TICKET_TYPE INT,
TARGET_ID INT,
TARGET_NAME VARCHAR(100),
INFORMATION VARCHAR(MAX),
TIME_STAMP DATETIME DEFAULT GETUTCDATE()
)
-- insert this row for testing...
INSERT INTO TICKET_INFORMATION (TICKET_TYPE, TARGET_ID, TARGET_NAME, INFORMATION) VALUES (1,1,'RT_ID','IF_ID,int=1&IF_ID,int=2&OTHER,varchar(10)=val,ue3&OTHER,varchar(10)=val,ue4')
The Information column holds data that needs to be parsed into a table. This is where I am having problems. In the resulting table, Target_Name needs to become a column that holds Target_ID as a value for each row in the resulting table.
The string that needs to be parsed is in this format:
#var_name1,#var_datatype1=#var_value1&#var_name2,#var_datatype2=#var_value2&#var_name3,#var_datatype3=#var_value3
And what I ultimately need as a result (in a table or table variable):
RT_ID IF_ID OTHER
1 1 val,ue3
1 2 val,ue3
1 1 val,ue4
1 2 val,ue4
And I need to be able to join on the result. Initially, I was just going to make this a function that returns a table variable but for some reason I can't figure out how to get it into an actual table variable. Whatever parses the string needs to be able to be used directly in queries so I don't think a stored procedure is really the right thing to be using.
This is the code that parses the Information string... it returns in a temporary table. In the end, this needs to be passed either just the information string or an ID number or something... doesn't matter what. Or somehow it can be in a view with the ticket_information table.
-- create/empty temp table for var_name, var_type and var_value fields
if OBJECT_ID('tempdb..#temp') is not null drop table #temp
create table #temp (row int identity(1,1), var_name varchar(max), var_type varchar(30), var_value varchar(max))
-- just setting stuff up
declare #target_name varchar(max), #target_id varchar(max), #info varchar(max)
set #target_name = (select target_name from ticket_information where ticket_info_id = 1)
set #target_id = (select target_id from ticket_information where ticket_info_id = 1)
set #info = (select information from ticket_information where ticket_info_id = 1)
--print #info
-- some of these variables are re-used later
declare #col_type varchar(20), #query varchar(max), #select as varchar(max)
set #query = 'select ' + #target_id + ' as ' + #target_name + ' into #target; '
set #select = 'select * into ##global_temp from #target'
declare #var_name varchar(100), #var_type varchar(100), #var_value varchar(100)
declare #comma_pos int, #equal_pos int, #amp_pos int
set #comma_pos = 1
set #equal_pos = 1
set #amp_pos = 0
-- while loop to parse the string into a table
while #amp_pos < len(#info) begin
-- get new comma position
set #comma_pos = charindex(',',#info,#amp_pos+1)
-- get new equal position
set #equal_pos = charindex('=',#info,#amp_pos+1)
-- set stuff that is going into the table
set #var_name = substring(#info,#amp_pos+1,#comma_pos-#amp_pos-1)
set #var_type = substring(#info,#comma_pos+1,#equal_pos-#comma_pos-1)
-- get new ampersand position
set #amp_pos = charindex('&',#info,#amp_pos+1)
if #amp_pos=0 or #amp_pos<#equal_pos set #amp_pos = len(#info)+1
-- set last variable for insert into table
set #var_value = substring(#info,#equal_pos+1,#amp_pos-#equal_pos-1)
-- put stuff into the temp table
insert into #temp (var_name, var_type, var_value) values (#var_name, #var_type, #var_value)
-- is this a new field?
if ((select count(*) from #temp where var_name = (#var_name)) = 1) begin
set #query = #query + ' create table #' + #var_name + '_temp (' + #var_name + ' ' + #var_type + '); '
set #select = #select + ', #' + #var_name + '_temp '
end
set #query = #query + ' insert into #' + #var_name + '_temp values (''' + #var_value + '''); '
end
if OBJECT_ID('tempdb..##global_temp') is not null drop table ##global_temp
exec (#query + #select)
--select #query
--select #select
select * from ##global_temp
Okay. So, the result I want and need is now in ##global_temp. How do I put all of that into something that can be returned from a function (or something)? Or can I get something more useful returned from the exec statement? In the end, the results of the parsed string need to be in a table that can be joined on and used... Ideally this would have been a view but I guess it can't with all the processing that needs to be done on that information string.
Ideas?
Thanks!
Edit: Still looking for answers to this. I would love to have a function that returns a table variable but I don't know how to get the results into a table variable. The result is currently in a global temporary table. Would it work if I defined my table variable in the dynamic portion of the code and then it would just magically be there to return? Or can I somehow select into a table variable from my global temp table, without first defining the columns of the table variable? Or can I create the table variable when I execute the dynamic part? The whole problem is because the columns of the end result are dynamic....... so..... I'm not sure how I could clarify the issues I'm having more. If a function that returns a table is a good route to go - could someone please provide me with code or a link as an example for returning a table variable with a dynamic column definition from a function? Plz, thnx.
You could use a table valued function. This would allow you to return the results as a table to be joined to just like you asked for.
CREATE FUNCTION dbo.GET_TICKET_INFORMATION (... some parameters... )
RETURNS #TICKET_INFORMATION TABLE
(
TICKET_INFO_ID INT IDENTITY(1,1) NOT NULL,
TICKET_TYPE INT,
TARGET_ID INT,
TARGET_NAME VARCHAR(100),
INFORMATION VARCHAR(MAX),
TIME_STAMP DATETIME DEFAULT GETUTCDATE()
)
AS
...
I used stored procedure for returning a table with a dynamic column definition
i generated dynamic name for global table:
declare #sp varchar(3)
set #sp = cast( ##spid as varchar(3) )
if object_id ( N'tempdb.dbo.#Periods' ) is not null
drop table #Periods
if object_id ( N'tempdb.dbo.##Result' + #sp ) is not null
execute ( 'drop table ##Result' + #sp )
i have sp for return periods table:
create table #Periods
(
[PERIOD_NUM] int
,[START_DATE] datetime
,[END_DATE] datetime
)
insert into #Periods
exec GET_PERIODS_TABLE_SP #pFromDate, #pToDate, #pPeriodType, 0
some fields in result table are dynamic:
select #PeriodCount = ...
declare #PeriodsScript varchar(max)
set #PeriodsScript = ''
set #i = 1
while #i <= #PeriodCount
begin
set #PeriodsScript = #PeriodsScript + ',PERIOD' + cast (#i as varchar(3))
set #i = #i + 1
end
generated and inserted data into ##Result:
declare #script varchar(max)
set #script = 'create table ##Result' + #sp +
'(ROW_NUM int'+
',BRANCH_ID int' +
',PARAM_NAME varchar(25)' +
#PeriodsScript + ')'
execute ( #script )
execute(
'insert into ##Result' + #sp + '( ROW_NUM, BRANCH_ID, NOM_SIZE_ID, PARAM_NAME )' +
'select ( row_number() over( order by BRANCH_ID, NOM_SIZE_ID ) - 1 ) * 3 + 1' +
' ,BRANCH_ID' +
' ,NOM_SIZE_ID' +
' ,''Min.price''' +
' from ( ' +
' select distinct BRANCH_ID' +
' ,NOM_SIZE_ID' +
' from ##ResultNomSizePrices' + #sp +
' ) as t'
)
and finaly, select from result table:
set #script =
'select distinct gb.TINY_NAME'+
' ,r.GROUP_NAME_1 as group1'+
' ,r.GROUP_NAME_2 as group2'+
' ,r.GROUP_NAME_3 as group3'+
' ,r.PARAM_NAME'+
' ,r.ROW_NUM'+
#PeriodsScript +
' from ##Result' + #sp + ' as r'+
' inner join dbo.GD_BRANCHES as gb'+
' on r.BRANCH_ID = gb.BRANCH_ID'+
' order by gb.TINY_NAME'+
' ,r.GROUP_NAME_1'+
' ,r.GROUP_NAME_2'+
' ,r.GROUP_NAME_3'+
' ,r.ROW_NUM'
execute ( #script )
p.s. sry for my english

TSQL select into Temp table from dynamic sql

This seems relatively simple, but apparently it's not.
I need to create a temp table based on an existing table via the select into syntax:
SELECT * INTO #TEMPTABLE FROM EXISTING_TABLE
The problem is, the existing table name is accepted via a parameter...
I can get the table's data via:
execute ('SELECT * FROM ' + #tableName)
but how do I marry the two so that I can put the results from the execute directly into the temp table.
The columns for each table that this is going to be used for are not the same so building the temp table before getting the data is not practical.
I'm open to any suggestions except using a global temp table.
Update:
This is completely ridiculous, BUT my reservations with the global temp table is that this is a multi user platform lends itself to issues if the table will linger for long periods of time...
Sooo.. just to get past this part I've started by using the execute to generate a global temp table.
execute('select * into ##globalDynamicFormTable from ' + #tsFormTable)
I then use the global temp table to load the local temp table:
select * into #tempTable from ##globalDynamicFormTable
I then drop the global table.
drop table ##globalDynamicFormTable
this is dirty and I don't like it, but for the time being, until i get a better solution, its going to have to work.
In the End:
I guess there is no way to get around it.
The best answer appears to be either;
Create a view in the execute command and use that to load the local temp table in the stored procedure.
Create a global temp table in the execute command and use that to load the local temp table.
With that said i'll probably just stick with the global temp table because creating and dropping views is audited in my organization, and I'm sure they are going to question that if it starts happening all the time.
Thanks!
A working example.
DECLARE #TableName AS VARCHAR(100)
SELECT #TableName = 'YourTableName'
EXECUTE ('SELECT * INTO #TEMP FROM ' + #TableName +'; SELECT * FROM #TEMP;')
Second solution with accessible temp table
DECLARE #TableName AS VARCHAR(100)
SELECT #TableName = 'YOUR_TABLE_NAME'
EXECUTE ('CREATE VIEW vTemp AS
SELECT *
FROM ' + #TableName)
SELECT * INTO #TEMP FROM vTemp
--DROP THE VIEW HERE
DROP VIEW vTemp
/*START USING TEMP TABLE
************************/
--EX:
SELECT * FROM #TEMP
--DROP YOUR TEMP TABLE HERE
DROP TABLE #TEMP
declare #sql varchar(100);
declare #tablename as varchar(100);
select #tablename = 'your_table_name';
create table #tmp
(col1 int, col2 int, col3 int);
set #sql = 'select aa, bb, cc from ' + #tablename;
insert into #tmp(col1, col2, col3) exec( #sql );
select * from #tmp;
How I did it with a pivot in dynamic sql (#AccPurch was created prior to this)
DECLARE #sql AS nvarchar(MAX)
declare #Month Nvarchar(1000)
--DROP TABLE #temp
select distinct YYYYMM into #temp from #AccPurch AS ap
SELECT #Month = COALESCE(#Month, '') + '[' + CAST(YYYYMM AS VarChar(8)) + '],' FROM #temp
SELECT #Month= LEFT(#Month,len(#Month)-1)
SET #sql = N'SELECT UserID, '+ #Month + N' into ##final_Donovan_12345 FROM (
Select ap.AccPurch ,
ap.YYYYMM ,
ap.UserID ,
ap.AccountNumber
FROM #AccPurch AS ap
) p
Pivot (SUM(AccPurch) FOR YYYYMM IN ('+#Month+ N')) as pvt'
EXEC sp_executesql #sql
Select * INTO #final From ##final_Donovan_12345
DROP TABLE ##final_Donovan_12345
Select * From #final AS f
DECLARE #count_ser_temp int;
DECLARE #TableName AS VARCHAR(100)
SELECT #TableName = 'TableTemporal'
EXECUTE ('CREATE VIEW vTemp AS
SELECT *
FROM ' + #TableTemporal)
SELECT TOP 1 * INTO #servicios_temp FROM vTemp
DROP VIEW vTemp
-- Contar la cantidad de registros de la tabla temporal
SELECT #count_ser_temp = COUNT(*) FROM #servicios_temp;
-- Recorro los registros de la tabla temporal
WHILE #count_ser_temp > 0
BEGIN
END
END
Take a look at OPENROWSET, and do something like:
SELECT * INTO #TEMPTABLE FROM OPENROWSET('SQLNCLI'
, 'Server=(local)\SQL2008;Trusted_Connection=yes;',
'SELECT * FROM ' + #tableName)

How to use table variable in a dynamic sql statement?

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;**