Dynamic query results into a temp table or table variable - sql-server

I have a stored procedure that uses sp_executesql to generate a result set, the number of columns in the result can vary but will be in the form of Col1 Col2 Col3 etc.
I need to get the result into a temp table or table variable so I can work with it. The problem is I need to define the columns of the temp table, which I cant do dynamically using sp_executesql as the scope of the temp table is lost after the command is executed.
I have toyed with the idea of using Global Temp tables, as the scope allows it to be created dynamically, however, there is a very good chance the Global Temps would get updated by the concurrent executions of this process.
Any ideas?

I have found a solution that works for me with the help of #SQLMenace in this post T-SQL Dynamic SQL and Temp Tables
In short, I need to create a #temp table in normal SQL first, then I can alter the structure using further dynamic SQL statements. In this example #colcount is set to 6. This will be determined by another stored proc when I implement this.
IF object_id('tempdb..#myTemp') IS NOT NULL
DROP TABLE #myTemp
CREATE TABLE #myTemp (id int IDENTITY(1,1) )
DECLARE #cmd nvarchar(max)
DECLARE #colcount int
SET #colcount = 6
DECLARE #counter int
SET #counter = 0
WHILE #counter < #colcount
BEGIN
SET #counter = #counter + 1
SET #cmd = 'ALTER TABLE #myTemp ADD col' + CAST(#counter AS varchar(4)) + ' NVARCHAR(MAX)'
EXEC(#cmd)
END
INSERT INTO #myTemp
EXEC myProc #param1, #param2, #param3
SELECT * FROM #myTemp

IS there any reason you can't do something like:
SELECT *
INTO #MyTempTable
FROM MyResultSet
SELECT INTO doesn't require an explicit field list.

You can use global temp tables whose names are 'uniquified' by the SPID of the creating process. This can allow you to avoid stomping on other global temp tables created by other connections.
Just make sure to clean them up when you're done... :)

Related

Why doesn't this alter after insert statement work?

I have a stored procedure with dynamic sql that i have embedded as below:
delete from #temp_table
begin tran
set #sql = 'select * into #temp_table from sometable'
exec (#sql)
commit tran
begin
set #sql = 'alter table #temp_table add column1 float'
exec(#sql)
end
update #temp_table
set column1 = column1*100
select *
into Primary_Table
from #temp_table
However, I noticed that all the statements work but the alter does not. When run the procedure, I get an error message: "Invalid Column name column1"
What am I doing wrong here?
EDIT: Realized I didn't mention that the first insert is a dynamic sql as well. Updated it.
Alternate approach tried but throws same error:
delete from #temp_table
begin tran
set #sql = 'select * into #temp_table from sometable'
exec (#sql)
commit tran
alter table #temp_table add column1 float
update #temp_table set column1 = column1*100
Local temporary tables exhibit something like dynamic scope. When you create a local temporary table inside a call to exec it goes out of scope and existence on the return from exec.
EXEC (N'create table #x (c int)')
GO
SELECT * FROM #x
Msg 208, Level 16, State 0, Line 4
Invalid object name '#x'.
The select is parsed after the dynamic SQL to create #x is ran. But #x is not there because dropped on exit from exec.
Update
Depending on the situation there are different ways to work around the issue.
Put everything into the same string:
DECLARE #Sql NVARCHAR(MAX) = N'SELECT 1 AS source INTO #table_name;
ALTER TABLE #table_name ADD TARGET float;
UPDATE #table_name SET Target = 100 * source;';
EXEC (#Sql);
Create the table ahead of the dynamic sql that populates it.
CREATE TABLE #table_name (source INT);
EXEC (N'insert into #table_name (source) select 1;');
ALTER TABLE #table_name ADD target FLOAT;
UPDATE #table_name SET target = 100 * source;
In this option, the alter table statement can be removed by adding the additional column to the create table statement.' Note also that the alter table and update statements could be in separate invocations of dynamic SQL, if that was beneficial to your context.
1) It should be ALTER TABLE #temp... Not ALTER #temp.
2) Even if #1 weren't an issue, you're adding column1, as a NULLable column with no default value and, in the next statement setting it's value to itself * 100...
NULL * 100 = NULL
3) Why are you using dynamic sql to alter the #temp table? It can just as easily be done with a regular ALTER TABLE script... or, better yet, can be included in the original table definition.
This is because the #temp_table reference in the outer batch is a different temp table than the one created in dynamic SQL. Consider:
use tempdb
drop table if exists sometable
drop table if exists #temp_table
go
create table sometable(id int, a int)
create table #temp_table(id int, b int)
exec( 'select * into #temp_table from sometable; select * from #temp_table;' )
select * from #temp_table
Outputs
id a
----------- -----------
(0 rows affected)
id b
----------- -----------
(0 rows affected)
A temp table created in a nested batch is scoped to the nested batch and automatically dropped after. A "nested batch" is either a dynamic SQL query or a stored procedure. This behavior is explained here CREATE TABLE, but it only mentions stored procedures. Dynamic SQL behaves the same.
If you create the temp table in a top level batch, you can access it in dynamic SQL, you just can't create a new temp table in dynamic SQL and see it in the outer batch or in subsequent same-level dynamic SQL. So try to use INSERT INTO instead of SELECT INTO.

T-SQL : how to use exec to insert into table not previously created?

I have the following code:
Declare #strSQL varchar(max);
set #strSQL = N'REALLY LONG QUERY';
exec (#strSQL) at <LinkedServerName>;
I did this because the query is longer than 8000 characters (it cannot be changed, it just has too many columns). It works, but I need to insert it into a temporal table that does not yet exist. So, I do not want to run the create table before hand. So, where should I write INTO tmp_table for correct syntax?
For example, this does not work:
exec (#strSQL) INTO tmp_table at <LinkedServerName>;
Declare #strSQL varchar(max);
if object_id('tempdb..MyTempTable') is not null drop table tempdb.dbo.MyTempTable
set #strSQL = N'select * into tempdb.dbo.MyTempTable from (REALLY LONG QUERY) k';
exec (#strSQL) at <LinkedServerName>;
-- for check of existence of table for linkedserver add it into #strSQL
or
replace tempdb.dbo.MyTempTable with temp table ##Table (for local server)
or
decompose and normalize your model, if you have qry with thousands chars you don't need dynamical qry but rethink your solution - for example use views or pivot your output

Use variable to create new table in ms sql

create proc City_Info
#StateRef nvarchar(20)
as
begin
declare #StateCod nvarchar(3);
declare #Check int;
select #StateCod = StateCod from State_Cod where State_Nam = #StateRef
create table C0NCAT(#StateCod' ,'City')(Sno int identity(1,1))
end
Can Anyone tell how can i fetch a Particular Name from Column and Make table using Procedure in mssql?
First of all it looks like classic example of SELECT * FROM sales + #yymm
This is a variation of the previous case, where there is a suite of tables that actually do describe the same entity. All tables have the same columns, and the name includes some partitioning component, typically year and sometimes also month. New tables are created as a new year/month begins.
In this case, writing one stored procedure per table is not really feasible. Not the least, because the user may want to specify a date range for a search, so even with one procedure per table you would still need a dynamic dispatcher.
If you still want to go this way you could use Dynamic-SQL.
create proc City_Info
#StateRef nvarchar(20)
as
begin
declare #StateCod nvarchar(3);
declare #Check int;
select #StateCod = StateCod from State_Cod where State_Nam = #StateRef;
DECLARE #sql NVARCHAR(MAX) =
'create table '
+ QUOTENAME(C0NCAT(#StateCod ,'City'))
+ '(Sno int identity(1,1))';
EXEC sp_executesql #sql
end

sp_msforeachtable performing actions on variables

I am trying to figure out how to use sp_msforeachtable to perform an action on all tables and variables that match variable/table names stored in another table
IE
I have a table that has 3 columns : table, variable, action
and I am trying to use sp_MSforeachtable to see which tables and variables match, and if match, perform that action on the table.
How do you call variable names in the sp_MSforeachtable statement? I know to use ? for the table name, but not sure how I would say if variable name=variable name then do X
Is there another way to do this without using this undocumented SP?
Ill try to explain better:
I am trying to clean personal info from a bunch of tables... I have a table that looks like this (not sure how to format a table, so imagine each entry is a seperate row, so the first row is Name, A, and set to '')
Variable
Name
Phone Number
Name
Table
A
A
B
Action
Set to ''
Set to '555-555-5555'
Set to ''
etc.
I then have a database full of tables....on table A, I would want my code to set all rows of variable 'Name'
to '' (blank)
, and Phone Number to '555-555-5555'
etc.and then move on to table B and do the same and so on
I would use a cursor and dynamic SQL:
--Set up for test:
CREATE TABLE #DataTable (column1 nvarchar(128) NOT NULL, column2 int NOT NULL); --Create global temp table so it can be accessed from dynamic SQL.
CREATE TABLE ##ActionTable ([table] nvarchar(128) NOT NULL, variable nvarchar(MAX) NOT NULL, [action] nvarchar(MAX) NOT NULL);
INSERT INTO ##ActionTable ([table], variable, [action])
VALUES
('#DataTable', '1', 'INSERT INTO #table (column1, column2) VALUES (''#variable_1'', #variable);'),
('#DataTable', '2', 'INSERT INTO #table (column1, column2) VALUES (''#variable_1'', #variable);'),
('#DataTable', '3', 'INSERT INTO #table (column1, column2) VALUES (''#variable_1'', #variable);'),
('#DataTable', '4', 'INSERT INTO #table (column1, column2) VALUES (''#variable_1'', #variable);');
--Code:
DECLARE #action nvarchar(MAX);
DECLARE #table nvarchar(128);
DECLARE #variable nvarchar(MAX);
DECLARE rowCurser CURSOR FOR SELECT [table], variable, [action] FROM ##ActionTable;
OPEN rowCurser;
FETCH rowCurser INTO #table, #variable, #action
WHILE ##FETCH_STATUS = 0
BEGIN
--Execute the code (pick one of the two. Option 2 is safer and can be cached (faster), but it does not work with my example because the parameters are left as variables).
-- Option 1:
SET #action = REPLACE(REPLACE(#action, '#table', #Table), '#variable', #variable);
EXECUTE(#action);
-- Option 2:
EXECUTE sp_executesql #stmt = N'INSERT INTO #DataTable (column1, column2) VALUES (CAST(#variable as nvarchar(128)) + N''_2'', #variable);', #params = N'#variable nvarchar(MAX)', #variable = #variable;
--Setup for next iteration
FETCH rowCurser INTO #table, #variable, #action
END
CLOSE rowCurser;
DEALLOCATE rowCurser;
--Check and cleanup from test
SELECT * FROM #DataTable;
DROP TABLE #DataTable;
DROP TABLE ##ActionTable;
Note: There are security concerns with what you are trying to do, since anyone who can add to your table will have the same access as the account which runs the script. You could reduce these concerns by defining the actions in another table which can only be edited by the administrator, then referencing the action in your existing table.
Note: It is best to have the data types of #action, #table, and #variable match their source columns. The variables an be any data type in your database (as long as it is not a local temp type). You will notice that there are two places in the code above where the types are defined, first where the variables are declared at the top, and second where the arguments for sp_executesql are defined in the string near the bottom.
Note: if #stmt and #params are assigned with a constant instead of a variable, make sure to prefix the constant with N so it will be read as a Unicode string.

SQL Server : update records in dynamically generated tables using parameters in stored procedure

I have to create a stored procedure where I will pass tableName, columnName, id as parameters. The task is to select records from the passed table where columnName has passed id. If record is found update records with some fixed data. Also implement Transaction so that we can rollback in case of any error.
There are hundreds of table in database and each table has different schema that is why I have to pass columnName.
Don't know what is the best approach for this. I am trying select records into a temp table so that I can manipulate it as per requirement but its not working.
I am using this code:
ALTER PROCEDURE [dbo].[GetRecordsFromTable]
#tblName nvarchar(128),
#keyCol varchar(100),
#key int = 0
AS
BEGIN
SET NOCOUNT ON;
BEGIN TRY
--DROP TABLE #TempTable;
DECLARE #sqlQuery nvarchar(4000);
SET #sqlQuery = 'SELECT * FROM ' + #tblName + ' WHERE ' + #keyCol + ' = 2';
PRINT #sqlQuery;
INSERT INTO #TempTable
EXEC sp_executesql #sqlQuery,
N'#keyCol varchar(100), #key int', #keyCol, #key;
SELECT * FROM #TempTable;
END TRY
BEGIN CATCH
EXECUTE [dbo].[uspPrintError];
END CATCH;
END
I get an error
Invalid object name '#TempTable'
Also not sure if this is the best approach to get data and then update it.
If you absolutely must make that work then I think you'll have to use a global temp table. You'll need to see if it exists before running your dynamic sql and clean up. With a fixed table name you'll run into problems with other connections. Inside the dynamic sql you'll add select * into ##temptable from .... Actually I'm not even sure why you want the temp table in the first place. Can't the dynamic sql just return the results?
On the surface it seems like a solid idea to have one generic procedure for returning data with a couple of parameters to drive it but, without a lot of explanation, it's just not the way database are designed to work.
You should create the temp table.
IF OBJECT_ID('tempdb..##TempTable') IS NOT NULL
DROP TABLE ##TempTable
CREATE TABLE ##TempTable()

Resources