Dynamic SQL - EXEC(@SQL) versus EXEC SP_EXECUTESQL(@SQL)
sp_executesql
is more likely to promote query plan reuse. When using sp_executesql
, parameters are explicitly identified in the calling signature. This excellent article descibes this process.
The oft cited reference for many aspects of dynamic sql is Erland Sommarskog's must read: "The Curse and Blessings of Dynamic SQL".
Execute dynamic query with IF-ELSE statements and sp_executesql command
You have the right idea and are headed in the right direction. I figured I'd give you a hand to help you learn how I would approach this.
Here is my version of your stored procedure:
CREATE OR ALTER PROCEDURE [dbo].[usp_dynamicquery1] (
@TableName NVARCHAR(50),
@Field NVARCHAR(100) = NULL,
@Criterion NVARCHAR(100) = NULL,
@Parameter NVARCHAR(100) = NULL,
@All VARCHAR(2) = '-1'
)
AS
BEGIN
SET NOCOUNT ON;
DECLARE
@SQL NVARCHAR(MAX),
@SQL_WHERE NVARCHAR(MAX),
@ParameterDef NVARCHAR(500);
SET @ParameterDef = '@Parameter NVARCHAR(100)'
SET @SQL = 'SELECT * FROM ' + @TableName;
SET @SQL_WHERE = '';
/* BUILD THE WHERE CLAUSE IF @Field IS PRESENT */
IF NULLIF ( @Field, '' ) IS NOT NULL BEGIN
-- Field.
SET @SQL_WHERE = ' WHERE ' + @Field;
-- Field Comparison.
SET @SQL_WHERE += CASE @Criterion
WHEN 'greater than' THEN ' >'
WHEN 'greater than or equal' THEN ' >='
WHEN 'less than' THEN ' <'
WHEN 'less than or equal' THEN ' <='
WHEN 'not equal' THEN ' <>'
ELSE ' ='
END;
-- Field Parameter.
SET @SQL_WHERE += ' @Parameter';
-- Set @Parameter value.
SET @Parameter =
CASE WHEN NULLIF ( @Parameter, '' ) IS NOT NULL
THEN @Parameter
ELSE @All
END;
END
-- Finish SQL statement.
SET @SQL = @SQL + ISNULL ( @SQL_WHERE, '' ) + ';';
-- Execute the dynamic statement.
EXEC sp_executesql @SQL, @ParameterDef, @Parameter = @Parameter;
END
GO
A few quick things worth noting:
@TableName
must be required, otherwise, what do you intend to query?The
WHERE
clause is only constructed if@Field
is present.Shortened the
@ParameterDef
variable to include@Parameter
only. The rest of the t-sql is formed prior to callingsp_execute_sql
. I left @Parameter so that you wouldn't have to worry about single ticks around values, etc.Added
@All
as aDEFAULT
ed parameter (-1) to the SP to the value can be changed if wanted.
T sql - How to store results from a dynamic query using EXEC or EXECUTE sp_executesql
Here is a fully functional example of how you can do this. Notice this is using a parameterized where clause and quotename around the column name in the dynamic sql to prevent sql injection.
if OBJECT_ID('tempdb..#Agents') is not null
drop table #Agents
create table #Agents
(
AgentNumber char(10)
, Level1Agent char(10)
, Level2Agent char(10)
, Level3Agent char(10)
, Level4Agent char(10)
, Level5Agent char(10)
)
insert #Agents
select '1122334455', '1122334499', '1122334488', '1122334477', '1122334466', '1122334455'
DECLARE @level INT = 3;
DECLARE @agent CHAR(10) = 1122334455;
DECLARE @colname NVARCHAR(11) = CONCAT('Level',@level,'Agent');
declare @agentout char(10)
DECLARE @qry NVARCHAR(300) = 'SELECT @agent_out = ' + quotename(@colname) + ' FROM #Agents WHERE AgentNumber = @agentin';
EXECUTE sp_executesql @qry, N'@agentin char(10), @agent_out char(10) output', @agentin = @agent, @agent_out = @agentout output
select @agentout
Stored procedure EXEC vs sp_executesql difference?
Your sp_executesql SQL should probably be;
DECLARE @SQL as nvarchar(128) = 'select ' + @Columns + ' from ' +
@TableName + ' where Status=@eStatus'
This will allow you to call sp_executesql with @eStatus as a parameter instead of embedding it into the SQL. That will give the advantage that @eStatus can contain any characters and it will be properly escaped automatically by the database if required to be secure.
Contrast that to the SQL required for EXEC;
DECLARE @SQL as nvarchar(128) = 'select ' + @Columns + ' from ' +
@TableName + ' where Status=' + char(39) + @Status + char(39)
...where a char(39) embedded in @Status will make your SQL invalid and possibly create an SQL injection possibility. For example, if @Status is set to O'Reilly
, your resulting SQL would be;
select acol,bcol,ccol FROM myTable WHERE Status='O'Reilly'
exec sp_executesql @sql and exec (@sql) SQL Server
Because EXEC sp_executesql
will cache the query plan -- EXEC
will not. For more info, and a very good read, see:
- The Curse and Blessings of Dynamic SQL
Caching a query means that the logistics to the query are temporarily stored, and make running the query later on faster for it.
SQL Server use EXEC/sp_executesql or just plain sql in stored procedure?
See no reason to use dynamic SQL here. When you do need to use dynamic SQL, you should consider sp_executesql
higher in preference than EXEC()
. There are a variety of reasons, including:
sp_executesql
is more likely to reuse query plans (see Dynamic SQL - EXEC(@SQL) versus EXEC SP_EXECUTESQL(@SQL));it is much easier to pass strongly-typed parameters into
sp_executesql
(thwarting SQL injection better than concatenating a string); and,you can also get variables from within the dynamic SQL scope back out to the calling scope, for example:
DECLARE @i INT, @sql NVARCHAR(MAX), @dbname SYSNAME = N'model';
SET @sql = N'SELECT @i = COUNT(*) FROM '
+ @dbname + '.sys.tables;'
EXEC sp_executesql @sql, N'@i INT OUTPUT', @i = @i OUTPUT;
PRINT @i;
That's not a very useful example, but it is a common problem when executing dynamic strings. But more to the point, you should only be considering dynamic SQL when you have to, not as a first resort.
Does sp_executesql support multiple values in one parameter and return multiple records?
I was trying to retrive the rows whose id matches within the IN clause.
SET @INClauseIds='''' + replace(@Ids, ',', ''',''') + ''''
Above statement would convert the ID's ='1,2,3' to '1','2','3' which i can directly place in the IN clause.
SET @SQLText1 ='EXEC(''SELECT Name,SEOFriendlyName FROM SKU Where Id IN ( ''+ @Ids+'' ) )'
EXEC sp_executesql @SQLText1 ,N'@INClauseIds nvarchar(max)',@Ids=@INClauseIds
If you want to avoid the usage of Temp Table which would add extra caliculation time. you can you the above strategy to retrive n number of records. Safe with strongly coupled with sp_executesql and without any sql injection.
Related Topics
Hive Select Count(*) Non Null Returns Higher Value Than Select Count(*)
How to Force Postgres to Use a Particular Index
Spark SQL Window Function With Complex Condition
Db2 Comma Separated Output by Groups
How to Introduce Multiple Conditions in Like Operator
Is There Any Boolean Type in Oracle Databases
Or Is Not Supported With Case Statement in SQL Server
Pass Table as Parameter into SQL Server Udf
Accounting for Dst in Postgres, When Selecting Scheduled Items
Calculating Number of Full Months Between Two Dates in SQL
Operand Should Contain 1 Column - MySQL Not In
How to Do a Case Sensitive Search in Where Clause (I'M Using SQL Server)
Group by Behavior When No Aggregate Functions Are Present in the Select Clause
SQL to Json - Array of Objects to Array of Values in SQL 2016
Select Info from Table Where Row Has Max Date
How to Use Distinct and Order by in Same Select Statement