Query with variable tablename - sql

I don't know why I keep bumping into these, but I have a small problem with a stored procedure. The only special in it is the variable Tablename:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE dbo.ProdTijdCompare
#TABLENAME SYSNAME,
#scanner nvarchar(50)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #SQL NVARCHAR(MAX);
SELECT #SQL = 'select SUM(tijd) from ' + #TABLENAME + 'where Scanner = #scanner'
EXEC sp_executesql #SQL;
END
GO
The error I'm getting when executing in SSMM:
Msg 102, Level 15, State 1, Line 3
Incorrect syntax near '='.
(1 row(s) affected)

You need a space between #TableName and where:
SELECT #SQL = 'select SUM(tijd) from ' + #TABLENAME + ' where Scanner = #scanner';
This type of error is incredibly obvious if you just print out the SQL before running it.

Always quote table names in dynamic SQL to avoid SQL Injection (cf QUOTENAME)
Second, supply the #scanner parameter to the sp_executesql procedure as you can see in the sample below:
SELECT #SQL = 'select SUM(tijd) from ' + QUOTENAME(#TABLENAME) + ' where Scanner = #scanner'
EXEC sp_executesql #SQL, N'#scanner NVARCHAR(50)', #scanner;

Related

Invalid Column name error during execution of stored procedure

I am not able to execute the stored procedure. It is throwing an error
Invalid Column name 'DW201401'
Command used to execute the stored procedure:
exec RM_UTIL_MODE server,'DW201401'
Stored procedure code:
ALTER Procedure [dbo].[RM_UTIL_MODE]
#ServerName varchar(50),
#Value varchar(50)
As
Begin
declare #query nvarchar(max)
set #query = N'SELECT mode FROM ' + #ServerName +
N'.master.dbo.sysdatabases WHERE name =' + #Value
exec sp_executesql #query
End
But when I tried to run the query alone as shown below it is giving me result.
select mode, name
from server.master.dbo.sysdatabases
where name = 'DW201401'
Presumably, the issue is quotes around #Value:
declare #query nvarchar(max)
set #query = N'SELECT mode FROM '
+ #ServerName
+ N'.master.dbo.sysdatabases
WHERE name = '''+#Value+'''';
However, I would use parameter substitution instead:
declare #query nvarchar(max) ;
set #query = N'SELECT mode
FROM ' + #ServerName + N'.master.dbo.sysdatabases
WHERE name = #value';
exec sp_executesql #query, N'#value varchar(50)', #value = #value;
You are already using sp_executesql, so you might as well use it properly. Note: you cannot substitute the server name.
EDIT:
To elaborate on the comment, I would write the code this way:
declare #sql nvarchar(max) ;
set #sql = N'
SELECT mode
FROM #ServerName.master.dbo.sysdatabases
WHERE name = #value';
set #sql = replace(#sql, '#ServerName', quotename(#ServerName));
exec sp_executesql #sql, N'#value varchar(50)', #value = #value;
When using dynamic SQL, I no longer piece together the query using string concatenation. Instead, I put in place holders and use replace(). I find that concatenation is hard to maintain and often obscures what the SQL is doing. Although there is a bit more overhead in using replace() (and I often do it multiple times), it is worth it for preventing errors and maintaining the code (plus, my queries tend to run for a while anyway, so the overhead is minimal compared to the query time).
Your select looks like:
select mode, name from server.master.dbo.sysdatabases where name = DW201401
so you need to add escaped quotes in your dynamic query:
exec RM_UTIL_MODE cefmtqcfindv3,'DW201401'
ALTER Procedure [dbo].[RM_UTIL_MODE]
#ServerName varchar(50),#Value varchar(50)
As
Begin
declare #query nvarchar(max)
set #query = N'SELECT mode FROM '
+ #ServerName
+ N'.master.dbo.sysdatabases
WHERE name ='''+#Value+''''
exec sp_executesql #query
End
Just as a suggestion, when you are building a dynamic sql, try using PRINT instead of EXEC, then get what is printed and try it out. Most of the times you will know what went wrong.
Just as an example:
ALTER Procedure [dbo].[RM_UTIL_MODE]
#ServerName varchar(50),#Value varchar(50)
As
Begin
declare #query nvarchar(max)
set #query = N'SELECT mode FROM '
+ #ServerName
+ N'.master.dbo.sysdatabases
WHERE name ='''+#Value+''''
PRINT #query
--exec sp_executesql #query
End

