I have a dynamic query where in I want to add the value of a local variable to every row of the result set. I have a simplified version of the example below.
While this query works fine:
DECLARE #purchaseDate AS DATE
SET #purchaseDate = '12/23/2020'
SELECT Name, #purchaseDate
FROM Fruits
The similar one in dynamic SQL does not work:
DECLARE #query AS NVARCHAR(MAX),
#purchaseDate AS DATE
SET #purchaseDate = '12/23/2020'
SET #query = 'SELECT Name, #purchaseDate FROM Fruits'
EXEC sp_executesql #query
I get the error
Must declare the scalar variable "#purchaseDate".
So I assumed I might need to declare my purchaseDate inside the query as the dynamic SQL query cannot access the variable. So I tried this:
DECLARE #query AS NVARCHAR(MAX)
SET #query = 'DECLARE #purchaseDate AS DATE' +
'SET #purchaseDate = ' + '12/23/2020 ' +
'SELECT Name, #purchaseDate FROM Fruits'
EXEC sp_executesql #query
But I get the same error message.
How do I go about fixing it?
You can't reference a variable declared outside of a dynamic statement inside a dynamic statement. You need to add the declaration in the parameters and pass the value:
DECLARE #query AS nvarchar(MAX),
#purchaseDate AS date;
SET #purchaseDate = '20201223';
SET #query = 'SELECT Name,#purchaseDate FROM Fruits;';
EXEC sp_executesql #query, N'#purchaseDate date', #purchaseDate = #purchaseDate;
The recommended way to put a variable in dynamic query with a prepared statement:
if OBJECT_ID('Fruits') is not null drop table Fruits
create table Fruits(
name varchar(100)
)
insert into Fruits
values('apple')
DECLARE #purchaseDate AS DATE
SET #purchaseDate = '12/23/2020'
DECLARE #P1 int;
EXEC sp_prepare #P1 output,
N'#purchaseDate date',
N'SELECT Name, #purchaseDate FROM Fruits';
EXEC sp_execute #P1, #purchaseDate;
EXEC sp_unprepare #P1;
Take a look at the Microsoft Doku.
If you are lazy or just want a simple adhoc solution (not recommended):
if OBJECT_ID('Fruits') is not null drop table Fruits
create table Fruits(
name varchar(100)
)
insert into Fruits
values('apple')
DECLARE #query AS NVARCHAR(MAX),
#purchaseDate AS DATE
SET #purchaseDate = '12/23/2020'
set #query = 'SELECT Name, ' + convert(nvarchar(10),#purchaseDate, 12) + ' FROM Fruits'
exec sp_executesql #query
Related
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.
I declared a variable #Obj and assign a complete table name 'ODS..Account' to it.
DECLARE #Obj VARCHAR(255)
Then I used it in a query immediately after FROM Clause. I perceive it is just a string, unable to act as a table object. So how can I fix the code to get it works? Cheers
INSERT Control.dbo.Consistency_Check
(Table_Name
,Schema_Name
,Id
,Incremental_DateTime_Column
)
SELECT
#Tab
,'ODS'
,Id
,SystemModstamp
FROM
#Obj )
You can use a local variable as a scalar value, not as a function. To do this, you need dynamic SQL:
declare #sql varchar(max);
select #sql = '
INSERT Control.dbo.Consistency_Check(Table_Name, Schema_Name, Id, Incremental_DateTime_Column)
SELECT ''#Tab'', 'ODS', Id, SystemModstamp
FROM #Tab
';
select #sql = replace(#sql, '#tab', #tab);
exec sp_executesql #sql;
Slightly different way of doing it with dynamic SQL:
DECLARE #Obj VARCHAR(255) = 'dbo.table'
DECLARE #SQL NVARCHAR(MAX) = ''
SET #SQL = #SQL +
'INSERT Control.dbo.Consistency_Check
(Table_Name
,Schema_Name
,Id
,Incremental_DateTime_Column
)
SELECT
#Tab
,''ODS''
,Id
,SystemModstamp
FROM
' + #Obj + ''
EXEC (#SQL)
You cannot. You probably want to use dynamic query. i.e. workout the SQL query string into a variable and exec using sp_executesql.
You may use the same variable name in the dynamic SQL but I changed it to #p_Tab for the example.
DECLARE #Tab int = 3
DECLARE #SQLString nvarchar(500)
DECLARE #ParmDefinition nvarchar(500) = N'#p_Tab int';
Declare #TableName nvarchar(100) = 'ODS..Account'
/* Build the SQL string dynamicly.*/
SET #SQLString = N'INSERT Control.dbo.Consistency_Check
(Table_Name
,Schema_Name
,Id
,Incremental_DateTime_Column
)
SELECT
#p_Tab
,''ODS''
,Id
,SystemModstamp
FROM
'+ #TableName
EXECUTE sp_executesql #SQLString, #ParmDefinition,
#p_Tab = #Tab
Further reference: https://msdn.microsoft.com/en-us/library/ms188001.aspx
I just wanna do a simple query with grouping by "Age_Band".
And to set the parameter to help me change the grouping column easily.
But got an error:
"Each GROUP BY expression must contain at least one column that is not an outer reference. "
How did I make mistake on using the parameter? Thanks a lot
DECLARE #group nvarchar(50);
DECLARE #SQLString nvarchar(500);
DECLARE #ParmDefinition nvarchar(500);
SET #SQLString =
N'SELECT AVG(Loan_Amount) as Avg_Loan_Amount
,count(Loan_Amount)
,SUM(Loan_Amount) as Sum_Loan_Amount
,SQRT(SUM(Loan_Amount)) as Square_Loan_Amount
,Age_Band
,SUM(Interest_Paid_Amount) as Sum_Interest_Paid_Amount
,GETDATE()as today
FROM [dbo].[Loan]
group by #groupcol';
SET #ParmDefinition = N'#groupcol nvarchar(50)';
SET #group=N'Age_Band'
exec sp_executesql #SQLString, #ParmDefinition,
#groupcol = #group;
You cannot use parameter in the group by clause by you can build query string using parameter. Something like this.
DECLARE #group nvarchar(50) = N'Age_Band'; --set col name
DECLARE #SQLString nvarchar(500);
SET #SQLString =
N'SELECT AVG(Loan_Amount) as Avg_Loan_Amount
,count(Loan_Amount)
,SUM(Loan_Amount) as Sum_Loan_Amount
,SQRT(SUM(Loan_Amount)) as Square_Loan_Amount
,' + #group + '
,SUM(Interest_Paid_Amount) as Sum_Interest_Paid_Amount
,GETDATE()as today
FROM [dbo].[Loan]
group by ' + #group;
exec sp_executesql #SQLString;
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) + ' = ...
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