Why the result is still null after placing the condition also? [duplicate] - sql-server

I wrote a T-SQL Statement similar like this (the original one looks different but I want to give an easy example here):
SELECT first_name +
CASE last_name WHEN null THEN 'Max' ELSE 'Peter' END AS Name
FROM dbo.person
This Statement does not have any syntax errors but the case-clause always chooses the ELSE-part - also if the last_name is null. But Why?
What I want to do is to unite first_name and last_name, but if last_name is null the whole name becomes null:
SELECT first_name +
CASE last_name WHEN null THEN '' ELSE ' ' + last_name END AS Name
FROM dbo.person
Do you know where the problem is?

CASE WHEN last_name IS NULL THEN '' ELSE ' '+last_name END

The WHEN part is compared with ==, but you can't really compare with NULL. Try
CASE WHEN last_name is NULL THEN ... ELSE .. END
instead or COALESCE:
COALESCE(' '+last_name,'')
(' '+last_name is NULL when last_name is NULL, so it should return '' in that case)

There are plenty of solutions but none covers why the original statement doesn't work.
CASE last_name WHEN null THEN '' ELSE ' '+last_name
After the when, there is a check for equality, which should be true or false.
If one or both parts of a comparison is null, the result of the comparison will be UNKNOWN, which is treated like false in a case structure. See: https://www.xaprb.com/blog/2006/05/18/why-null-never-compares-false-to-anything-in-sql/
To avoid this, Coalesce is the best way.

Given your query you can also do this:
SELECT first_name + ' ' + ISNULL(last_name, '') AS Name FROM dbo.person

The problem is that null is not considered equal to itself, hence the clause never matches.
You need to check for null explicitly:
SELECT CASE WHEN last_name is NULL THEN first_name ELSE first_name + ' ' + last_name

try:
SELECT first_name + ISNULL(' '+last_name, '') AS Name FROM dbo.person
This adds the space to the last name, if it is null, the entire space+last name goes to NULL and you only get a first name, otherwise you get a firts+space+last name.
this will work as long as the default setting for concatenation with null strings is set:
SET CONCAT_NULL_YIELDS_NULL ON
this shouldn't be a concern since the OFF mode is going away in future versions of SQl Server

The issue is that NULL is not considered to be equal to anything even not to itself, but the strange part is that is also not not equal to itself.
Consider the following statements (which is BTW illegal in SQL Server T-SQL but is valid in My-SQL, however this is what ANSI defines for null, and can be verified even in SQL Server by using case statements etc.)
SELECT NULL = NULL -- Results in NULL
SELECT NULL <> NULL -- Results in NULL
So there is no true/false answer to the question, instead the answer is also null.
This has many implications, for example in
CASE statements, in which any null value will always use the ELSE clause unless you use explicitly the WHEN IS NULL condition (NOT the WHEN NULL condition )
String concatenation, as
SELECT a + NULL -- Results in NULL
In a WHERE IN or WHERE NOT IN clause, as if you want correct results make sure in the correlated sub-query to filter out any null values.
One can override this behavior in SQL Server by specifying SET ANSI_NULLS OFF, however this is NOT recommended and should not be done as it can cause many issues, simply because deviation of the standard.
(As a side note, in My-SQL there is an option to use a special operator <=> for null comparison.)
In comparison, in general programming languages null is treated is a regular value and is equal to itself, however the is the NAN value which is also not equal to itself, but at least it returns 'false' when comparing it to itself, (and when checking for not equals different programming languages have different implementations).
Note however that in the Basic languages (i.e. VB etc.) there is no 'null' keyword and instead one uses the 'Nothing' keyword, which cannot be used in direct comparison and instead one needs to use 'IS' as in SQL, however it is in fact equal to itself (when using indirect comparisons).

Found a solution to this. Just ISNULL the CASE statement:
ISNULL(CASE x WHEN x THEN x ELSE x END, '') AS 'BLAH'

