case statement in where clause condition in sql - sql

I have a stored procedure like this:
ALTER PROCEDURE [dbo].[hr_SearchVacanciesAdmin]
#SearchText NVARCHAR(50) = NULL,
#CompanyName NVARCHAR(100) = NULL,
#DutyStationID INT = NULL,
#VacancyCategoryIDs VARCHAR(1000) = NULL,
#Status INT,
#Language INT = 1
BEGIN
SELECT * from table
where
deleted = 0
and status = #status
and catname = &searchtext
..
..
..
END
I want to add case statement in where condition so that if #status = 4 then delete = is not checked. I tried following but not working
WHERE
1 = CASE
WHEN #Status = 4
THEN #status
WHEN dbo.hr_Vacancies.Deleted = 0
--Search Criteria
AND dbo.hr_Vacancies.Status = #Status
THEN #Status
END

You don't need a CASE statement to acheive this functionality. The following will do fine:
SELECT *
from table
where (deleted = 0 OR #status=4)
and status = #status
and catname = #searchtext
Note also that your code refers to &searchtext in the where clause, but this should be #searchtext as per this answer (ie prefix with # not &).

#RB is correct you don't need a case for that but you can use a case with this trick, which is useful in other scenarios as well:
Where deleted = case #status when 4 then deleted else 0 end

You may use this construct for the SELECT query to start with:
Select * from table
WHERE
((1 = CASE WHEN #Status = 4 THEN 1 ELSE 0 END)
OR
dbo.hr_Vacancies.Deleted = 0)
AND
dbo.hr_Vacancies.Status = #Status

Related

SQL Server - Using CASE statement

I have a SELECT statement with a WHERE clause that I want to dynamically change depending if a parameter is supplied or not.
I can't seem to understand how to use CASE statement in a WHERE clause but this is how I want it to look like using an IF statement.
DECLARE #Gender NVARCHAR(100) = NULL --this is an INPUT parameter and may or may not be NULL
DECLARE #Status NVARCHAR(100) = NULL --this is an INPUT parameter and may or may not be NULL
SELECT Name
FROM Person
WHERE
-- first WHERE clause
IF #Gender IS NULL
BEGIN
Gender IS NULL
END
ELSE
BEGIN
Gender = #Gender
END
AND
-- second WHERE clause
IF #Status IS NULL
BEGIN
Status IS NULL
END
ELSE
BEGIN
Status LIKE '%' + #Status + '%'
END
Is it possible to transform this code into a CASE statement?
I think you want:
select p.name
from person p
where ( (#gender is null and gender is null) or gender = #gender) and
( (#status is null and status is null) or status = #status);
Note that this does "null-matching". Often, people want to use NULL to select all records, not just the NULL ones. If that is what you intend, then:
select p.name
from person p
where ( #gender is null or gender = #gender) and
( #status is null or status = #status);
In either situation, case is not needed in the where. As a general rule, don't use case in where -- unless you really need it to control the order of evaluation of expressions.
You can do this:
SELECT Name
FROM Person
WHERE Gender = COALESCE(#gender, Gender)
AND (#Status is null or Status like '%' + #status + '%')
DECLARE #Gender NVARCHAR(100) = NULL --this is an INPUT parameter and may or may not be NULL
DECLARE #Status NVARCHAR(100) = NULL --this is an INPUT parameter and may or may not be NULL
SELECT Name
FROM Person
WHERE CASE WHEN #Gender IS NULL THEN 1
WHEN #Gender = ISNULL(Gender, '') THEN 1
ELSE 0
END = 1
AND CASE WHEN #Status IS NULL THEN 1
WHEN ISNULL(Status, '') LIKE '%' + #Status + '%' THEN 1
ELSE 0
END = 1

Search Accounts using SQL Server 2008 stored procedure?

I recently started developing/improving the way my search algorithm works. There is few different queries in the system that will use this approach. I'm new in stored procedures world and from what I researched they should improve security, performance and save some code redundancy. Here is example of what I have done in one of my stored procedures for Account search:
ALTER PROCEDURE [dbo].[SearchAccounts]
#Status INT = NULL,
#Type INT = NULL,
#FilterBy INT = NULL,
#Username VARCHAR(50) = NULL,
#Email VARCHAR(80) = NULL,
#LastName VARCHAR(50) = NULL,
#FirstName VARCHAR(50) = NULL,
#FullName VARCHAR(100) = NULL
WITH RECOMPILE
AS
DECLARE #AccountStatus INT = #Status;
DECLARE #AccountType INT = #Type;
DECLARE #AccountFilter INT = #FilterBy;
DECLARE #AccountUsername VARCHAR(50) = #Username;
DECLARE #AccountEmail VARCHAR(80) = #Email;
DECLARE #AccountLast VARCHAR(50) = #LastName;
DECLARE #AccountFirst VARCHAR(50) = #FirstName;
DECLARE #AccountFull VARCHAR(10) = #FullName;
SELECT
A.AccountID, A.FirstName, A.LastName, A.Middle, A.Email,
A.IsUser, A.ActiveUser, A.SystemAdmin, A.AccessType,
A.AccessLevel, A.UserName, A.IsStaff, A.ActiveStaff,
A.Position AS PositionCode, M.Name AS Position
FROM
Accounts AS A
LEFT OUTER JOIN
Master AS M ON M.Tblid = 'STAFF_POS'
AND M.Code = A.Position
WHERE
( -- If Account Type is 1 (User)
(#AccountType = 1 AND A.IsUser = 1)
OR -- If Account Type is 2 (Staff)
(#AccountType = 2 AND A.IsStaff = 1)
OR -- Or if Account type is 0 (All accounts)
(#AccountType = 0 AND 1 = 1)
)
AND
(
(-- If account type is user and Status is 1 (Active) or 0 (Inactive) or 2(pull active and inactive)
#AccountType = 1
AND
(#AccountStatus = 0 OR #AccountStatus = 1) AND ActiveUser = #AccountStatus)
OR
(#AccountType = 1 AND #AccountStatus = 2 AND 1 = 1
)
OR
(-- If account type is staff and Status is 1 (Active) or 0 (Inactive) or 2 (pull active and inactive)
#AccountType = 2
AND
(#AccountStatus = 0 OR #AccountStatus = 1) AND ActiveStaff = #AccountStatus)
OR
(#AccountType = 2 AND #AccountStatus = 2 AND 1 = 1
)
OR
(-- If account type is all pull all accounts active and inactive
(#AccountType != 1 AND #AccountType != 2 AND 1 = 1)
)
)
AND
( -- Filter is 1 then check user name.
(#AccountFilter = 1 AND A.UserName LIKE '%'+#AccountUsername+'%')
OR -- Filter is 2 then check email.
(#AccountFilter = 2 AND A.Email = #AccountEmail)
-- Here if filter is 3 then I should check First or Last or Full Name.
-- Still not sure what is the best approach to filter on the name fields.
)
ORDER BY
A.LastName, A.FirstName
As you can see query above has few different filters. First user can choose if they want to search Users, Staff or pull All account types. Then to choose if they want to pull Active, Inactive or all records. Last thing is to pick the filter. I give them an option to search Username, Email or Name. Each Account has First, Last name. What would be a good option to search for those names? Check just one full name or I have to check first and last separately? First and Last name allow this set of characters in the Accounts form : A-Z, space, dash, apostrophe, period, comma - no other special characters
There are probably dozens of ways to accomplish such a goal. But one way I would prefer (if I was using a query like the one presented) would be like this:
AND A.LNAME LIKE
CASE WHEN Len(#AccountLast) > 0 THEN
'%' + #AccountLast + '%'
ELSE '%'
END
AND A.FNAME LIKE
CASE WHEN Len(#AccountFirst) > 0 THEN
'%' + #AccountFirst + '%'
ELSE '%'
END

SQL OR CONDITION ON TWO DIFFERENT COLUMNS

I have two fields (#EmployeeId,#SSOId) out of which one value can come or both can come, but when i am applying OR condition it is not giving me correct output. What i am doing wrong ?
ALTER PROCEDURE [dbo].[usp_User_GetDetails] (
#UserId INT = NULL
,#ADSId NVARCHAR(32) = NULL
,#EmployeeId NVARCHAR(32) = NULL
,#SSOId NVARCHAR(32) = NULL
,#UserName NVARCHAR(100) = NULL
)
AS
*/
SET NOCOUNT ON;
BEGIN
SELECT [USER_ID] AS UserId
,[FIRST_NM] AS FirstName
,[LST_NM] AS LastName
,[FULL_NM] AS FullName
,[ADS_USER_ID] AS ADSId
,[SEG_ID] AS SegmentId
,[PHONE_NO] AS PhoneNo
,[FAX_NO] AS FaxNo
,[EMP_ID] AS EmployeeId
,[EMAIL_AD_TX] AS Email
,[SSO_ID] AS SSOId
,[SFDC_IN] AS IsSFDC
,[USER_SFDC_ID] AS UserSFDCId
,[MGR_SFDC_ID] AS ManagerSFDCId
,[ACT_IN] AS IsActive
,[SYS_USER_IN] AS IsSystemUser
,[PORFOLIO_OWN_IN] AS CanHavePortfolio
,[MGR_ID] AS ManagerId
,[LST_LOG_IN_TS] AS LastLoginDate
,[EMP_BAND_TX] AS Band
,[CREAT_TS] AS CreatedDate
,[CREAT_BY_USER_ID] AS CreatedBy
,[LST_UPDT_TS] AS UpdatedDate
,[LST_UPDT_BY_USER_ID] AS UpdatedBy
FROM [dbo].[USER] WITH (NOLOCK)
WHERE ([EMP_ID] = ISNULL(#EmployeeId, [EMP_ID])OR [SSO_ID] = ISNULL(#SSOId, [SSO_ID])
AND [ADS_USER_ID] = ISNULL(#ADSId, [ADS_USER_ID])
AND [USER_ID] = ISNULL(#UserId, [USER_ID])
AND [FULL_NM] LIKE CASE
WHEN #UserName IS NOT NULL
THEN '%' + #UserName + '%'
ELSE [FULL_NM]
END
END
I don't think the parentheses are balanced correctly. In any case, I would write this without the ISNULL():
WHERE ((#EmployeeId IS NULL OR EMP_ID = #EmployeeId) OR
(#SSOId IS NULL OR SSO_ID = #SSOId)
) AND
(#ADSId IS NULL OR ADS_USER_ID = #ADSId) AND
(#UserId IS NULL OR USER_ID = #UserId) AND
(#UserName IS NULL OR FULL_NM LIKE '%' + #UserName + '%')
I am guessing that the OR is for the first two conditions. This is where the parens don't seem to line up in the query in the question.
I prefer this construct for two reasons. First, it handles NULL values in the column values as well as the parameter values. And second -- because it is more general -- it is one of the standard two ways I use to handle optional parameters (the other is to use dynamic SQL which can make use of indexes).
Query seems to be okay .Are you passing DBNull from you C# code or empty text
WHERE (#EmployeeId IS NULL OR (EMP_ID = #EmployeeId))
AND (#SSOId IS NULL OR (SSO_ID = #SSOId))
AND [ADS_USER_ID] = ISNULL(#ADSId, [ADS_USER_ID])
AND [USER_ID] = ISNULL(#UserId, [USER_ID])
AND [FULL_NM] LIKE CASE
WHEN #UserName IS NOT NULL
THEN '%' + #UserName + '%'
ELSE [FULL_NM]
Used this script
WHERE EMP_ID = CASE WHEN ISNULL(#EmployeeId,0) > 0 THEN #EmployeeId ELSE EMP_ID END AND SSO_ID = CASE WHEN ISNULL(#SSOId,0) > 0 THEN #SSOId ELSE SSO_ID END

Dyanamic SQL Query not working

I have a table called procedure look up which stores medical procedures
and have multiple company table for which i had to calculate the procedure fees so i had created a dynamic query for it
below is the query
declare #TableProviderName varchar(500)
,#SQLQuery1 nvarchar(max)
,#MaxRecordSize Int
,#Name varchar(250) = null
,#code varchar(50) = null
set #Name = 'sug'
set #TableProviderName = 'PRD_Tata_Details'
set #MaxRecordSize = 50
set #SQLQuery1 = '
;WITH CTE_Procedure AS
(
select top (#MaxRecordSize1)
GPL_ID_PK as ProcedureID
,GPL_ProcedureType as ProcedureType
,GPL_Code as ProcedureCode
,coalesce(Name,GPL_Name,null)as Procedurename
,GPL_CurrencyType_FK as CurrencyType
,ISNULL(GPL_Description,''NIL'') as ProcedureDescription
,ISNULL(GPL_PatientInstruction,''NIL'')as PatientInstructions
,GPL_ProcedureCategory_FK as ProcedureCategory
,GPL_CategorySpecialization_FK as ProcedureSpecialization
,coalesce(PatientPayable,GPL_ProcedureFee,0) as PatientPayable
,0 as InsurancePayable
,0 as InsuranceDiscount
,1 as ProcedureCount
,0 as IndBillingStatus
,Case
when GeneralProcedureID is not null then ''Insurance Supported''
else ''Insurance not Supported''
end as InsuranceStatus
,ROW_NUMBER( ) OVER ( ORDER BY GPL_Name ASC) as RowNumber
from
dbo.PRD_GeneralProcedure_Lookup
left join '
+ #TableProviderName +
'
on
GeneralProcedureID = GPL_ID_PK
where
GPL_ProcedureType = #ProcedureType1
and
(#Name1 is null or GPL_Name like %#Name1%)
and
(#code1 is null or GPL_Code like %#code1%)
)
Select
*
from
CTE_Procedure
'
Execute sp_executesql #SQLQuery1, N'#MaxRecordSize1 int, #ProcedureType1 tinyint,#Name1 varchar(250)
, #code varchar(50)' ,#MaxRecordSize1 = #MaxRecordSize, #ProcedureType1 = 1 , #Name1 = #Name, #code1 = #code
but when executing error occurs saying
"Incorrect syntax near '#Name1'"
can anyone help me with that where condition side issue
I think It may have something to do with your like statement and the way you pass the parameter.
Have a look at this question Parameters & Like statement.
#Name1 = "'%yourvalue%'"

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,
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).
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
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.
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.