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
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.
It seems very simple solution, but I can't figure it out. Please help.
I have to call a stored proc with OUTPUT param from another stored proc. I think one of the issues is dynamic SQL, but I don't know how else to write it since #SQLWhere will change dynamically within C# code.
This is the proc being called from another proc:
ALTER PROCEDURE [dbo].[USP_RetrieveTotalRecord]
#SQLWhere AS NVARCHAR(1000),
#TotalRecordsFound as varchar(16) OUTPUT
AS
BEGIN
SET NOCOUNT ON;
DECLARE #SQL AS NVARCHAR(Max)
SET #SQL='Select #TotalRecordsFound = Count(Table_ID)
From TableName Where ' + #SQLWhere
EXEC(#SQL)
return
END
Here is how I am calling it from another proc:
Declare #TotalRec AS NVARCHAR(16);
Declare #SQLWhere AS NVARCHAR(1000);
SET #SQLWhere='Date Between ''12/13/2016'' AND ''12/14/2016'''
EXECUTE USP_RetrieveTotalRecord #SQLWhere, #TotalRec output;
Here is the error I am trying to resolve:
Msg 137, Level 15, State 1, Line 30
Must declare the scalar variable "#TotalRecordsFound".
Don't do what you are trying to do, Only pass values to stored procedure and then build the dynamic sql inside your procedure, something like ......
ALTER PROCEDURE [dbo].[USP_RetrieveTotalRecord]
#StartDate DATE = NULL
,#EndDate DATE = NULL
,#TotalRecordsFound INT OUTPUT
AS
BEGIN
SET NOCOUNT ON;
DECLARE #SQL NVARCHAR(Max);
SET #SQL = N' Select #TotalRecordsFound = Count(Table_ID) '
+ N' From TableName Where 1 =1 '
+ CASE WHEN #StartDate IS NOT NULL THEN
N' AND [Date] >= #StartDate ' ELSE N' ' END
+ CASE WHEN #EndDate IS NOT NULL THEN
N' AND [Date] <= #EndDate ' ELSE N' ' END
EXEC sp_executesql #SQL
,N'#StartDate DATE, #EndDate DATE, #TotalRecordsFound INT OUTPUT'
,#StartDate
,#EndDate
,#TotalRecordsFound OUTPUT
END
Now #EndDate and #StartDate variables are optional , depending on what variable values you pass procedure will build the dynamic sql accordingly and return the results.
Also using parameterised query with sp_executesql will protect you against a possible SQL-Injection attach and also your proc will benefit from parameterised execution plans.
Not sure why you have a 2nd SP, just use one like so:
Declare #TotalRec AS NVARCHAR(16);
Declare #SQLWhere AS NVARCHAR(1000);
SET #SQLWhere='Date Between ''12/13/2016'' AND ''12/14/2016'''
SET #SQL='Select #TotalRecordsFound = Count(Table_ID)
From TableName Where ' + #SQLWhere
EXEC(#SQL)
Or use date variables if that's all you are using for selection (no dynamic sql necessary) - unless this is just a simplified example
--- comment section is broken, so, in response to get a value out, use something like this:
Ok - the simplest way is to use sp_ExecuteSQL
Declare #result int
Declare #sql nvarchar(max)
SET #SQL = ' SELECT COUNT(*) FROM MyTable'
exec sp_ExecuteSQL #sql, N' #Result int Output', #Result output
select #result as MyCount
M. ali, thanks for your help, but we have all SELECT, WHERE, and GROUP by criteria being passed from the application after dynamic selections. I needed a quick fix.
Finally I was able to resolve the issue using a temp table. I know they are not recommended, but I tried using Common table expression, table variable, but the #TotalRecordsFound was not visible outside the dynamic SQL. Therefore, created temp table, Inserted data into it using dynamic SQL, and then joined it with the next select statement.
create procedure sp_First
#columnname varchar
AS
begin
select #columnname from Table_1
end
exec sp_First 'sname'
My requirement is to pass column names as input parameters.
I tried like that but it gave wrong output.
So Help me
You can do this in a couple of ways.
One, is to build up the query yourself and execute it.
SET #sql = 'SELECT ' + #columnName + ' FROM yourTable'
sp_executesql #sql
If you opt for that method, be very certain to santise your input. Even if you know your application will only give 'real' column names, what if some-one finds a crack in your security and is able to execute the SP directly? Then they can execute just about anything they like. With dynamic SQL, always, always, validate the parameters.
Alternatively, you can write a CASE statement...
SELECT
CASE #columnName
WHEN 'Col1' THEN Col1
WHEN 'Col2' THEN Col2
ELSE NULL
END as selectedColumn
FROM
yourTable
This is a bit more long winded, but a whole lot more secure.
No. That would just select the parameter value. You would need to use dynamic sql.
In your procedure you would have the following:
DECLARE #sql nvarchar(max) = 'SELECT ' + #columnname + ' FROM Table_1';
exec sp_executesql #sql, N''
Try using dynamic SQL:
create procedure sp_First #columnname varchar
AS
begin
declare #sql nvarchar(4000);
set #sql='select ['+#columnname+'] from Table_1';
exec sp_executesql #sql
end
go
exec sp_First 'sname'
go
This is not possible. Either use dynamic SQL (dangerous) or a gigantic case expression (slow).
Create PROCEDURE USP_S_NameAvilability
(#Value VARCHAR(50)=null,
#TableName VARCHAR(50)=null,
#ColumnName VARCHAR(50)=null)
AS
BEGIN
DECLARE #cmd AS NVARCHAR(max)
SET #Value = ''''+#Value+ ''''
SET #cmd = N'SELECT * FROM ' + #TableName + ' WHERE ' + #ColumnName + ' = ' + #Value
EXEC(#cmd)
END
As i have tried one the answer, it is getting executed successfully but while running its not giving correct output, the above works well
You can pass the column name but you cannot use it in a sql statemnt like
Select #Columnname From Table
One could build a dynamic sql string and execute it like EXEC (#SQL)
For more information see this answer on dynamic sql.
Dynamic SQL Pros and Cons
As mentioned by MatBailie
This is much more safe since it is not a dynamic query and ther are lesser chances of sql injection . I Added one situation where you even want the where clause to be dynamic . XX YY are Columns names
CREATE PROCEDURE [dbo].[DASH_getTP_under_TP]
(
#fromColumnName varchar(10) ,
#toColumnName varchar(10) ,
#ID varchar(10)
)
as
begin
-- this is the column required for where clause
declare #colname varchar(50)
set #colname=case #fromUserType
when 'XX' then 'XX'
when 'YY' then 'YY'
end
select SelectedColumnId from (
select
case #toColumnName
when 'XX' then tablename.XX
when 'YY' then tablename.YY
end as SelectedColumnId,
From tablename
where
(case #fromUserType
when 'XX' then XX
when 'YY' then YY
end)= ISNULL(#ID , #colname)
) as tbl1 group by SelectedColumnId
end
First Run;
CREATE PROCEDURE sp_First #columnname NVARCHAR(128)--128 = SQL Server Maximum Column Name Length
AS
BEGIN
DECLARE #query NVARCHAR(MAX)
SET #query = 'SELECT ' + #columnname + ' FROM Table_1'
EXEC(#query)
END
Second Run;
EXEC sp_First 'COLUMN_Name'
Please Try with this.
I hope it will work for you.
Create Procedure Test
(
#Table VARCHAR(500),
#Column VARCHAR(100),
#Value VARCHAR(300)
)
AS
BEGIN
DECLARE #sql nvarchar(1000)
SET #sql = 'SELECT * FROM ' + #Table + ' WHERE ' + #Column + ' = ' + #Value
--SELECT #sql
exec (#sql)
END
-----execution----
/** Exec Test Products,IsDeposit,1 **/
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().
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) + ' = ...