Null value column and NOT EXISTS T-sql - sql

I'm trying to do the following:
IF NOT EXISTS (
SELECT *
FROM [tbl_web_company]
WHERE [name] = #name
AND [address1] = #address1
AND [address2] = #address2
AND [city] = #city
AND [province_id] = #province_id
AND [postal_code] = #postalcode
AND [contact_phone] = #phone
AND [contact_fax] = #fax
AND [deleted] = dbo.pvd_fn_getDeletedDate(#id, #active))
BEGIN
SELECT 'update'
END
ELSE
BEGIN
SELECT 'no update'
END
I'm basically trying to see if any of the columns have changed, but I'm having problems when #province_id and dbo.pvd_fn_getDeletedDate(#id, #active) are NULL in the database, and are both set as NULL.
Province ID is an INT - Nullable
Deleted is a Datetime - Nullable.
If the record in the database has NULL for both these values, then this will always select 'update'. Which is wrong as [province_id] and [deleted] are NULL.
Any suggestions how to handle NULLS in this case?

Can you use the ISNULL() function to set a default value?
SELECT *
FROM [tbl_web_company]
WHERE [name] = #name
AND [address1] = #address1
AND [address2] = #address2
AND [city] = #city
AND ISNULL([province_id],99999) = ISNULL(#province_id,99999)
AND [postal_code] = #postalcode
AND [contact_phone] = #phone
AND [contact_fax] = #fax
AND ISNULL([deleted], '1990-01-01') = ISNULL(dbo.pvd_fn_getDeletedDate(#id, #active), '1990-01-01')
BEGIN
SELECT 'update'
END
ELSE
BEGIN
SELECT 'no update'
END
Using ISNULL() prevents the optimizer from using indexes, so normally I'd advise against this, but the way this query is written, I'd be surprised if it's making use of an index anyway.

use
IS NULL
instead
= NULL

Use "IS NULL" instead:
SELECT 'Is null' WHERE NULL = NULL
woudn't return any rows, but:
SELECT 'Is null' WHERE NULL IS NULL
will...
A good reading about nulls here

As your values are coming from parameters and are not hard coded you can use the following:
...
AND ([province_id] IS NULL OR [province_id] = #province_id)
...
Use the same structure for your other NULLABLE fields.

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

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

Conditional stored procedure with bool values

I am trying to assemble a stored procedure to return a set of results.
On my page, I have three checkboxes and a searchbox.
Then in the stored procedure, I have this:
CREATE Procedure [dbo].[sp_Search]
#Bool1 bit = NULL,
#Bool2 bit = NULL,
#Bool3 bit = NULL,
#SearchTerm nvarchar(30)
As
Begin
Select
[CompanyID],
[CompanyName],
[Label],
[Bool1],[Bool2],[Bool3]
From [Contract Companies]
WHERE (CompanyName LIKE #SearchTerm OR [Label] LIKE #SearchTerm) AND
if(#Bool1 IS NOT NULL)
[Bool1] = 'True'
end
if(#Bool2 IS NOT NULL)
AND [Bool2] = 'True'
end
if(#Bool3 IS NOT NULL)
AND [Bool3] = 'True'
end
Something like that, with those three boolean values not true all the time, just when their checkbox will be checked.
How can I achieve that?
Thanks in advance, Laziale
You might try something like this:
create procedure [dbo].[sp_Search]
#Bool1 bit = null,
#Bool2 bit = null,
#Bool3 bit = null,
#SearchTerm nvarchar(30)
as
begin
select
CompanyID,
CompanyName,
Label,
Bool1,
Bool2,
Bool3
from Contract Companies
where (CompanyName like #SearchTerm or Label like #SearchTerm)
and (Bool1 = #Bool1 or #Bool1 is null)
and (Bool2 = #Bool2 or #Bool2 is null)
and (Bool3 = #Bool3 or #Bool3 is null)
end
This will get matching records for each bool when present, and all records otherwise.
It is noted that this may not be the best performing method of dynamically checking values, but they're bits here (often not indexed) and I think it fits very elegantly. There are alternatives discussed in that article.
Option with CASE expression
CREATE Procedure [dbo].[sp_Search]
#Bool1 bit = NULL,
#Bool2 bit = NULL,
#Bool3 bit = NULL,
#SearchTerm nvarchar(30)
As
Begin
Select
[CompanyID],
[CompanyName],
[Label],
[Bool1],[Bool2],[Bool3]
From [Contract Companies]
WHERE (CompanyName LIKE #SearchTerm OR [Label] LIKE #SearchTerm) AND
[Bool1] = CASE WHEN #Bool1 IS NOT NULL THEN 'True' ELSE [Bool1] END AND
[Bool2] = CASE WHEN #Bool2 IS NOT NULL THEN 'True' ELSE [Bool2] END AND
[Bool3] = CASE WHEN #Bool3 IS NOT NULL THEN 'True' ELSE [Bool3] END

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.

SQL Coalesce in WHERE clause

I'm trying to implement optional parameters in a stored procedure that I have but I am running into a problem. Here's a simplified query to illustrate the issue:
SET ANSI_NULLS OFF
DECLARE #MiddleName VARCHAR(20);
SET #MiddleName = NULL;
SELECT * FROM [Customer]
WHERE [LastName] = 'Torres'
AND [MiddleName] = COALESCE(#MiddleName, [MiddleName])
When I run this query I need to get one row back because one Torres has NULL in the [MiddleName] column. But the query returns zero rows. Using IFNULL() produces the same result. From researching COALESCE, I was under the impression that NULL would be returned if all expressions are NULL. As I am not a SQL expert I assume that I am missing something, but what is it.....
Thanks in advance for any help.
The problem is that in sql, "WHERE Null = Null" will never return any rows since Null does not equal itself.
You have to do
SELECT * FROM [Customer]
WHERE [LastName] = 'Torres'
AND ( #MiddleName IS NULL OR [MiddleName] = #MiddleName )
You state you are looking for the query to return the row where the field MiddleName is NULL. Unfortunately (NULL = NULL) does not return true, it returns NULL.
You need something like...
SELECT * FROM [Customer]
WHERE [LastName] = 'Torres'
AND ([MiddleName] = #MiddleName OR #MiddleName IS NULL)
Are you trying to do this?
SELECT * FROM [Customer]
WHERE [LastName] = 'Torres'
AND ([MiddleName] = #MiddleName OR #MiddleName IS NULL)
From what I understand it looks like it.
Your COALESCE returns NULL when the #MiddleName parameter and the MiddleName column are both NULL, but the test will evaluate to false because a NULL does not equal any other NULL.
To workaround this you should explicitly test the #MiddleName parameter for nullity:
SELECT *
FROM [Customer]
WHERE [LastName] = 'Torres'
AND (#MiddleName IS NULL OR [MiddleName] = #MiddleName)