SQL EXEC(#QUERY) does not have access to global variables - sql-server

I am writing a query which need to append the database name dynamically.
The query gets values from a table (in another database) , and I would interested to know if there is a better approach/way.
So my query looks like following :
DECLARE #TagNames AS VARCHAR(MAX) --Probably this is not a Global variable
DECLARE #QUERY AS VARCHAR(MAX)
SET #QUERY ='SELECT #TagNames = coalesce( #TagNames + '','','''') +
fldTagName FROM '+ dbo.fnGetZiConfigValue('KEYNAME')+'.dbo.tblTags Where
fldInterpolate = 1 AND fldUnitID = 13'
EXEC (#QUERY)
When I execute this , I get an error,
Must declare the scalar variable "#TagNames".

It is for sure not a global variable. You have to "declare" it inside dynamic SQL and return outside just like you'd do this with stored procedure.
DECLARE #TagNames AS VARCHAR(MAX) --Probably this is not a Global variable
DECLARE #QUERY AS NVARCHAR(MAX)
SET #QUERY = N'SELECT #TagNames = coalesce( #TagNames + '','','''') +
fldTagName FROM '+ QUOTENAME(dbo.fnGetZiConfigValue('KEYNAME'))+'.dbo.tblTags Where
fldInterpolate = 1 AND fldUnitID = 13'
exec sp_executesql #QUERY, N'#TagNames VARCHAR(MAX) OUTPUT', #TagNames OUTPUT
for sp_executesql dynamic sql and it's params declaration must be NVarchar.

Related

Execute dynamic query that executes another stored procedure

I have a query like this:
DECLARE #TaskId UNIQUEIDENTIFIER
DECLARE #TaskName VARCHAR(255) = 'MasterSet'
DECLARE #sql NVARCHAR(MAX)
DECLARE #StartingDateTask DATETIME2 = (SELECT TOP 1 [Date]
FROM [TaskStatusAudit]
WHERE [TaskId] = 'A1FDFC16-904D-4560-B19D-5E7D4FEB1C2B'
AND [TaskStatusName] = 'IN-PROGRESS')
DECLARE #EndingDateTask DATETIME2 = (SELECT TOP 1 [Date]
FROM [TaskStatusAudit]
WHERE [TaskId] = 'A1FDFC16-904D-4560-B19D-5E7D4FEB1C2B'
AND [TaskStatusName] = 'COMPLETED')
SET #sql = N'SELECT dbo.TotalMinuteRange(#StartingDateTask,#EndingDateTask) as ' + quotename(#TaskName) + N''
EXEC sp_executesql #sql
Problem is when I execute it I get this error:
Must declare the scalar variable "#StartingDateTask".
Any ideas why I getting that if I declare my variable correctly? Regards
When executing sp_executesql you need to declare and pass the variables to sp_executesql something like..
SET #sql = N'SELECT dbo.TotalMinuteRange(#StartingDateTask,#EndingDateTask) as '
+ quotename(#TaskName) + N''
exec sp_executesql #sql
, N'#StartingDateTask DATETIME2 , #EndingDateTask DATETIME2'
, #StartingDateTask
, #EndingDateTask
Second parameter is the variable declaration parameter followed by the actual variables separately.
You can also do like taking a parameters variable and assign it.
declare #params nvarchar(100)
set #params='#StartingDateTask DATETIME2,#EndingDateTask DATETIME2'
SET #sql = N'SELECT dbo.TotalMinuteRange(#StartingDateTask,#EndingDateTask) as '
+ quotename(#TaskName) + N''
exec sp_executesql #sql
, #params
, #StartingDateTask
, #EndingDateTask

SQL set variable as the result of a query in a stored procedure

I am creating a stored procedure with a cursor and I need to store the quantity of a column into a variable.
SQL says that it cannot convert nvarchar into int
I have tried to use CONVERT, CAST and EXEC but couldn't make it work.
How can I solve this ?
DECLARE #FieldName nvarchar(255);
DECLARE #RequestCode Nvarchar(50);
DECLARE #ReqSQLQuantity nvarchar(max);
DECLARE #Quantity int;
Select #ReqSQLQuantity = concat('select count(*) From (select distinct [', #FieldName , '] from [report_' , #RequestCode , ']) as number')
Set #Quantity = (#ReqSQLQuantity)
Select #Quantity
Another a bit safer option would be to use sp_executesql stored procedure something like this....
DECLARE #FieldName nvarchar(255);
DECLARE #RequestCode Nvarchar(50);
DECLARE #ReqSQLQuantity nvarchar(max);
DECLARE #Quantity int, #tableName SYSNAME;
SET #tableName = N'report_' + #RequestCode
Select #ReqSQLQuantity = N' select #Quantity = count(*) '
+ N' From (select distinct ' + QUOTENAME(#FieldName)
+ N' from ' + QUOTENAME(#tableName) + N' ) as number'
Exec sp_executesql #ReqSQLQuantity
,N'#Quantity INT OUTPUT'
,#Quantity OUTPUT
Select #Quantity
Set #Quantite = (#ReqSQLQuantite)
This is not an evaluation, is an implicit conversion. From NVARCHAR to INT, and you are trying to convert a SQL query text into an INT, hence the error.
Also, you are assuming that results are the same as return values and can be assigned to variables. This is incorrect, SQL execution results are sent to the client. To capture a result, you must use a SELECT assign: SELECT #value =.... Trying to run #variable = EXEC(...) is not the same thing. Think at SELECT results the same way as a print in an app: a print sends some text to the console. If you run some pseudo-code like x = print('foo') then 'foo' was sent to the console, and x contains the value returned by print (whatever that is). Same with this pseudo-SQL, #x = EXEC('SELECT foo') will send foo to the client, and #x will some numeric value which is the value returned by EXEC (in a correct example one would have to use explicit RETURN statement to set it).
Overall, the code as posted has absolutely no need for capturing the value and returning it, it can simply execute the dynamic SQL and let the result be returned to client:
SET #sql = concat(N'SELECT count(*) FROM (SELECT ... FROM ...)');
exec sp_executesql #sql;

Dynamic Column Name in SQL in Update statement

DECLARE #sql NVARCHAR(max)
DECLARE #ParmDefinition NVARCHAR(500)
SET #sql = 'UPDATE [Table1] SET [Table1].[#columnName] = TEST';
SET #ParmDefinition = N'#columnName NVARCHAR(50)';
EXEC sp_executesql #sql, #ParmDefinition, #columnName = N'MyColumn';
When I run the above query, I get Invalid column name '#columnName'.. Clearly, the column name is not being replaced when the query is run.
In reality, my #sql variable is much larger and I have many columns I wish to update, thus I would like to avoid doing SET SQL = for all enumerations of the column name.
I'd like to declare the sql string once, and invoke the query with different values. e.g.:
EXEC sp_executesql #sql, #ParmDefinition, #columnName = N'MyColumn';
EXEC sp_executesql #sql, #ParmDefinition, #columnName = N'AnotherColumn';
EXEC sp_executesql #sql, #ParmDefinition, #columnName = N'YetAnotherColumn';
-- And so on
Is something like this possible?
Yes, you have to concatenate the variable outside the string. In other words:
SET #sql = 'UPDATE [Table1] SET [Table1].[' + #columnName + '] = t1.Value ' +
EDIT: Another solution we have used is to replace tokens in the base sql to construct a new sql variable for execution.
DECLARE #sql nvarchar(max) = 'SELECT #ColumnName FROM #TableName';
DECLARE #sql2 nvarchar(max) = REPLACE(REPLACE(#sql,'#ColumnName',#ColumnNameVariable),'#TableName',#TableNameVariable)
EXEC (#sql2)
...Some code that changes the values of #ColumnNameVariable and #TableNameVariable...
DECLARE #sql2 nvarchar(max) = REPLACE(REPLACE(#sql,'#ColumnName',#ColumnNameVariable),'#TableName',#TableNameVariable)
EXEC (#sql2)
And you'll notice that the Declaration and Exec of SQL2 are exactly the same lines in both cases. This lends itself to use in a LOOP if that is applicable. (Except that you wouldn't DECLARE #Sql2 in the loop...just populate/re-populate it).
First of all, kudos for trying to parameterize your dsql using sp_executesql. The problem is, you can only parameterize something you could put into a variable in the first place such as in a search predicate or select list.
However it's still possible; just concatenate the column name with your DSQL string, wrapping it with the quotename function
set #sql = 'update table1 set table1.' + quotename(#ColumnName) + ' = ...

Can I specify a database name dynamically in a trigger?

I need to get data from a table in a database who's database name will be determined as a variable during a trigger. I then, knowing this variable need to get a seqno from a table in the determined database for a item which was also determined as a variable during a trigger.
I am trying this route as I assume I need to build the SQL statement before I set it to a variable.
This is not working and I need to know the best way on how I can do this:
DECLARE #SU_SEQNO INTEGER, #SU_NAME VARCHAR(50), #SU_OWNER VARCHAR(15), #SUD_SEQNO INTEGER, #SQL NVARCHAR(500)
SET #SU_OWNER = 'XXX'
SET #SU_NAME = '1ABC234'
SET #SQL ='SELECT #SUD_SEQNO=SEQNO FROM ' + (#SU_OWNER) + '.SU_MAIN
WHERE UNITNAME= ' + #SU_NAME
SET #SUD_SEQNO = (EXECUTE (#SQL))
Thanks alot for any help with this
From: Get result from dynamic SQL in stored procedure
SET #SQL = N'SELECT DISTINCT #FiscalYear = FiscalYear FROM ' + #DataSource;
EXEC sp_executesql #SQL, N'#FiscalYear INT OUTPUT', #FiscalYear OUTPUT;
PRINT #FiscalYear;
I'd re-engineer to use the sp_executesql method as shown above. That should do the trick.
I have amended the code, and it works
declare #su_owner varchar(15) = 'DBTEST'
declare #SU_SEQNO INTEGER=1, #SUD_SEQNO INTEGER=0, #SQL NVARCHAR(500)
DECLARE #ParmDefinition NVARCHAR(500), #SU_NAME_INPUT VARCHAR(50)='SU123'
SET #SU_NAME_INPUT = (SELECT UNITNAME FROM SU_MAIN WHERE SEQNO=#SU_SEQNO)
SET #SU_NAME = (SELECT UNITNAME FROM SU_MAIN WHERE SEQNO=#SU_SEQNO)
SET #SQL = N'SELECT #sud_seqnoOUT=MAX(SEQNO) FROM ' + quotename(#su_owner) + '.[dbo].[SU_MAIN] WHERE UNITNAME]=#SU_NAME_INPUT' ;
SET #ParmDefinition = N'#SU_NAME_INPUT VARCHAR(50),#sud_seqnoOUT INT OUTPUT'
EXEC sp_executesql #SQL,#ParmDefinition,#SU_NAME_INPUT = #SU_NAME,
#sud_seqnoOUT = #SUD_SEQNO OUTPUT

SQL Server variable columns name?

I am wondering why I cannot use variable column name like that:
declare #a as varchar;
set #a='TEST'
select #a from x;
Thank you
You can't do it because SQL is compiled before it knows what the value of #a is (I'm assuming in reality you would want #a to be some parameter and not hard coded like in your example).
Instead you can do this:
declare #a as varchar;
set #a='TEST'
declare #sql nvarchar(max)
set #sql = 'select [' + replace(#a, '''', '''''') + '] from x'
exec sp_executesql #sql
But be careful, this is a security vulnerability (sql-injection attacks) so shouldn't be done if you can't trust or well clean #a.
Because it is not allowed.
Insted of this you could use dynamic sql query:
declare #a as varchar;
set #a='TEST'
exec ('select ' + #a + ' from x')
Because the column names are resolved at compile time not at run time for the SQL statement.
use sp_executesql for this
Example
SET #SQLString = N'SELECT *
FROM table1
WHERE timet = #time and items in (#item)';
DECLARE #SQLString nvarchar(500);
DECLARE #ParmDefinition nvarchar(500);
SET #ParmDefinition = N'#time timestamp,
#item varchar(max) ';
EXECUTE sp_executesql
#SQLString
,#ParmDefinition
,#time = '2010-04-26 17:15:05.667'
,#item = '''Item1'',''Item2'',''Item3'',''Item4'''
;
If there are not too many columns to chose, how about a CASE WHEN statement?
DECLARE #ColName VARCHAR(50) = 'Test1'
SELECT [Foo], [Bar],
CASE
WHEN #ColName = 'Test1' THEN [Test1]
WHEN #ColName = 'Test2' THEN [Test2]
WHEN #ColName = 'Test3' THEN [Test3]
WHEN #ColName = 'Test4' THEN [Test4]
ELSE [TestDefault]
END [TestResult]
FROM [TableName];
This avoids using any EXEC.

Resources