How to pass a table variable using sp_executesql

I'm trying to create a table using sp_executesql but I keep getting an error that says "Incorrect syntax near '#_TableName'. Any idea what I'm doing wrong here?
Here's the code that I'm using:
DECLARE #SQLString NVARCHAR(MAX),
#ParamDefinition NVARCHAR(MAX),
#TableName NVARCHAR(MAX);
SET #TableName = N'[dbo].[MyTable]';
SET #SQLString = N'SELECT * FROM #_TableName;';
SET #ParamDefinition = N'#_TableName NVARCHAR(max)';
EXEC sp_executesql #SQLString, #ParamDefinition,
#_TableName = #TableName;
That yields the error:
Msg 102, Level 15, State 1, Line 1
Incorrect syntax near '#_TableName'.
If I hard code the table name and the column type (I have to do both) then the query works, otherwise I get the incorrect syntax message for both those variables.
In case you're wondering, I want to put this code inside a stored procedure, so that if anyone wants to create or modify a table then they call this stored procedure which can run additional validations.
Figured out the problem.
Apparently sp_executesql expects the parameter definition for a table to be of a table type (see this answer for an example: https://stackoverflow.com/a/4264553/21539).
An easier way to solve this problem was to insert the variables names directly into the SQLStatement string as follows:
DECLARE #SQLString NVARCHAR(MAX),
#TableName NVARCHAR(MAX);
SET #TableName = N'[dbo].[MyTable]';
SET #SQLString = N'SELECT * FROM ' + #TableName + ';';
SET #ParamDefinition = N'#_TableName NVARCHAR(max);
EXEC sp_executesql #SQLString;

SQL Server stored procedures calls require variable declaration (but already declared)

I have a few stored procedures which I call in this order.
So, from the first stored procedure, importTaxonomy I call parseXBRL and from parseXBRL I call createTaxonomyStructure.
But in this flow, when the code of the last stored procedure is executed I get an error.
Msg 1087, Level 15, State 2, Line 1
Must declare the table variable "#temporaryTable".
Below you can find the first few lines code of this stored procedure:
CREATE PROCEDURE createTaxonomyStructure #taxonomy_table nvarchar(max), #debug bit = 0
AS
DECLARE #statement NVARCHAR(MAX)
DECLARE #temporaryTable TABLE (taxonomyLine NVARCHAR(MAX)) -- declared a temporary table to avoid creating a Dynamic Query with the entire cursor, but just with the temporary table
DECLARE #taxonomyLine NVARCHAR(MAX) -- variable that will store one line of the taxonomy
SET #statement = 'INSERT INTO #temporaryTable SELECT taxText FROM ' + #taxonomy_table -- statement that will import the taxonomy in the temporary table
EXEC sp_executesql #statement
DECLARE taxonomyCursor CURSOR READ_ONLY FAST_FORWARD FOR -- read each line in the taxonomy to parse afterwards
SELECT taxonomyLine
FROM #temporaryTable
OPEN taxonomyCursor
FETCH NEXT FROM taxonomyCursor INTO #taxonomyLine -- parsing each taxonomy line and extracting the values from important attributes
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE #id_element NVARCHAR(MAX)
DECLARE #leaf_element NVARCHAR(MAX)
SELECT #id_element = (SELECT dbo.extract_IDElement(#taxonomyLine))
SELECT #leaf_element = (SELECT dbo.extract_IDLeafElement(#taxonomyLine))
SET #statement = 'UPDATE ' + #taxonomy_table + ' SET fullName = ''' + #id_element + ''', leafName = ''' + #leaf_element + '''';
EXEC sp_executesql #statement
END
I do declare this variable, but I still get the error and I don't understand why.
How can I get over this error?
Thanks!
the error is here:
SET #statement = 'INSERT INTO #temporaryTable SELECT taxText FROM ' + #taxonomy_table -- statement that will import the taxonomy in the temporary table
EXEC sp_executesql #statement
change it to
set #statement = 'select taxText from ' + #taxonomy_table
insert into #temporaryTable
exec sp_executesql #statement
The error is happening because in the scope of executing #statement there's no variable table #temporaryTable.

SQL dynamically create stored procedures?

I am trying to create a script to create/setup a group of stored procedures that will all be fairly similar.
So I am trying to loop through this code, changing the #DATABASE_NAME and #TableName when needed.
/* Start loop */
DECLARE #create_stored_procedure nvarchar(max)
SET #create_stored_procedure = N'
USE [' + #DATABASE_NAME + ']
CREATE PROCEDURE [dbo].[sproc_imp_' + #TableName + ']
AS
BEGIN
PRINT(''doing something'')
END'
EXEC sp_executesql #statement = #create_stored_procedure
/* End loop */
But I am getting errors saying
'CREATE/ALTER PROCEDURE' must be the first statement in a query batch.
or
'CREATE/ALTER PROCEDURE' does not allow specifying the database name as a prefix to the object name.
All the solutions online suggest using GO, but that won't work in dynamic SQL.
Does anyone know a possible solution for SQL Server 2005?
I wouldn't call the solution intuitive, but apparently this works. I prefer the look of this one though.
Try with spiting USe DB and create procedure. Like this
DECLARE #create_store_procedure nvarchar(max)
SET #create_store_procedure = N'
USE [' + #DATABASE_NAME + '] '
EXEC sp_executesql #statement = #create_store_procedure
SET #create_store_procedure = N'
CREATE PROCEDURE [dbo].[sproc_imp_' + #TableName + ']
AS
BEGIN
PRINT(''doing something'')
END '
EXEC sp_executesql #statement = #create_store_procedure
This is working perfectly for me
I tried Nithesh's answer and that didn't work for me it ended up creating the store procedure in the master table. Zec's answer worked. Creating a dynamic query inside my dynamic query.
DECLARE #create_store_procedure nvarchar(max)
DECLARE #use_db nvarchar(max)
SET #create_store_procedure = N'
CREATE PROCEDURE [dbo].[sproc_imp_' + #TableName + ']
AS
BEGIN
PRINT(''doing something'')
END '
SET #use_db = N'
USE [' + #DATABASE_NAME + ']
DECLARE #sub_create_store_procedure nvarchar(max)
SET #sub_create_store_procedure = ''' + REPLACE(#create_store_procedure, CHAR(39), CHAR(39) + CHAR(39)) + '''
EXEC sp_executesql #statement = #sub_create_store_procedure
'
EXEC sp_executesql #statement = #use_db

Hunting the SQL bug: sp_executesql -- Msg 911, Level 16, State 1

I need to execute certain script on various SQL Server instances. They use the database with a different identification. However, all the databases have the same table (same name and structure) that should be processed. Because of that I want to detect the name of the database, set it to a string variable, construct the SQL statement, and execute the constructed string via sp_executesql. The bare command that executes correctly is:
USE [1000574];
SELECT TOP 10 temperature_1, UTC FROM dbo.Data;
Then I am trying to execute the equivalent string with the #database_name placeholder:
DECLARE #database_name nvarchar(100);
SET #database_name = '1000574';
EXEC sp_executesql N'USE [#database_name];
SELECT TOP 10 temperature_1, UTC FROM dbo.Data;',
N'#database_name nvarchar(100)',
#database_name = #database_name
What I get is the following error message:
*Msg 911, Level 16, State 1, Line 1
Database '#database_name' does not exist. Make sure that the name is entered correctly.*
Where is the error?
Thanks, Petr
You cannot parameterize the argument to use: it expects a literal. Construct the query dynamically instead:
declare #sql nvarchar(max)
set #sql = 'USE [' + #database_name + '];' +
'SELECT TOP 10 temperature_1, UTC FROM dbo.Data;';
exec (#sql)
One more way of doing this:
DECLARE #database_name NVARCHAR(100),#sp_executesql NVARCHAR(100),#sql_cmd NVARCHAR(512);
SET #database_name = N'1000574';
SET #sp_executesql = #database_name + N'.dbo.sp_executesql';
SET #sql_cmd = N'SELECT TOP 10 temperature_1, UTC FROM dbo.Data;';
EXEC #sp_executesql #sql_cmd;
Why not:
DECLARE #sql NVARCHAR(MAX), #database_name SYSNAME;
SET #database_name = N'1000574';
SET #sql = N'SELECT TOP 10 temperature_1, UTC FROM '
+ QUOTENAME(#database_name) + '.dbo.Data;';
EXEC sp_executesql #sql;
Also why is there no order by to tell SQL Server what you mean by TOP?
Or simply use the qualified name, as documented here:
SELECT TOP 10 1000574..temperature_1, UTC FROM dbo.Data
The query can, of course, be built dynamically.