How to insert Dynamic SQL Result INTO Temporary Table - sql-server

I implement some code:
BEGIN
DECLARE
#SQL AS NVARCHAR(MAX),
#TempTable AS NVARCHAR(MAX)
SET #SQL = 'SELECT * from Employee where Instance_ID = 1';
BEGIN
CREATE TABLE ##tempResults (SQL NVARCHAR(4000))
INSERT INTO ##tempResults EXEC #SQL;
SET #TempTable= 'select * from #tempResults ORDER BY CASE WHEN ' + #index+ ' =1 THEN [First Name] END DESC '+ ',' + ' CASE WHEN ' + #index + '=2 THEN [Last name] END DESC'
END
EXEC sp_executesql #TempTable;
END
I want to insert the dynamic results into temp table but I can't execute statement and get error. Please advice me for how should I need to do ?
As the error shown:
"Msg 203 is not a valid identifier."

You should use EXEC(#SQL) See here
It is advisable to switch to exec sp_executesql #SQL, it gives you parameterization and helps agains sql injection. Especially since you already use this later on in your query (it's never a good idea to use different methods to accomplish the same thing). See here

Related

How to exclude master Database

I have created the following query which will get the data from all databases. when i execute the query i am getting error invalid column id. i have investigated and found the table tbl_table_A (example) is listed in master database and this table is not having column id. i have exlcluded this DB master but not sure why the query is still calling the master DB. kindly advise
Query :
CREATE TABLE ##tbl_data
(
[database_name] NVARCHAR(500),
id INT,
last_run DATETIME,
[next_run] DATETIME,
last_run_status NVARCHAR(500)
)
DECLARE #StartDate NVARCHAR(MAX)
DECLARE #EndDate NVARCHAR(MAX)
DECLARE #strSQL NVARCHAR(MAX)
SET #StartDate = '10-Dec-2019 00:12:59'
SET #EndDate = '10-Dec-2019 00:17:59'
SET #strSQL =
'
USE [?]
IF ''?'' <> ''master'' AND ''?'' <> ''model'' AND ''?'' <> ''msdb'' AND ''?'' <> ''tempdb''
BEGIN
IF OBJECT_ID(''tbl_table_A'') IS NULL
RETURN;
insert into ##tbl_data
SELECT ''?'', id,last_run,next_run,last_run_status
FROM dbo.tbl_table_A nolock
WHERE last_run between cast ('''+#StartDate+''' as Datetime2) and cast ('''+#EndDate+''' as Datetime2)
END'
EXEC dbo.sp_msforeachdb #strSQL
Select * from ##tbl_data
Drop table ##tbl_data
i have exlcluded this DB master but not sure why the query is still
calling the master DB
No, your code does not "call" master.
Here is your code where I use print instead of insert.
This way you can see what exactly db is checked and whether there is or there is not your table there:
declare #strSQL NVARCHAR(MAX)
SET #strSQL =
'
USE [?]
IF ''?'' <> ''master'' AND ''?'' <> ''model'' AND ''?'' <> ''msdb'' AND ''?'' <> ''tempdb''
BEGIN
print ''?''
IF OBJECT_ID(''tbl_table_A'') IS NULL
begin
print ''there is no table tbl_table_A''
print ''----------------''
RETURN;
end
print ''***** THERE IS table tbl_table_A *****''
print ''----------------''
END'
EXEC dbo.sp_msforeachdb #strSQL
I always like to take a different approach to these types of problems. For one, I really dislike cursors and you don't really need one here. Also, sp_msforeachdb is not only undocumented it has some problems. It will sometimes skip databases and nobody really seems to know why. Aaron Bertrand discusses this and provides a better alternative here. https://sqlblog.org/2010/12/29/a-more-reliable-and-more-flexible-sp_msforeachdb
I prefer to do something like below. There are no loops and won't run into weird behavior like skipping tables. It also does not require a global temp table which can have serious concurrency issues. This requires two dynamic sql statements. The first gets the list of databases with the table you want to find. Then we use that data to generate the dynamic sql statement against the list of database we want to search.
if OBJECT_ID('tempdb..#Databases') is not null
drop table #Databases
DECLARE #StartDate NVARCHAR(MAX)
, #EndDate NVARCHAR(MAX)
, #strSQL NVARCHAR(MAX)
SELECT #StartDate = '20191210 00:12:59'
, #EndDate = '20191210 00:17:59'
, #strSQL = ''
declare #TableName sysname = 'tbl_table_A'
select #strSQL = #strSQL + 'select ''' + d.name + ''' from ' + quotename(d.name) + '.sys.tables where name = ''' + #TableName + ''' union all '
from sys.databases d
select #strSQL = left(#strSQL, len(#strSql) - 10) --this removes the last union all
CREATE TABLE #Databases
(
DatabaseName sysname
)
--select #strSQL
insert #Databases
(
DatabaseName
)
exec sp_executesql #strSQL
set #strSQL = ''
select #strSQL = #strSQL + 'select ''' + d.DatabaseName + ''', id, last_run, next_run, last_run_status from ' + quotename(d.DatabaseName) + '.dbo.' + #TableName + ' where last_run between #_StartDate and #_EndDate union all '
from #Databases d
select #strSQL = left(#strSQL, len(#strSql) - 10)
--select #strSQL
exec sp_executesql #strSQL, N'#_StartDate datetime, #_EndDate datetime', #_StartDate = #StartDate, #_EndDate = #EndDate

Column name not working when placed inside a variable in SQL Server [duplicate]

create procedure sp_First
#columnname varchar
AS
begin
select #columnname from Table_1
end
exec sp_First 'sname'
My requirement is to pass column names as input parameters.
I tried like that but it gave wrong output.
So Help me
You can do this in a couple of ways.
One, is to build up the query yourself and execute it.
SET #sql = 'SELECT ' + #columnName + ' FROM yourTable'
sp_executesql #sql
If you opt for that method, be very certain to santise your input. Even if you know your application will only give 'real' column names, what if some-one finds a crack in your security and is able to execute the SP directly? Then they can execute just about anything they like. With dynamic SQL, always, always, validate the parameters.
Alternatively, you can write a CASE statement...
SELECT
CASE #columnName
WHEN 'Col1' THEN Col1
WHEN 'Col2' THEN Col2
ELSE NULL
END as selectedColumn
FROM
yourTable
This is a bit more long winded, but a whole lot more secure.
No. That would just select the parameter value. You would need to use dynamic sql.
In your procedure you would have the following:
DECLARE #sql nvarchar(max) = 'SELECT ' + #columnname + ' FROM Table_1';
exec sp_executesql #sql, N''
Try using dynamic SQL:
create procedure sp_First #columnname varchar
AS
begin
declare #sql nvarchar(4000);
set #sql='select ['+#columnname+'] from Table_1';
exec sp_executesql #sql
end
go
exec sp_First 'sname'
go
This is not possible. Either use dynamic SQL (dangerous) or a gigantic case expression (slow).
Create PROCEDURE USP_S_NameAvilability
(#Value VARCHAR(50)=null,
#TableName VARCHAR(50)=null,
#ColumnName VARCHAR(50)=null)
AS
BEGIN
DECLARE #cmd AS NVARCHAR(max)
SET #Value = ''''+#Value+ ''''
SET #cmd = N'SELECT * FROM ' + #TableName + ' WHERE ' + #ColumnName + ' = ' + #Value
EXEC(#cmd)
END
As i have tried one the answer, it is getting executed successfully but while running its not giving correct output, the above works well
You can pass the column name but you cannot use it in a sql statemnt like
Select #Columnname From Table
One could build a dynamic sql string and execute it like EXEC (#SQL)
For more information see this answer on dynamic sql.
Dynamic SQL Pros and Cons
As mentioned by MatBailie
This is much more safe since it is not a dynamic query and ther are lesser chances of sql injection . I Added one situation where you even want the where clause to be dynamic . XX YY are Columns names
CREATE PROCEDURE [dbo].[DASH_getTP_under_TP]
(
#fromColumnName varchar(10) ,
#toColumnName varchar(10) ,
#ID varchar(10)
)
as
begin
-- this is the column required for where clause
declare #colname varchar(50)
set #colname=case #fromUserType
when 'XX' then 'XX'
when 'YY' then 'YY'
end
select SelectedColumnId from (
select
case #toColumnName
when 'XX' then tablename.XX
when 'YY' then tablename.YY
end as SelectedColumnId,
From tablename
where
(case #fromUserType
when 'XX' then XX
when 'YY' then YY
end)= ISNULL(#ID , #colname)
) as tbl1 group by SelectedColumnId
end
First Run;
CREATE PROCEDURE sp_First #columnname NVARCHAR(128)--128 = SQL Server Maximum Column Name Length
AS
BEGIN
DECLARE #query NVARCHAR(MAX)
SET #query = 'SELECT ' + #columnname + ' FROM Table_1'
EXEC(#query)
END
Second Run;
EXEC sp_First 'COLUMN_Name'
Please Try with this.
I hope it will work for you.
Create Procedure Test
(
#Table VARCHAR(500),
#Column VARCHAR(100),
#Value VARCHAR(300)
)
AS
BEGIN
DECLARE #sql nvarchar(1000)
SET #sql = 'SELECT * FROM ' + #Table + ' WHERE ' + #Column + ' = ' + #Value
--SELECT #sql
exec (#sql)
END
-----execution----
/** Exec Test Products,IsDeposit,1 **/

Variable table name in select statement

I have some tables for storing different file information, like thumbs, images, datasheets, ...
I'm writing a stored procedure to retrieve filename of a specific ID. something like:
CREATE PROCEDURE get_file_name(
#id int,
#table nvarchar(50)
)as
if #table='images'
select [filename] from images
where id = #id
if #table='icons'
select [filename] from icons
where id = #id
....
How can I rewrite this procedure using case when statement or should I just use table name as variable?
You can't use case .. when to switch between a table in the FROM clause (like you can in a conditional ORDER BY). i.e. so the following:
select * from
case when 1=1
then t1
else t2
end;
won't work.
So you'll need to use dynamic SQL. It's best to parameterize the query as far as possible, for example the #id value can be parameterized:
-- Validate #table is E ['images', 'icons', ... other valid names here]
DECLARE #sql NVARCHAR(MAX)
SET #sql = 'select [filename] from **TABLE** where id = #id';
SET #sql = REPLACE(#sql, '**TABLE**', #table);
sp_executesql #sql, N'#id INT', #id = #id;
As with all dynamic Sql, note that unparameterized values which are substituted into the query (like #table), make the query vulnerable to Sql Injection attacks. As a result, I would suggest that you ensure that #table comes from a trusted source, or better still, the value of #table is compared to a white list of permissable tables prior to execution of the query.
Just build SQL string in another variable and EXECUTE it
DECLARE #sql AS NCHAR(500)
SET #sql=
'SELECT [filename] '+
' FROM '+#table+
' WHERE id = #id'
EXECUTE(#sql)
CREATE PROCEDURE get_file_name(
#id int,
#table nvarchar(50)
)as
DECLARE #SQL nvarchar(max);
SET #SQL = 'select [filename] from ' + #table + ' where id = ' + #id
EXECUTE (#SQL)

Using temp table with exec #sql in stored procedure

I have a stored procedure and part of them as below:
#DRange is a incoming varchar value
declare #sql varchar(max)
set #sql = 'select * into #tmpA from TableA where create_date >= getDate - ' + #DRange + '' and is_enabled = 1'
exec (#sql)
select * from #tmpA
The problem is when I execute the stored procedure, an error message occurs:
Cannot find the object "#tmpA" because it does not exist or you do not have permissions.
Is it not possible to use temp table and execute it or did I do something wrong?
#tmpA is created in a different scope, so is not visible outside of the dynamic SQL. You can just make the ultimate SELECT a part of the dynamic SQL. Also a couple of other things:
Always use the schema prefix when creating/referencing objects
Always use sp_executesql for dynamic SQL; in this case it allows you to parameterize the #DRange value and avoid SQL injection risks.
Always prefix Unicode strings with N - Unicode is required for sp_executesql but if you get lazy about this in other areas of your code it can also lead to painful implicit conversions.
DECLARE #sql NVARCHAR(MAX);
SET #sql = N'select * into #tmpA from dbo.TableA
where create_date >= DATEADD(DAY, -#DRange, GETDATE())
AND is_enabled = 1; SELECT * FROM #tmpA';
EXEC sp_executesql #sql, N'#DRange INT', #DRange;
Of course if all you're doing is selecting, I have a hard time understanding why this is dynamic SQL in the first place. I assume your query (or what you later do with the temp table) is more complicated than this - if so, don't dumb it down for us. Telling us your whole problem will prevent a lot of back and forth, as the additional details could change the answer.
Here's what I'd do.
declare #sql varchar(max)
set #sql = 'select * from TableA where create_date >= getDate - ' + #DRange + '' and is_enabled = 1'
Select * Into #tmpA from TableA where create_date = '01/01/1000' -- to create a blank table
insert into #tmpA
exec (#sql)
select * from #tmpA

ALTER TABLE my_table ADD #column INT

If i want to use a variable as name of the new column, is this posible in MS SQL?
Example that dont work:
ALTER TABLE my_table ADD #column INT
This worked great for me:
EXEC ('ALTER TABLE my_table ADD ' + #column + ' INT')
This is possible using dynamic sql to build your DDL and using the EXEC command to execute the string.
Declare #SQL VarChar(1000)
SELECT #SQL = 'ALTER TABLE my_table ADD ' + #column + ' INT'
Exec (#SQL)
See this article.
I will also add that the moment you venture to the land of dynamic sql, you need to take care to not expose yourself to SQL Injection attacks. Always clean up the parameters coming in.
As Philip mentions - think long and hard before doing this. The fact that it is possible does not make it a good thing...
Erland Sommarskog wrote an extensive article about using dynamic sql - The curse and blessings of dynamic SQL which I recommend reading fully.
Have a look at (EXECUTE (Transact-SQL))
CREATE TABLE MyTable(
ID INT
)
GO
SELECT * FROM MyTable
GO
DECLARE #column VARCHAR(100)
SET #column = 'MyNewCol'
EXEC('ALTER TABLE MyTable ADD ' + #column + ' INT')
GO
SELECT * FROM MyTable
GO
DROP TABLE MyTable
alter procedure sp_check_table_column
(
#field_name varchar(max),
#data_type varchar(max),
#mandatory varchar(max)
)
as
if not exists (select COLUMN_NAME from INFORMATION_SCHEMA.COLUMNS where TABLE_NAME = '<table_name>' and COLUMN_NAME = #field_name)
begin
declare #sql varchar(max)
set #sql = ('ALTER TABLE <table_name> ADD ' + #field_name + ' ' + #data_type + ' ' + #mandatory)
exec (#sql)
end

Resources