'SQL, how to use Dynamic Condition logics?
When you need Dynamic WHERE Clause I can use;
CREATE PROCEDURE [dbo].[sp_sel_Articles]
@articleId INT = NULL
, @title NVARCHAR(250) = NULL
, @accessLevelId INT = NULL
AS
BEGIN
SELECT *
FROM table_Articles Art
WHERE
(Art.ArticleId = @articleId OR @articleId IS NULL)
AND (Art.Title LIKE '%' + @title + '%' OR @title IS NULL)
AND (Art.AccessLevelId = @accessLevelId OR @accessLevelId IS NULL)
END
So, I am able to invoke this procedure -for example- ONLY by ArticleId
EXEC [sp_sel_Articles] @articleId = 3
But, sometimes I'll need to invoke by AccessLevelId and sometimes NOT by an EXACT VALUE. For example, I'll need MORE THAN the given accesslevelId or LESS THAN.
Current procedure can ONLY handle the EXACT value by using
Art.AccessLevelId = @accessLevelId
Could also be possible to give the CONDITION type as well as the value into the procedure? It may seem very odd in this example but please just bear with me:
CREATE PROCEDURE [dbo].[sp_sel_Articles]
@articleId INT = NULL
, @title NVARCHAR(250) = NULL
, @accessLevelId INT = NULL
, **@accessLevelIdCondition**
AS
BEGIN
SELECT *
FROM table_Articles Art
WHERE
(Art.ArticleId = @articleId OR @articleId IS NULL)
AND (Art.Title LIKE '%' + @title + '%' OR @title IS NULL)
AND (Art.AccessLevelId **@accessLevelIdCondition** @accessLevelId OR @accessLevelId IS NULL)
END
Perhaps an Function can be used, I don't know. Since, there will be at least 20 Procedure that will require this flexibility, I'll need a better, more global solution as much as possible rather than writing IF ELSE condition in every procedure.
Thanks in advance,
Solution 1:[1]
Read this www.sommarskog.se/dynamic_sql.html before applying
CREATE PROCEDURE [dbo].[sp_sel_Articles]
@articleId INT = NULL
, @title NVARCHAR(250) = NULL
, @accessLevelId INT = NULL
, @accessLevelIdCondition varchar(100)
AS
BEGIN
DECLARE @SQL varchar(8000)
SET @SQL='
SELECT *
FROM table_Articles Art
WHERE
(Art.ArticleId = '+cast(@articleId as varchar(100))+' OR '+cast(@articleId as varchar(100))+'IS NULL)
AND (Art.Title LIKE ''%'' + @title + ''%'' OR @title IS NULL)
AND (Art.AccessLevelId '+@accessLevelIdCondition+ cast(@accessLevelId as varchar(100))+' OR '+cast(@accessLevelId as varchar(100))+' IS NULL) '
EXEC(@sql)
END
Solution 2:[2]
You'd probably need to use dynamic SQL to pass in the operator. Or you could pass in two values, e.g.
@MinAccessLevelID INT,
@MaxAccessLevelID INT
...
WHERE (
(@MinAccessLevelID IS NULL AND @MaxAccessLevelID IS NULL)
OR
(AccessLevelID >= @MinAccessLevelID AND AccessLevelID <= @MaxAccessLevelID)
)
When you want exact (e.g. only 3), just pass 3 into both values. When you want anything above 3, pass 20000000000 into the @Max param, or 0 if you want everything below 3.
But you'll find as these permutations get more complex, you are going to be better off just using dynamic SQL (and with optimize for ad hoc workloads set, this will be better for plan cache reuse and thwarting parameter sniffing as well).
Solution 3:[3]
You can always make a dynamic query with just making a querystring
execute ('select count(*) from table' )
So with the params entered in your stored procedure, you can also form up a querystring which you can execute.
Solution 4:[4]
You could use a case statement - it can look a little funny if not formatted correctly but you can try something like:
SELECT Columns FROM SomeTable
WHERE 1 = CASE
WHEN @SomeOption = '<>' AND SomeValue >= @SomeMinParam AND SomeValue <= SomeMaxParam THEN 1
WHEN @SomeOption '=' AND SomeValue = @SomeMinParam THEN 1
ELSE 0
END
(though as Aaron pointed out - the <> you pass in doesn't really reflect the comparison operators in the statement - change this to something meaningful :))
in your case:
CREATE PROCEDURE [dbo].[sp_sel_Articles]
@articleId INT = NULL,
@title NVARCHAR(250) = NULL,
@MinaccessLevelId INT = NULL,
@MaxaccessLevelId INT = NULL,
@accessType varchar(5) = '<>'
AS
BEGIN
SELECT *
FROM table_Articles Art
WHERE
(Art.ArticleId = @articleId OR @articleId IS NULL)
AND (Art.Title LIKE '%' + @title + '%' OR @title IS NULL)
AND 1 = CASE
WHEN @accessType = '<>' AND (Art.AccessLevelId = @MinaccessLevelId OR @accessLevelId IS NULL) THEN 1
WHEN @accessType = '=' AND (Art.AccessLevelId >= @MinaccessLevelId OR Art.AccessLevelId <= @MaxaccessLevelId) THEN 1
ELSE 0
END
END
Maybe use a bit @CompareAccessLevelToMin instead of a varchar() for the @accessType param. Still has the trouble of not telling you what setting it to 'false' means though.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Himanshu Jansari |
| Solution 2 | |
| Solution 3 | Rolfvm |
| Solution 4 |