CASE
WHEN last_name IS null THEN ''
ELSE ' ' + last_name
END

Jason caught an error, so this works...
Can anyone confirm the other platform versions?
SQL Server:
SELECT
CASE LEN(ISNULL(last_name,''))
WHEN 0 THEN ''
ELSE ' ' + last_name
END AS newlastName
MySQL:
SELECT
CASE LENGTH(IFNULL(last_name,''))
WHEN 0 THEN ''
ELSE ' ' + last_name
END AS newlastName
Oracle:
SELECT
CASE LENGTH(NVL(last_name,''))
WHEN 0 THEN ''
ELSE ' ' + last_name
END AS newlastName

When you get frustrated trying this:
CASE WHEN last_name IS NULL THEN '' ELSE ' '+last_name END
Try this one instead:
CASE LEN(ISNULL(last_Name,''))
WHEN 0 THEN ''
ELSE ' ' + last_name
END AS newlastName
LEN(ISNULL(last_Name,'')) measures the number of characters in that column, which will be zero whether it's empty, or NULL, therefore WHEN 0 THEN will evaluate to true and return the '' as expected.
I hope this is a helpful alternative.
I have included this test case for sql server 2008 and above:
DECLARE #last_Name varchar(50) = NULL
SELECT
CASE LEN(ISNULL(#last_Name,''))
WHEN 0 THEN ''
ELSE 'A ' + #last_name
END AS newlastName
SET #last_Name = 'LastName'
SELECT
CASE LEN(ISNULL(#last_Name,''))
WHEN 0 THEN ''
ELSE 'A ' + #last_name
END AS newlastName

I tried casting to a string and testing for a zero-length string and it worked.
CASE
WHEN LEN(CAST(field_value AS VARCHAR(MAX))) = 0 THEN
DO THIS
END AS field

You can use IsNull function
select
isnull(rtrim(ltrim([FirstName]))+' ','') +
isnull(rtrim(ltrim([SecondName]))+' ','') +
isnull(rtrim(ltrim([Surname]))+' ','') +
isnull(rtrim(ltrim([SecondSurname])),'')
from TableDat
if one column is null you would get an empty char
Compatible with Microsoft SQL Server 2008+

Use the CONCAT function available in SQL Server 2012 onward.
SELECT CONCAT([FirstName], ' , ' , [LastName]) FROM YOURTABLE

NULL does not equal anything. The case statement is basically saying when the value = NULL .. it will never hit.
There are also several system stored procedures that are written incorrectly with your syntax. See sp_addpullsubscription_agent and sp_who2.
Wish I knew how to notify Microsoft of those mistakes as I'm not able to change the system stored procs.

In SQL Server 2017, Microsoft introduced a Concatenate With Separator function, for just your situation:
SELECT CONCAT_WS(' ', first_name, last_name) FROM dbo.person
CONCAT_WS skips NULL values, but not empty strings.
Interestingly, MySQL introduced CONCAT_WS over a decade earlier.

You can use like this:
CASE IsNull(last_name,'') WHEN '' THEN 'Max' ELSE 'Peter' END AS Name

Related

Append the previous value of a column in most optimized way without using case

Below is my T-SQL
Update Table1
Set Note = CONCAT(Note, ', ', #Note)
If in the above query Note is null or empty I want to avoid appending in that case
my query should behave like
Update Table1
Set Note=#Note
I think there are better and optimized way to do this so just searching for that option. I can write the query using case.
You could use coalesce because when using the + operator any null results in null as opposed to concat which handles null values as an empty string.
update Table1 set Note = coalesce(Note + ', ' + #Note, #Note)
But this doesn't handle the case when Note is an empty string. You need a case expression for that.
Actually you can handle the empty string case as follows:
update Table1 set Note = coalesce(nullif(Note,'') + ', ' + #Note, #Note)
But in my opinion that is more complex than using a simple case expression:
update Table1 set
Note = case when coalesce(Note,'') <> '' then concat(Note, ', ', #Note) else #Note end
The case expression is an integral part of T-SQL so I wouldn't be concerned about using it.
Further: NULLIF is actually a case under the hood anyway.
Similar to Dale K's 1st answer:
Update Table1 Set Note = CONCAT(NULLIF(Note, '') + ', ', #Note)
Parts explained:
NULLIF(Note, '') is used to turn an empty Note into NULL.
NULL + 'something' evaluates to: NULL.
CONCAT(NULL, 'something') evaluates to : 'something'.

SQL Server 2016 How to use a simple Regular Expression in T-SQL?

I have a column with the name of a person in the following format: "LAST NAME, FIRST NAME"
Only Upper Cases Allowed
Space after comma optional
I would like to use a regular expression like: [A-Z]+,[ ]?[A-Z]+ but I do not know how to do this in T-SQL. In Oracle, I would use REGEXP_LIKE, is there something similar for SQL Server 2016?
I need something like the following:
UPDATE table
SET is_correct_format = 'YES'
WHERE REGEXP_LIKE(table.name,'[A-Z]+,[ ]?[A-Z]+');
First, case sensitivity depends on the collation of the DB, though with LIKE you can specify case comparisons. With that... here is some Boolean logic to take care of the cases you stated. Though, you may need to add additional clauses if you discover some bogus input.
declare #table table (Person varchar(64), is_correct_format varchar(3) default 'NO')
insert into #table (Person)
values
('LowerCase, Here'),
('CORRECTLY, FORMATTED'),
('CORRECTLY,FORMATTEDTWO'),
('ONLY FIRST UPPER, LowerLast'),
('WEGOT, FormaNUMB3RStted'),
('NoComma Formatted'),
('CORRECTLY, TWOCOMMA, A'),
(',COMMA FIRST'),
('COMMA LAST,'),
('SPACE BEFORE COMMA , GOOD'),
(' SPACE AT BEGINNING, GOOD')
update #table
set is_correct_format = 'YES'
where
Person not like '%[^A-Z, ]%' --check for non characters, excluding comma and spaces
and len(replace(Person,' ','')) = len(replace(replace(Person,' ',''),',','')) + 1 --make sure there is only one comma
and charindex(',',Person) <> 1 --make sure the comma isn't at the beginning
and charindex(',',Person) <> len(Person) --make sure the comma isn't at the end
and substring(Person,charindex(',',Person) - 1,1) <> ' ' --make sure there isn't a space before comma
and left(Person,1) <> ' ' --check preceeding spaces
and UPPER(Person) = Person collate Latin1_General_CS_AS --check collation for CI default (only upper cases)
select * from #table
The tsql equivalent could look like this. I'm not vouching for the efficiency of this solution.
declare #table as table(name varchar(20), is_Correct_format varchar(5))
insert into #table(name) Values
('Smith, Jon')
,('se7en, six')
,('Billy bob')
UPDATE #table
SET is_correct_format = 'YES'
WHERE
replace(name, ', ', ',x')
like (replicate('[a-z]', charindex(',', name) - 1)
+ ','
+ replicate('[a-z]', len(name) - charindex(',', name)) )
select * from #table
The optional space is hard to solve, so since it's next to a legal character I'm just replacing with another legal character when it's there.
TSQL does not provide the kind of 'repeating pattern' of * or + in regex, so you have to count the characters and construct the pattern that many times in your search pattern.
I split the string at the comma, counted the alphas before and after, and built a search pattern to match.
Clunky, but doable.

CASE statement within WHERE clause in SQL Server 2008

I want to write a CASE statement to find the data between two dates based on #sFRomDate empty or not. Given below query not working as per my requirement. Please help me to find a proper solution
SELECT
*
FROM
tbl_emp_data
WHERE
CASE
WHEN #sFRomDate!=''
THEN SubmissionDate BETWEEN #sFRomDate AND DATEADD(DAY,1,#sToDate)
ELSE
SubmissionDate = NULL
END
This might be the logic you were trying to implement. In the event that #sFRomDate be NULL or empty and SubmissionDate also be NULL or empty the record will be returned. Otherwise, the SubmissionDate will be checked to make sure it is within the range you defined.
SELECT *
FROM tbl_emp_data
WHERE (COALESCE(#sFRomDate, '') = '' AND
COALESCE(SubmissionDate, '') = '') OR
(COALESCE(#sFRomDate, '') <> '' AND
COALESCE(SubmissionDate, '') <> '' AND
SubmissionDate BETWEEN #sFRomDate AND DATEADD(DAY, 1, #sToDate))
If I understand what you want, I think it becomes simpler to follow the logic if you write:
IF #sFromDate = ''
BEGIN
SELECT * FROM tbl_emp_data where SubmissionDate=null
END
ELSE
BEGIN
SELECT * FROM tbl_emp_data where SubmissionDate between #sFRomDate and DATEADD(DAY,1,#sToDate)
END
But as #Tim Biegeleisen says, be careful with nulls, not just for #sFromDate but also #sToDate

How to convert empty spaces into null values, using SQL Server?

I have a table and the columns on this table contains empty spaces for some records. Now I need to move the data to another table and replace the empty spaces with a NULL value.
I tried to use:
REPLACE(ltrim(rtrim(col1)),' ',NULL)
but it doesn't work. It will convert all of the values of col1 to NULL. I just want to convert only those values that have empty spaces to NULL.
I solved a similar problem using NULLIF function:
UPDATE table
SET col1 = NULLIF(col1, '')
From the T-SQL reference:
NULLIF returns the first expression if the two expressions are not equal. If the expressions are equal, NULLIF returns a null value of the type of the first expression.
Did you try this?
UPDATE table
SET col1 = NULL
WHERE col1 = ''
As the commenters point out, you don't have to do ltrim() or rtrim(), and NULL columns will not match ''.
SQL Server ignores trailing whitespace when comparing strings, so ' ' = ''. Just use the following query for your update
UPDATE table
SET col1 = NULL
WHERE col1 = ''
NULL values in your table will stay NULL, and col1s with any number on space only characters will be changed to NULL.
If you want to do it during your copy from one table to another, use this:
INSERT INTO newtable ( col1, othercolumn )
SELECT
NULLIF(col1, ''),
othercolumn
FROM table
This code generates some SQL which can achieve this on every table and column in the database:
SELECT
'UPDATE ['+T.TABLE_SCHEMA+'].[' + T.TABLE_NAME + '] SET [' + COLUMN_NAME + '] = NULL
WHERE [' + COLUMN_NAME + '] = '''''
FROM
INFORMATION_SCHEMA.columns C
INNER JOIN
INFORMATION_SCHEMA.TABLES T ON C.TABLE_NAME=T.TABLE_NAME AND C.TABLE_SCHEMA=T.TABLE_SCHEMA
WHERE
DATA_TYPE IN ('char','nchar','varchar','nvarchar')
AND C.IS_NULLABLE='YES'
AND T.TABLE_TYPE='BASE TABLE'
A case statement should do the trick when selecting from your source table:
CASE
WHEN col1 = ' ' THEN NULL
ELSE col1
END col1
Also, one thing to note is that your LTRIM and RTRIM reduce the value from a space (' ') to blank (''). If you need to remove white space, then the case statement should be modified appropriately:
CASE
WHEN LTRIM(RTRIM(col1)) = '' THEN NULL
ELSE LTRIM(RTRIM(col1))
END col1
Maybe something like this?
UPDATE [MyTable]
SET [SomeField] = NULL
WHERE [SomeField] is not NULL
AND LEN(LTRIM(RTRIM([SomeField]))) = 0
here's a regex one for ya.
update table
set col1=null
where col1 not like '%[a-z,0-9]%'
essentially finds any columns that dont have letters or numbers in them and sets it to null. might have to update if you have columns with just special characters.

SQL if statement in where clause for searching database

I'm creating a stored procedure to return search results where some of the parameters are optional.
I want an "if statement" in my where clause but can't get it working. The where clause should filter by only the non-null parameters.
Here's the sp
ALTER PROCEDURE spVillaGet
-- Add the parameters for the stored procedure here
#accomodationFK int = null,
#regionFK int = null,
#arrivalDate datetime,
#numberOfNights int,
#sleeps int = null,
#priceFloor money = null,
#priceCeil money = null
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
select tblVillas.*, tblWeeklyPrices.price from tblVillas
INNER JOIN tblWeeklyPrices on tblVillas.villaId = tblWeeklyPrices.villaFK
where
If #accomodationFK <> null then
accomodationTypeFK = #accomodationFK
#regionFK <> null Then
And regionFK = #regionFK
IF #sleeps <> null Then
And sleeps = #sleeps
IF #priceFloor <> null Then
And price >= #priceFloor And price <= #priceCeil
END
Any ideas how to do this?
select tblVillas.*, tblWeeklyPrices.price
from tblVillas
INNER JOIN tblWeeklyPrices on tblVillas.villaId = tblWeeklyPrices.villaFK
where (#accomodationFK IS null OR accomodationTypeFK = #accomodationFK)
AND (#regionFK IS null or regionFK = #regionFK)
AND (#sleeps IS null OR sleeps = #sleeps)
AND (#priceFloor IS null OR (price BETWEEN #priceFloor And #priceCeil))
We've used a lot of COALESCE here in the past for "dynamic WHERE clauses" like you're talking about.
SELECT *
FROM vehicles
WHERE ([vin] LIKE COALESCE(#vin, [vin]) + '%' ESCAPE '\')
AND ([year] LIKE COALESCE(#year, [year]) + '%' ESCAPE '\')
AND ([make] LIKE COALESCE(#make, [make]) + '%' ESCAPE '\')
AND ([model] LIKE COALESCE(#model, [model]) + '%' ESCAPE '\')
A big problem arises though when you want to optionally filter for a column that is also nullable... if the data in the column is null for a given row AND the user didn't enter anything to search by for that column (so the user input is also null), then that row won't even show up in the results (which, if your filters are optional, is incorrect exclusionary behavior).
In order to compensate for nullable fields, you end up having to do messier looking SQL like so:
SELECT *
FROM vehicles
WHERE (([vin] LIKE COALESCE(#vin, [vin]) + '%' ESCAPE '\')
OR (#vin IS NULL AND [vin] IS NULL))
AND (([year] LIKE COALESCE(#year, [year]) + '%' ESCAPE '\')
OR (#year IS NULL AND [year] IS NULL))
AND (([make] LIKE COALESCE(#make, [make]) + '%' ESCAPE '\')
OR (#make IS NULL AND [make] IS NULL))
AND (([model] LIKE COALESCE(#model, [model]) + '%' ESCAPE '\')
OR (#model IS NULL AND [model] IS NULL))
Just so you understand, IF is procedural code in T-SQl. It canot be used in an insert/update/delete/select statement it can only be used to determine which of two statements you want to run. When you need different possibilities within a statement, you can do as above or use a CASE statement.
You can also use IsNull or Coalesce function
Where accomodationTypeFK = IsNull(#accomodationFK, accomodationTypeFK)
And regionFK = Coalesce(#regionFK,regionFK)
And sleeps = IsNull(#sleeps,sleeps )
And price Between IsNull(#priceFloor, Price) And IsNull(priceCeil, Price)
This does the same thing as Michael's suggestion above...
IsNull(), and Coalesce() work more or less the same way, they return the first non-Null argument in the list, except iSNull only allows 2 arguments, and Coalesce can take any number...
http://blogs.msdn.com/sqltips/archive/2008/06/26/differences-between-isnull-and-coalesce.aspx
Try putting your IF statement around the entire SQL statement. That means will have one SQL statement for each condition. That worked for me.

Resources