My requirement is to retrieve into a variable, the next value for a sequence, the name of which is derived from a string and a value.
When I run the following as a test using exec sp_executesql ... an error is reported:
Incorrect syntax near #sequenceName
What's wrong with my code?
DECLARE #nextSeqID varchar(10);
DECLARE #sql nvarchar(100);
DECLARE #eqtypeID int;
DECLARE #sequence nvarchar(50);
DECLARE #paramdef nvarchar(100);
SET #eqtypeID = 7;
SET #sequence = 'dbo.seq_eqtype_autoserno_' + CAST(#eqtypeID as nvarchar(8));
-- #sequence = dbo.seq_eqtype_autoserno_7
SET #sql = N'SELECT #nextSeqID_OUT = NEXT VALUE FOR #sequenceName';
-- #sql = SELECT #nextSeqID_OUT = NEXT VALUE FOR #sequenceName
SET #paramdef = N'#nextSeqID_OUT varchar(10) OUTPUT, #sequenceName nvarchar(50)';
-- #paramdef = #nextSeqID_OUT varchar(10) OUTPUT, #sequenceName nvarchar(50)
EXEC sp_executesql #sql, #paramdef, #sequenceName = #sequence, #nextSeqID_OUT = #nextSeqID OUTPUT;
/*
Msg 102, Level 15, State 1, Line 1
Incorrect syntax near '#sequenceName'.
*/
It is admirable and correct that you are using sp_executesql to pass dynamic things through variables. However, you can not do this with object names(like a sequence) and other stuff which are required by the query at runtime.
Remove #sequenceName from the parameters and the definition, and put it directly on the code. The correct way to do this to still be safe from injection is to use it within quotename, so whatever injection attack happens, it will be quoted, and thus safe:
SET #sql = N'SELECT #nextSeqID_OUT = NEXT VALUE FOR '+quotename(#sequenceName);
SET #paramdef = N'#nextSeqID_OUT varchar(10) OUTPUT';
EXEC sp_executesql #sql, #paramdef, #nextSeqID_OUT = #nextSeqID OUTPUT;
Through the exec in SQL Server, I want to retrieve the parameter's value dynamically. Please look into the below Example:
declare #p1 varchar(50) = '10'
declare #p2 varchar(50) = '20'
declare #p_nm varchar(50)
set #p_nm = '#p1' -- OR set #p_nm = '#p2'
declare #sql varchar(50) = 'select #p1'
declare #ans_val varchar(50)
set #ans_val = exec #sql
How could I get the result 10 if using set #p_nm = '#p1' and 20 if using set #p_nm = '#p2'?
In my case set #ans_val = exec #sql giving syntax error.
A variable only has scope within the batch it is inside. When you run dynamic SQL, the dynamic SQL has a different scope, so variables cannot be accessed if they are in a different one. For example, both of the following statements will generate an error:
DECLARE #SQL nvarchar(MAX) = N'SELECT #i;';
DECLARE #i int = 1;
EXEC (#SQL);
GO
DECLARE #SQL nvarchar(MAX) = N'DECLARE #i int = 1;';
EXEC (#SQL);
SELECT #i;
GO
If you are running dynamic SQL, you need to pass details of any variables as parameters, using sp_executesql. For example:
DECLARE #SQL nvarchar(MAX) = N'SELECT #n;';
DECLARE #i int = 1;
EXEC sp_executesql #SQL, N'#n int', #n = #i;
If you want the return scalar values to a variable, use an OUTPUT parameter (personally, I recommend against the syntax {Variable} = EXEC {Expression/Stored Procedure}, one reason being you are therefore limited to one scalar value).
So, again, as an example:
DECLARE #SQL nvarchar(MAX) = N'SET #a = #n + 1;';
DECLARE #i int = 1, #b int;
EXEC sp_executesql #SQL, N'#n int, #a int OUTPUT', #n = #i, #a = #b OUTPUT;
PRINT #b;
I'll do it like:
EXECUTE sp_executesql
N'SELECT #Var AS Result',
N'#Var VARCHAR(5)',
#Var = '12345';
Results:
+----+--------+
| | Result |
+----+--------+
| 1 | 12345 |
+----+--------+
Or
DECLARE #Out VARCHAR(5);
EXECUTE sp_executesql
N'SELECT #Out = #Var',
N'#Var VARCHAR(5) , #Out VARCHAR(5) OUTPUT',
#Var = '12345',
#Out = #Out OUTPUT;
SELECT #Out As Result;
Finally, please don't forgot to visit sp_executesql
I have a stored procedure that I'm working on and I'm trying to get it to return the results that are stored into a table variable that I created within the procedure. For some reason, its not returning anything when I execute the procedure. However, if I take the piece of code used to populate the table variable and take it out of the procedure it works...
The code is as follows:
Test Code
DECLARE #RC int
DECLARE #constraint_names nvarchar(max)
DECLARE #column_names nvarchar(max)
DECLARE #table_name nvarchar(max)
DECLARE #table_schema nvarchar(max)
DECLARE #database_name nvarchar(max)
DECLARE #debug int
DECLARE #ExistFlag int;
-- TODO: Set parameter values here.
SET #column_names = 'M2016_Object_ID int, Name varchar(50), a int, b'
SET #table_name = 'tblM2016_Objects';
SET #debug = 0;
SET #ExistFlag = 1;
DECLARE #existing_cols TABLE (Value nvarchar(max));
INSERT INTO #existing_cols EXEC spM2016_CheckColumnExistence_tblM2016
#column_names, #table_name, #database_name, #table_schema, 1, 0;
select * from #existing_cols
Results
Value
M2016_Object_ID int
Name varchar(50)
This is the expected result since I'm testing to see if M2016_Object_ID and Name column exist in the tblM2016_Objects table.
However, when I execute this inside the stored procedure I'm working on, it returns nothing.
Stored Procedure
ALTER PROCEDURE spM2016_ChangePKConstraints_tblM2016
-- Add the parameters for the stored procedure here
#constraint_names nvarchar(max) =
N'PK_tblM2016_Fields_M2016_Field_ID',
#column_names nvarchar(max) = N'M2016_Field_ID',
#table_name nvarchar(max) = N'tblM2016_Fields',
#table_schema nvarchar(max) = N'dbo',
#database_name nvarchar(max) = N'MET',
#debug int = 0
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
DECLARE #tbl_name nvarchar(max) = N'tblM2016_Fields',
#col_names nvarchar(max) = N'M2016_Field_ID',
#tbl_schema nvarchar(max) = N'dbo',
#db_name nvarchar(max) = N'MET',
#tbl_full_name nvarchar(max),
#tbl_short_name nvarchar(max),
#sql nvarchar(max),
#params nvarchar(max)
-- Insert statements for procedure here
SET #col_names = ISNULL(NULLIF(#column_names, ''), #col_names);
SET #tbl_name = quotename(ISNULL(NULLIF(#table_name, ''), #tbl_name));
SET #tbl_schema = quotename(ISNULL(NULLIF(#table_schema, ''), #tbl_schema));
SET #db_name = quotename(ISNULL(NULLIF(#database_name, ''), #db_name));
SET #tbl_full_name = #db_name + '.' + #tbl_schema + '.' + #tbl_name;
SET #tbl_short_name = #tbl_schema + '.' + #tbl_name;
DECLARE #existing_cols TABLE (Value nvarchar(max));
DECLARE #nonexisting_cols TABLE (Value nvarchar(max));
--INSERT INTO #split_columns
-- SELECT *
-- FROM
-- fnM2016_Split_String_Inline(#col_names, ',');
--IF (#debug = 1)
-- SELECT * FROM #split_columns;
IF NOT EXISTS (SELECT * FROM sys.objects WHERE type = 'PK' AND parent_object_id = OBJECT_ID(#tbl_full_name))
BEGIN
-- No PK constraint, check col_names to see which exist and don't exist
INSERT INTO #existing_cols EXEC spM2016_CheckColumnExistence_tblM2016 #col_names, #tbl_name, #db_name, #tbl_schema, 1, 0;
INSERT INTO #nonexisting_cols EXEC spM2016_CheckColumnExistence_tblM2016 #col_names, #tbl_name, #db_name, #tbl_schema, 0, 0;
SELECT * FROM #existing_cols;
SELECT * FROM #nonexisting_cols;
END
--ELSE
--BEGIN
--END
END
Results
These are the results after executing the procedure with the same parameter values in the test above:
EXECUTE #RC = [dbo].[spM2016_ChangePKConstraints_tblM2016]
#constraint_names
,#column_names
,#table_name
,#table_schema
,#database_name
,#debug;
Result
Value
The table purposely does not have a primary key. Proof:
In your procedure, change the name of the table in your Declare section.
This should fix the issue.
ALTER PROCEDURE spM2016_ChangePKConstraints_tblM2016
-- Add the parameters for the stored procedure here
#constraint_names nvarchar(max) =
N'PK_tblM2016_Fields_M2016_Field_ID',
#column_names nvarchar(max) = N'M2016_Field_ID',
#table_name nvarchar(max) = N'tblM2016_Objects',
#table_schema nvarchar(max) = N'dbo',
#database_name nvarchar(max) = N'MET',
#debug int = 0
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
DECLARE #tbl_name nvarchar(max) = N'tblM2016_Objects',
#col_names nvarchar(max) = N'M2016_Field_ID',
#tbl_schema nvarchar(max) = N'dbo',
#db_name nvarchar(max) = N'MET',
#tbl_full_name nvarchar(max),
#tbl_short_name nvarchar(max),
#sql nvarchar(max),
#params nvarchar(max)
I figured out the issue. My #table_name, #table_schema, and #database_name being passed into the spM2016_CheckColumnExistence_tblM2016 procedure inside the spM2016_ChangePKConstraints_tblM2016 were already escaped through the call to quotename(). Inside the spM2016_CheckColumnExistence_tblM2016, I also do a qutoename() of the parameters as a validation check against bad table, database, and schema names. That procedure code was left out and I apologize for that.
Essentially the problem area is here (with problem parameters highlighted with **)
Problem code in spM2016_ChangePKConstraints_tblM2016
INSERT INTO #existing_cols EXEC spM2016_CheckColumnExistence_tblM2016 #col_names, **#tbl_name**, **#db_name**, **#tbl_schema**, 1, 0;
Notice that those parameters being passed to spM2016_CheckColumnExistence_tblM2016 have already been escaped with quotename() above in the spM2016_ChangePKConstraints_tblM2016 procedure. Now, here is the missing key code in the spM2016_CheckColumnExistence_tblM2016 procedure:
Problem code in spM2016_CheckColumnExistence_tblM2016
ALTER PROCEDURE spM2016_CheckColumnExistence_tblM2016
-- Add the parameters for the stored procedure here
#column_names nvarchar(max) = N'M2016_Field_ID int',
#table_name nvarchar(max) = N'tblM2016_Fields',
#database_name nvarchar(max) = N'MET',
#table_schema nvarchar(max) = N'dbo',
#ExistFlag int = 1, -- Check for column existence in given table = 1
-- Check for column non-existence in given table = 0
#debug int = 0
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
DECLARE #tbl_name nvarchar(max) = N'tblM2016_Fields',
#col_names nvarchar(max) = N'M2016_Field_ID',
#tbl_schema nvarchar(max) = N'dbo',
#db_name nvarchar(max) = N'MET',
#tbl_full_name nvarchar(max),
#tbl_short_name nvarchar(max)
-- Insert statements for procedure here
***** PROBLEM STARTS HERE *****
SET #col_names = ISNULL(NULLIF(#column_names, ''), #col_names);
SET #tbl_name = quotename(ISNULL(NULLIF(#table_name, ''), #tbl_name));
SET #tbl_schema = quotename(ISNULL(NULLIF(#table_schema, ''), #tbl_schema));
SET #db_name = quotename(ISNULL(NULLIF(#database_name, ''), #db_name));
SET #tbl_full_name = #db_name + '.' + #tbl_schema + '.' + #tbl_name;
SET #tbl_short_name = #tbl_schema + '.' + #tbl_name;
I've highlighted the issue with ***** PROBLEM STARTS HERE ***** for clarity. As you can see, spM2016_CheckColumnExistence_tblM2016 also does a quotename call to make sure the #table_name, #table_schema, and #database_name passed to spM2016_CheckColumnExistence_tblM2016 has proper escaping as well. But, since I passed the already quotenamed #table_name, #table_schema, and #database_name from spM2016_ChangePKConstraints_tblM2016 into spM2016_CheckColumnExistence_tblM2016 as #col_names, #tbl_name, #db_name, #tbl_schema, they got double quoted and were thus unrecognizable when doing comparisons in the sys.object queries in spM2016_CheckColumnExistence_tblM2016 and so were returning bogus results.
The reason the test code worked is because the parameters were not already escaped when passed into the spM2016_CheckColumnExistence_tblM2016, thus causing the table variable to execute fine.
Turns out, it had nothing to do with the table variable :/
I'm trying to verify a job number exists on a linked server and get back a variable (#JobExists) indicating whether it does or not (1 for yes, 0 for no).
To do this I'm trying to use OPENQUERY along with sp_executesql as I have to pass in a parameter for the job number. I've tried the code below but get the error 'Procedure expects parameter '#statement' of type 'ntext/nchar/nvarchar'. For testing purposes I've declared and set the variable #JobNumber.
DECLARE #JobNumber as varchar(50)
SET #JobNumber = '2112111'
DECLARE #JobExists as BIT
DECLARE #JobCount as int
DECLARE #ParmDefinition as varchar(100)
DECLARE #sql as varchar(500)
SET #JobExists = 0
SET #ParmDefinition = N'#Result int output'
SET #sql = 'SELECT #Result = SELECT COUNT(*) FROM OPENQUERY(MYLINKEDSVR,''SELECT JOB_NUMBER FROM PROD.tbl1 WHERE JOB_NUMBER = ''''' + UPPER(RTRIM(LTRIM(#JobNumber))) + ''''''')'
exec sp_executesql #sql, #ParmDefinition, #Result = #JobCount output
IF #JobCount > 0
SET #JobExists = 1
SELECT #JobExists
I've read up sp_executesql here: http://technet.microsoft.com/en-us/library/ms188001.aspx
I've also done various searches but haven't come across any answers that work for me.
Is there something I'm missing?
The error message is clear: you must declare #sql as nvarchar and not as varchar.
The same for #ParamDefinition:
DECLARE #ParmDefinition as nvarchar(100)
DECLARE #sql as nvarchar(500)
I write a query in sql server i am getting value in a temporary variable, this is my query
declare #tacolumn1 varchar(50)='cargo'
declare #temp1 varchar(50)
declare #sql nvarchar(max)
print #tacolumn1
set #sql= ('select '+#tacolumn1+' from operatingincome where YEAR(createddate)= 2009')
exec sp_executesql #sql
print #temp1
from the above #sql has the value , now i want to save that value in #temp1 how can i do that one
sp_executesql procedure can have input and output parameters:
DECLARE #IntVariable int;
DECLARE #SQLString nvarchar(500);
DECLARE #ParmDefinition nvarchar(500);
DECLARE #max_title varchar(30);
SET #IntVariable = 197;
SET #SQLString = N'SELECT #max_titleOUT = max(JobTitle)
FROM AdventureWorks2008R2.HumanResources.Employee
WHERE BusinessEntityID = #level';
SET #ParmDefinition = N'#level tinyint, #max_titleOUT varchar(30) OUTPUT';
EXECUTE sp_executesql #SQLString, #ParmDefinition, #level = #IntVariable, #max_titleOUT=#max_title OUTPUT;
SELECT #max_title;