I am running a cursor that executes dynamic SQL using a variable.
SET #Wk = CAST(#Cntr AS nvarchar(5))
DECLARE #params nvarchar(30) = N'#Wk nvarchar(5)'
-- .. start cursor
EXEC sp_executesql N'ALTER TABLE #Temp DROP COLUMN [WK #WK Sold]', #params, #Wk
I get the error
Msg 4924, Level 16, State 1, Line 4
ALTER TABLE DROP COLUMN failed because column 'WK #WK Sold' does not exist in table #Temp
I know that #param and #Wk work because I ran
EXEC sp_executesql N'select #Wk', #params, #Wk
and it worked. I know I can just run
EXEC ('ALTER TABLE #Temp DROP COLUMN [WK ' + #Wk + ' Sold]')
but I'd like to use sp_executesql.
Is it even possible the way I have tried?
Thank you
The problem has nothing to do with the variable here. The problem is that you think that a variable/parameter when used in the context of an object results in that variable being injected into the statement. That doesn't happen. SELECT 1 AS [#a] return return a column aliased as literally #a not a column where the alias is the value of a variable called #a.
What you need to do here is safely inject the value into the dynamic statement and ideally validate the name too:
DECLARE #Cntr SomeDataType; --I don't know what the datatype should be.
DECLARE #wk nvarchar(5),
#Column sysname;
SET #wk = #Cntr; --No need for the cast here
SET #Column = (SELECT c.name
FROM tempdb.sys.tables t
JOIN tempdb.sys.columns c ON t.object_id = c.object_id
WHERE t.[name] LIKE N'#temp%'
AND c.name = N'WK
' + #wk + N' Sold');--Does your column name really have a carriage return and line break in it?
DECLARE #SQL nvarchar(MAX) = N'ALTER TABLE #Temp DROP COLUMN ' + QUOTENAME(#Column) + N';';
EXEC sys.sp_executesql #SQL;
Related
I know it's bad practice but the current situation requires a special character (sharp s = ß) as a column name. How can I write a SQL query which adds the column with the special character? With SSMS it adds Straße but when I run the SQL query with sqlcmd.exe through an external program it adds Straße.
This is the script:
DECLARE #Street varchar(50)='Straße';
IF NOT EXISTS(SELECT 1 FROM sys.columns
WHERE Name = N'#Street'
AND Object_ID = Object_ID(N'Document'))
BEGIN
EXECUTE
(
'ALTER TABLE dbo.Document ADD ' +
#Street + ' varchar(50) NULL
')
END
You need to use nvarchar not varchar:
DECLARE #Street sysname = N'Straße'; --synonym for nvarchar(128) NOT NULL
IF NOT EXISTS(SELECT 1 FROM sys.columns
WHERE Name = #Street --This shouldn't be a literal string
AND Object_ID = Object_ID(N'Document'))
BEGIN
DECLARE #SQL nvarchar(MAX);
SET #SQL - N'ALTER TABLE dbo.Document ADD ' QUOTENAME#(Street) + N' varchar(50) NULL;';
--Should the column be an nvarchar too, considering it's name?
EXEC sys.sp_executesql #SQL;
END;
I am trying to execute this query:
declare #tablename varchar(50)
set #tablename = 'test'
select * from #tablename
This produces the following error:
Msg 1087, Level 16, State 1, Line 5
Must declare the table variable "#tablename".
What's the right way to have the table name populated dynamically?
For static queries, like the one in your question, table names and column names need to be static.
For dynamic queries, you should generate the full SQL dynamically, and use sp_executesql to execute it.
Here is an example of a script used to compare data between the same tables of different databases:
Static query:
SELECT * FROM [DB_ONE].[dbo].[ACTY]
EXCEPT
SELECT * FROM [DB_TWO].[dbo].[ACTY]
Since I want to easily change the name of table and schema, I have created this dynamic query:
declare #schema sysname;
declare #table sysname;
declare #query nvarchar(max);
set #schema = 'dbo'
set #table = 'ACTY'
set #query = '
SELECT * FROM [DB_ONE].' + QUOTENAME(#schema) + '.' + QUOTENAME(#table) + '
EXCEPT
SELECT * FROM [DB_TWO].' + QUOTENAME(#schema) + '.' + QUOTENAME(#table);
EXEC sp_executesql #query
Since dynamic queries have many details that need to be considered and they are hard to maintain, I recommend that you read: The curse and blessings of dynamic SQL
Change your last statement to this:
EXEC('SELECT * FROM ' + #tablename)
This is how I do mine in a stored procedure. The first block will declare the variable, and set the table name based on the current year and month name, in this case TEST_2012OCTOBER. I then check if it exists in the database already, and remove if it does. Then the next block will use a SELECT INTO statement to create the table and populate it with records from another table with parameters.
--DECLARE TABLE NAME VARIABLE DYNAMICALLY
DECLARE #table_name varchar(max)
SET #table_name =
(SELECT 'TEST_'
+ DATENAME(YEAR,GETDATE())
+ UPPER(DATENAME(MONTH,GETDATE())) )
--DROP THE TABLE IF IT ALREADY EXISTS
IF EXISTS(SELECT name
FROM sysobjects
WHERE name = #table_name AND xtype = 'U')
BEGIN
EXEC('drop table ' + #table_name)
END
--CREATES TABLE FROM DYNAMIC VARIABLE AND INSERTS ROWS FROM ANOTHER TABLE
EXEC('SELECT * INTO ' + #table_name + ' FROM dbo.MASTER WHERE STATUS_CD = ''A''')
Use:
CREATE PROCEDURE [dbo].[GetByName]
#TableName NVARCHAR(100)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
DECLARE #sSQL nvarchar(500);
SELECT #sSQL = N'SELECT * FROM' + QUOTENAME(#TableName);
EXEC sp_executesql #sSQL
END
You can't use a table name for a variable. You'd have to do this instead:
DECLARE #sqlCommand varchar(1000)
SET #sqlCommand = 'SELECT * from yourtable'
EXEC (#sqlCommand)
You'll need to generate the SQL content dynamically:
declare #tablename varchar(50)
set #tablename = 'test'
declare #sql varchar(500)
set #sql = 'select * from ' + #tablename
exec (#sql)
Use sp_executesql to execute any SQL, e.g.
DECLARE #tbl sysname,
#sql nvarchar(4000),
#params nvarchar(4000),
#count int
DECLARE tblcur CURSOR STATIC LOCAL FOR
SELECT object_name(id) FROM syscolumns WHERE name = 'LastUpdated'
ORDER BY 1
OPEN tblcur
WHILE 1 = 1
BEGIN
FETCH tblcur INTO #tbl
IF ##fetch_status <> 0
BREAK
SELECT #sql =
N' SELECT #cnt = COUNT(*) FROM dbo.' + quotename(#tbl) +
N' WHERE LastUpdated BETWEEN #fromdate AND ' +
N' coalesce(#todate, ''99991231'')'
SELECT #params = N'#fromdate datetime, ' +
N'#todate datetime = NULL, ' +
N'#cnt int OUTPUT'
EXEC sp_executesql #sql, #params, '20060101', #cnt = #count OUTPUT
PRINT #tbl + ': ' + convert(varchar(10), #count) + ' modified rows.'
END
DEALLOCATE tblcur
You need to use the SQL Server dynamic SQL:
DECLARE #table NVARCHAR(128),
#sql NVARCHAR(MAX);
SET #table = N'tableName';
SET #sql = N'SELECT * FROM ' + #table;
Use EXEC to execute any SQL:
EXEC (#sql)
Use EXEC sp_executesql to execute any SQL:
EXEC sp_executesql #sql;
Use EXECUTE sp_executesql to execute any SQL:
EXECUTE sp_executesql #sql
Declare #tablename varchar(50)
set #tablename = 'Your table Name'
EXEC('select * from ' + #tablename)
Also, you can use this...
DECLARE #SeqID varchar(150);
DECLARE #TableName varchar(150);
SET #TableName = (Select TableName from Table);
SET #SeqID = 'SELECT NEXT VALUE FOR ' + #TableName + '_Data'
exec (#SeqID)
Declare #fs_e int, #C_Tables CURSOR, #Table varchar(50)
SET #C_Tables = CURSOR FOR
select name from sysobjects where OBJECTPROPERTY(id, N'IsUserTable') = 1 AND name like 'TR_%'
OPEN #C_Tables
FETCH #C_Tables INTO #Table
SELECT #fs_e = sdec.fetch_Status FROM sys.dm_exec_cursors(0) as sdec where sdec.name = '#C_Tables'
WHILE ( #fs_e <> -1)
BEGIN
exec('Select * from ' + #Table)
FETCH #C_Tables INTO #Table
SELECT #fs_e = sdec.fetch_Status FROM sys.dm_exec_cursors(0) as sdec where sdec.name = '#C_Tables'
END
If I run the select statement below by itself (with the table name hard coded in) it runs fine and the temp table is created. If I run it as below it says 'Invalid object name '#TempCodes'' although when I print #Sql1 it looks exactly the same as when I run it by itself (with the table name hard coded in).
Any ideas on what's going on here will be appreciated.
DECLARE #TableName NVARCHAR(50)
SET #TableName = '[My Table Name]'
DECLARE #Sql1 NVARCHAR(MAX);
SET #Sql1 = N'SELECT AccountNumber,LTRIM(RTRIM(m.n.value(''.[1]'',''varchar(8000)''))) AS mdcodes INTO #TempCodes FROM (SELECT AccountNumber,CAST(''<XMLRoot><RowData>''
+ REPLACE(MD_Results,'','',''</RowData><RowData>'')
+ ''</RowData></XMLRoot>'' AS XML) AS x FROM ' + #TableName
+ N')t CROSS APPLY x.nodes(''/XMLRoot/RowData'')m(n)'
IF OBJECT_ID('tempdb.dbo.#TempCodes', 'U') IS NOT NULL
BEGIN
drop table #TempCodes
END
EXECUTE sp_executesql #Sql1
Select * from #TempCodes
I believe ## is a typo. Even if you fix it to # it will throw same error.
EXECUTE sp_executesql #Sql1
A local temporary table created in a stored procedure is dropped
automatically when the stored procedure is finished.
In your case sp_executesql is the stored procedure.
the table created inside the dynamic query will be dropped after the exec sp_executesql is completed. That's why you are getting that error.
You need to create table outside and use Insert into table..select syntax inside the dynamic query
IF OBJECT_ID('tempdb.dbo.#TempCodes', 'U') IS NOT NULL
drop table #TempCodes
create table #TempCodes(AccountNumber varchar(100),mdcodes varchar(100))
SET #Sql1 = N'Insert into #TempCodes
SELECT AccountNumber,LTRIM(RTRIM(m.n.value(''.
[1]'',''varchar(8000)''))) AS mdcodes FROM (SELECT
AccountNumber,CAST(''<XMLRoot><RowData>'' +
REPLACE(MD_Results,'','',''</RowData><RowData>'') + ''</RowData></XMLRoot>''
AS XML) AS x FROM '+#TableName+ N')t CROSS APPLY
x.nodes(''/XMLRoot/RowData'')m(n)'
Try using a global temp table ##TempCodes as local temporary tables are only visible in current session.
DECLARE #TableName NVARCHAR(50)
SET #TableName = '[My Table Name]'
DECLARE #Sql1 NVARCHAR(MAX);
SET #Sql1 = N'SELECT AccountNumber,LTRIM(RTRIM(m.n.value(''.
[1]'',''varchar(8000)''))) AS mdcodes INTO ##TempCodes FROM (SELECT
AccountNumber,CAST(''<XMLRoot><RowData>'' +
REPLACE(MD_Results,'','',''</RowData><RowData>'') + ''</RowData></XMLRoot>''
AS XML) AS x FROM '+#TableName+ N')t CROSS APPLY
x.nodes(''/XMLRoot/RowData'')m(n)'
IF OBJECT_ID('tempdb.dbo.##TempCodes', 'U') IS NOT NULL
BEGIN
drop table ##TempCodes
END
EXECUTE sp_executesql #Sql1
Select * from ##TempCodes
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)
Hi I am writing a large stored procedure, which creates a dynamic report table, of n columns in size, the first 6 are constant the remainder depend on a few arguments passed to the procedure to create the table with the required columns.
The problem that I am having is with the following TSQL
DECLARE #columnname VARCHAR(50)
SET #columnname = 'on_' + #description
IF NOT EXISTS(SELECT * FROM syscolumns WHERE id = OBJECT_ID('reports')
AND NAME = #columnname)
BEGIN
ALTER TABLE reports ADD #columnname VARCHAR(50) NULL
END
I am getting syntax errors with this at the #columnname in the ALTER TABLE statement of the above code.
Also as I am new to this, I am not sure if this is the best way to do this, or if there are better ways in TSQL to generate the required dynamic table.
Try this:
declare #sql nvarchar(100)
set #sql = 'ALTER TABLE reports ADD '+ #columnname+' VARCHAR(50) NULL'
exec sp_executesql #sql
Try
DECLARE #columnname VARCHAR(50)
SET #columnname = '[on_' + #description +']'
IF NOT EXISTS(SELECT * FROM syscolumns WHERE id = OBJECT_ID('reports')
AND NAME = #columnname)
BEGIN
ALTER TABLE reports ADD #columnname VARCHAR(50) NULL
END
Cannot get around having to do it dynamically I believe so change your BEGIN block to something like this:
DECLARE #sql VARCHAR(8000)
BEGIN
SET #sql = 'ALTER TABLE Table_1 ADD '+#columnname+' VARCHAR(50) NULL'
EXEC(#sql)
END