This is my first project in Dynamic SQL.
When i run the below query. I'm getting an error:
Must declare scalar variable "
Though i declared the variable #lcrcolumn_total upfront.
EXECUTE (' UPDATE facetswrk.dbo.ODS_SUBSC_PREM_REPORT ' + ' SET ' + #lcrcolumn_name + ' = #lcrcolumn_total')
Thanks in advance!!!
You need to pass variable to dynamic SQL:
DECLARE #sql NVARCHAR(MAX) =
'UPDATE facetswrk.dbo.ODS_SUBSC_PREM_REPORT
SET #lcrcolumn_name = #lcrcolumn_total'
-- WHERE = ?; -- are you sure you want to update all rows
SET #sql = REPLACE(#sql, '#lcrcolumn_name', QUOTENAME(#lcrcolumn_name));
EXEC dbo.sp_executesql
#sql,
N'#lcrcolumn_total INT', -- set type of #lcorumn_total
#lcrcolumn_total;
LiveDemo
Remarks:
Add WHERE condition otherwise you will update all rows
Use sp_executesql instead of EXEC
Pass variable #lcrcolumn_total with correct datatype
Use QUOTENAME to avoid SQL Injection, when replacing column_name
If you are learning dynamic SQL, then just learn to use sp_executesql, because it is the best way to execute such statement. It allows you to pass arguments in and out. This is important because the "exec" statement does not share variables with the outer context.
The code would look more like this:
DECLARE #sql nvarchar(max);
SET #sql = '
UPDATE facetswrk.dbo.ODS_SUBSC_PREM_REPORT
SET #lcrcolumn_name = #lcrcolumn_total
';
SET #sql = REPLACE(#sql, '#lcrcolumn_name', #lcrcolumn_name);
EXEC sp_executesql #sql, N'#lcrcolumn_total int', #lcrcolumn_total = #lcrcolumn_total;
Note that you cannot pass column and table names as parameters, so these are handled using REPLACE().
Related
How to use numeric variable in sql statement inside procedure?
This is my try:
create procedure ##sp_check (
#tolerance numeric
)
AS
Declare #SQL nvarchar(max)
SET #SQL = '
SELECT
*
FROM
a
WHERE
value > #tolerance
'
exec sp_executesql #SQL
go
exec ##sp_check 1
and the ERROR: Must declare the scalar variable "#tolerance".
I think this is because the variable is invisible between ' and '
so I can do it by declaring #tolerance as varchar and in sql statement converting it into numeric but its a bit confusing...
You have to explicitly pass the variable into the sp_executesql (it runs in a different scope that doesn't have access to variables declared in the calling scope).
SET #SQL = 'SELECT * FROM a WHERE value > #t'
exec sp_executesql #SQL, N'#t numeric', #t=#tolerance
I wonder why you're using Dynamic SQL here.. try this:
create procedure ##sp_check (
#tolerance numeric
)
AS
SELECT * FROM a WHERE value > #tolerance
go
exec ##sp_check 1
You can't reference a variable declared outside of a dynamic statement inside it. Thus, the correct syntax would be:
CREATE PROC ##sp_check (#tolerance numeric)
AS
DECLARE #SQL nvarchar(max)
SET #SQL = N'SELECT *' + NCHAR(10) +
N'FROM a' + NCHAR(10) +
N'WHERE value > #dtolerance;';
PRINT #SQL;
EXEC sp_executesql #SQL, N'#dtolerance numeric',#dtolerance = #tolerance;
go
exec ##sp_check 1;
I suggest, however, you declare your scale and precision on your numeric, it's very bad practice to not do so.
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 have been using dynamic queries in sql-server like:
declare #sql nvarchar (1000),#condition nvarchar(100)='';
set #sql=N'select * from tablename where (0=0)'+#condition+'';
exec(#sql)
by this i was able to get my result whether the #condition has any value or not.
But i got to know that sp_executesql is better then exec as it promote query plan reuse.
So, i tried my query with `sp_executesql,
set #sql =N'select * from dbo.testclient where (0=0) #condition'
exec sp_executesql #sql,N'#condition nvarchar(100)',#condition
but it failed with an error as
Incorrect syntax near '#condition'.
My problem is how can i make the above query to work with sp_executesql where the parameter #condition can be a condition or blank (' ') and what am i doing wrong.
When you use variables such as #condition in sp_executesql, it does not simply replace your variable in the sql string with the content of the variable.
What happens is that the variable is bound to the supplied value and the sql statement is untouched.
This all means that you need to create a full sql statement which uses variables if you want to leverage query plan reuse.
For example:
SET #byIDsql = 'select * from tableName where (0=0) and Id = #Id'
SET #byNameSQL = 'select * from tableName where (0=0) and FirstName = #FirstName'
Then you can use sp_executesql to supply the value of #id and #firstName and get query plan reuse.
exec sp_executesql #byIDsql, N'#ID INT', 15
Tested code,
DECLARE #Sql nvarchar(MAX)='',
#paramlist nvarchar(4000),
#id int=3
set #paramlist = N' #id1 int'
set #Sql='select * from test where id=#id1'
exec sp_executesql #Sql,#paramlist,#id
select #Sql
I am calling a stored procedure with OUTPUT parameter using dynamic SQL.
set #cSql='EXEC '+#cName+'.dbo.uspNDateGet '''
+convert(varchar(10),#dtAsOn,102)+''','''
+#cBr+''','''
+#cLCode+''','''
+convert(varchar(10),#dtNDate,102)+''' OUTPUT'
exec(#cSql)
On executing the script, I get following error.
Cannot use the OUTPUT option when passing a constant to a stored procedure.
Without using dynamic SQL, the script gives me the required result.
EXEC uspNDateGet #dtAsOn,#cBr,#cLCode,#dtNDate OUTPUT
You need to pass parameters from outside into the inside query.
Here I show you the generic case:
declare #sql nvarchar(max);
declare #Out1 nvarchar(10);
declare #Out2 nvarchar(10);
declare #ParmDef nvarchar(max);
set #ParmDef =
' #Parm_Out1 nvarchar(10) '
+ ', #Parm_Out2 nvarchar(10) ' ;
set #sql='EXEC myproc #Parm_Out1 OUTPUT, #Parm_Out2 OUTPUT '
exec sp_executesql #sql, #ParmDef, #Parm_Out1 = #Out1, #Parm_Out2 = #Out2
In this particular instance, you don't actually need dynamic SQL at all.
You can parameterize the name of the stored procedure being called with EXEC, and pass the parameters normally. This is documented here:
DECLARE #dtNDate datetime, #procName nvarchar(386);
SET #ProcName = #cName + N'.dbo.uspNDateGet';
EXEC #procName
#dtAsOn = #dtAsOn,
#cBr = #cBr,
#cLCode = #cLCode,
#dtNDate = #dtNDate OUTPUT
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) + ' = ...