Handling NULLs in SQL query - sql-server

What is the right practice of checking NULLs in SQL Case ?
1) Using ISNULL()
WHEN (ISNULL(TABLE.COLUMN,0) > 0) THEN ....
2) Using IS NULL
WHEN TABLE.COLUMN IS NOT NULL THEN ....

If you are checking any condition then always use 'is null' and if replacing any value with a different one, then use isnull(a,b).
Check the following -
http://msdn.microsoft.com/en-us/library/ms184325.aspx
Read the last line specially!!!

Second one is right if you want to check for null value in SQL case..

Both are correct if the values in the column are either greater than 0 or null.
You can refer to this post if you want to know about the weird behavior of nulls in SQL Server.

This is also another approach to check for NON NULL values.
Checking for length of the column if it is greater than 1 or equal 1 then its a NON NULL
value.
declare #emp table
(
fname varchar(50)
);
INSERT into #emp VALUES('vishwanath');
INSERT into #emp VALUES('chetan');
INSERT into #emp VALUES(NULL);
INSERT into #emp VALUES(NULL);
SELECT * FROM #emp
where len(fname)>=1 and fname<>'';
Gives..
fname
--------------------------------------------------
vishwanath
chetan

when you are checking whether a column is null or not it is better to use
col IS NULL
when you use ISNULL(TABLE.COLUMN,0) > 0) function , null values have to be converted to zero fist then should take all values greater than zero
this function is useful in another occasion. lets say if I want to return all the null values as well as the negative values.
so the query would be
select * from table where col is null or col<0
this can be re-written as
select * from table isnull(col,-1)<0

Both are correct in there cause, however ISNULL can be helpfull when you want to use a constant value instead of NULL in that column while calculating SUM, average, etc.
For example you can check :http://www.w3schools.com/sql/sql_isnull.asp
Due to this feature I personally use ISNULL/COALESCE for calculation purposes.

Related

replace .0 on varchar data type

I have a varchar column that has numbers with .0
This column has both numeric data and non-numeric data.
I first tried to convert data type to integer, but since there is non-numeric data type, it would not let me.
How do I remove .0 (from all numbers that has .0)?
So, for example, 100.0 should be 100
I am not trying to use select, cast or truncate as I need to actually modify the existing data.
Thanks.
Since the column has both numeric and non-numeric data it is not enough to just check if it ends with '.0'.
You should also check if it is a numeric value, which can be done with TRY_CAST():
UPDATE tablename
SET col = LEFT(col, LEN(col) - 2)
WHERE col LIKE '%.0' AND TRY_CAST(col AS FLOAT) IS NOT NULL
See the demo.
Assuming you want to update your table...
where x = your table name
yourfieldname = the field name you need to update.
.
UPDATE table X
SET yourfieldName = left(yourfieldname,len(yourieldName)-2)
WHERE right(yourfieldName,2)='.0')
-- or perhaps where yourfieldname like '%.0' would be faster...
Should: update all fields ending in .0 would need to test to see which where clause would be faster depending on indexes. if speed is a consideration. If not; and this is a 1 and done... does it matter?
Be sure to test on a subset/copy table!
Assumes you don't have a spaces after the .0... or any non-display characters.. If you do you'll need to trim off the spaces and replace the non-display characters with empty string ''
Just another option
Example
Declare #YourTable table (SomeCol varchar(50))
Insert Into #YourTable values
('100.0')
,('1001.0')
,('Not Numeric')
,('-200.05')
,('10,250.0')
Update #YourTable
set SomeCol = format(try_convert(money,SomeCol),'#.######')
From #YourTable
Where try_convert(money,SomeCol) is not null
The Updated Table
SomeCol
100
1001
Not Numeric
-200.05
10250

Conversion failed when converting the vardhar value 'abc' to data type int

I am inserting data from one table to another so when inserting I got above error mentioned in title
Insert into dbo.source(
title
)
Select
Title from dbi.destination
title in dbo.source table is of INT data type and title in dbo.destination table is of Varchar data type and I have data like abc, efg, etc. in the dbo.destination table.
So how to solve this now or is it possible to convert and insert values?
You can use SQL Server try_cast() function as shown below. Here is the official documentation of TRY_CAST (Transact-SQL).
It Returns a value cast to the specified data type if the cast succeeds; otherwise, returns null.
Syntax
TRY_CAST ( expression AS data_type [ ( length ) ] )
And the implementation in your query.
INSERT INTO dbo.source (title)
SELECT try_cast(Title AS INT)
FROM dbi.destination
Using this solution you need to be sure you have set the column allow null true otherwise it will give error.
If you do not want to set the allow null then you need minor changes in select query as shown below - passing the addition criteria to avoid null values.
Select ... from ... where try_cast(Title AS INT) is not null
You must use isnumeric method of SQL for checking is data numeric or not
CONVERT(INT,
CASE
WHEN IsNumeric(CONVERT(VARCHAR(12), a.value)) = 1 THEN CONVERT(VARCHAR(12),a.value)
ELSE 0 END)
Think about your data types - obviously you cannot have a text string like 'abc' in a column that is defined to hold integers.
It makes no sense to copy a string value into an integer column, so you have to confirm how you want to handle these - do you simply discard them (what is the impact of throwing data away?) or do you replace them with some other value?
If you want to ignore them and use NULL in place then use:
INSERT dbo.Source (Title)
SELECT CASE
WHEN ISNUMERIC(Title) = 1 THEN CAST(Title as INT)
ELSE NULL
END
FROM dbo.Destination
If you want to replace the value then simply change NULL above to the value you want e.g. 0
You can use regex to root out non numeric characters
Insert into dbo.source(
title
)
Select
case when Title not like '%[^0-9]%' then null else cast(Title as int) end as Title
from dbi.destination
Just filter only numeric field from destination table like as below:
Insert into dbo.source(
title
)
Select
Title from dbi.destination
where ISNUMERIC(Title) = 1

Is it necessary to test for NULL if also testing for greater than?

I inherited some old stored procedures today, and came across several examples that followed this general pattern, where #Test is some INT value:
IF #Test IS NOT NULL AND #Test > 0
-- do something...
My understanding is that if #Test is NULL, then it has no value, and is not greater than, less than or even equal to zero. Therefore, testing for NULL is redundant in the above code:
IF #Test > 0
-- do something...
This second version seems to work just fine, and is far more readable IHMO.
So, my question: Is my understanding of NULL being unnecessary in this instance correct, or is there some obvious use-case I'm overlooking here where it could all go horribly wrong?
Note: In some cases, it was obvious that the intent was checking for the existence of a value, and I've changed those to IF EXISTS... my question is more concerned with the general case outlined above.
In SQL all comparisons to a NULL value evaluate to false.
So you always have to check explicitly for NULL, if you wish to act on it.
So, in this case, the additional test is not necessary.
#FlorianHeer is right on. NULL > 0 will eventually evaluate to false but as #Pred points out that is because Null > 0 actually evaluates to null and null cast to a bit is false....
A null is an unknown and therefore any comparison with it is also unknown. Think of arithmetic functions such as addition 1 + NULL = NULL, or concatenation 'A' + NULLL = NULL. NULL means the SQL database engine cannot interpret what its value is so any function or comparison on it is also unknown.
#MikkaRin pointed out that it is the assumption in the ELSE portion of a case statement or IF statement where that can become problematic but lets also think about this in the context of a join and how you may or may not want to see the results.
DECLARE #Table1 AS TABLE (Col INT)
DECLARE #Table2 AS TABLE (Col INT)
INSERT INTO #Table1 VALUES (1),(2),(3)
INSERT INTO #Table2 VALUES (1),(NULL),(3),(4)
SELECT *
FROM
#Table1 t1
INNER JOIN #Table2 t2
ON t1.Col <> t2.Col
Naturally you might think because NULL would be not equal to 1,2,3 that it should be included in the result set. But null is unknown so SQL is saying well I don't know if NULL could be 1,2,3 so I cannot return that as a result.
Now lets do the same thing but add a NULL in the first table:
DECLARE #Table1 AS TABLE (Col INT)
DECLARE #Table2 AS TABLE (Col INT)
INSERT INTO #Table1 VALUES (1),(2),(3),(NULL)
INSERT INTO #Table2 VALUES (1),(NULL),(3),(4)
SELECT *
FROM
#Table1 t1
INNER JOIN #Table2 t2
ON t1.Col = t2.Col
Again you might think that NULL is = to NULL but any comparison of NULL is considered unknown so even though both tables have NULL in it it will not be returned in the dataset.
Now consider:
DECLARE #Table1 AS TABLE (Col INT)
INSERT INTO #Table1 VALUES (1),(2),(3),(NULL)
SELECT *, CASE WHEN Col < 2 THEN Col ELSE 1000 END as ColCase
FROM
#Table1 t1
Which will make even the NULL 1000 the question is should NULL an unknown be 1000? if NULL is unknown how do we know that it isn't less than 2?
For a lot of your operations it may simply be enough to compare #Value > 1 but especially when you start dealing with ELSE in case of IF statements or joining on the antithesis you should consider dealing with the NULLs. Such as using ISNULL() or COALESCE() as #GuidoG points out.
IMHO being explicit about your intentions during operations to appropriately account for null values out weighs the minimal savings of typing.
Compare with NULL is necessary if you use ELSE statements:
for example:
declare #t int
set #t=null
if (#t>0) print '1' -- works fine
if (#t<0) print '2' --works fine
if (#t>0)
print '3' --works fine
else print '4' --here we start getting problems, because we are sure that #t<=0 that is obviously not true
you could replace it with
if isnull(#test, 0) > 0
This way it will be shorter and you still have checked everything
another interesting example:
SELECT (null > 0) AS a, !(null > 0) AS b
value of both a and b will be NULL
From my understanding, in some cases null checks are added sometimes to short circuit OR logic. For example, consider the following:
select * from tbl where (#id is null or #id > id)
If you pass in a value for #id, it tests the first condition (#id is null) and sees that it's false, but since it's part of an OR statement, it then goes ahead and then runs the #id > id comparison to see what that one returns as well. OR statements only need one true returned for the whole thing to resolve to true, and must keep testing until it comes across an OR condition that does.
Whereas if you pass in null for the #id parameter, as soon as it gets to the first condition and it returns true. Seeing that the next it's part of an OR statement, SQL knows it doesn't even have to do any of the following comparison, because the entire OR statement has already resolved to true. The #id > id comparison and will not even run it. This can save a ton of processing if it's a huge table or complex join, etc.

SQL Server - Cast invalid value to int

Is there any way to deal with SQL casts if the input data is corrupt?
Let's say I have a column of datatype NVarchar(10) and want to cast this column to int.
Let's also say that some of the nvarchar values are corrupt, so they can't be converted to int.
Is there any way to silently ignore these, default them to 0 or some such?
DECLARE #t TABLE (Numbers VARCHAR(20))
INSERT INTO #t
VALUES
('30a'),('30'),('100'),
('100a'),('200'),('200a')
SELECT CASE
WHEN ISNUMERIC(Numbers) = 1
THEN CAST(Numbers AS INT) ELSE NULL END AS Number
FROM #t
ISNUMERIC Function returns 1 when it is an integer value you can use this function.
Result
Number
NULL
30
100
NULL
200
NULL
it will cast the integer values to INT and ignore the values that cannot be cast to Int
Try this with PatIndex() function:
select id, val
from t
where patindex('%[^0-9]%',val) = 0
Note: above query is filtering out corrupted values, if you need to bring them in with 0 values, please use a case expression as below.
select id, case when patindex('%[^0-9]%',val) = 0
then convert(int, val)
else 0 end val
from t
Fiddle demo for both queries
I'll be the unpopular one and advise REGEX because ISNUMERIC, while sometimes useful, doesn't catch everything. This answer on SO excellently covers some REGEX concepts, for instance:
One numeric digit
Probably the easiest one of the bunch:
WHERE Column LIKE '[0-9]'
For more details, here's a useful REGEX workbench by Phil Factor and Robyn Pae.

SQL Server return Rows that are not equal <> to a value and NULL

I have a table that has a column of values that can be rowTypeID = (1,2,3, or null) . I would like to write a query that returns any row that doesn't have a value of 3 in it. In this example I want all NULL rows along with all 1,2 rows I just don't want rows with the value of 3
Set ANSI null ON is currently set for the database.
I'm curious as to why I can't write
select * from myTable where myCol <> 3
This query will not return any rows that have NULL in the myCol column
I have to write
select * from my Table where myCol <> 3 or myCol Is NULL
Do I always have to include the IS NULL or can I set it up so a where clause myCol <>3 will return rows that have Null as value for my Col
I think your approach is fine:
SELECT *
FROM MyTable
WHERE myCol <> 3 OR myCol IS NULL
Since you are asking for alternatives, another way to do it is to make your column NOT NULL and store another (otherwised unused) value in the database instead of NULL - for example -1. Then the expression myCol <> 3 will match your fake-NULL just as it would with any other value.
SELECT *
FROM MyTable
WHERE myCol <> 3
However in general I would recommend not to use this approach. The way you are already doing it is the right way.
Also it might be worth mentioning that several other databases support IS DISTINCT FROM which does exactly what you want:
SELECT *
FROM MyTable
WHERE myCol IS DISTINCT FROM 3
MySQL has the NULL-safe equal which can also be used for this purpose:
SELECT *
FROM MyTable
WHERE NOT myCol <=> 3
Unfortunately SQL Server doesn't yet support either of these syntaxes.
You must handle the NULLs one way or another, since expressions involving NULL evaluate to Unknown. If you want, you could instead do:
select *
from MyTable
where isnull(MyColumn, -1) <> 3
But this involves a magic number (-1), and is arguably less readable than the original test for IS NULL.
Edit: and, as SQLMenace points out, is not SARGable.
Whenever you test for a value all NULLs are omitted – after all, you are testing whether the value in some column passes certain criteria and NULL is not a value.
Do I always have to include the IS NULL or can I set it up so a where clause myCol <>3 will return rows that have Null as value for my Col?
You always, always, always have to include is null.
Because 3 does not equal Not/Applicable and it does not equal Unkown.
because you can't compare NULL to anything else, NULL is not even equal to NULL
DECLARE #i INT
DECLARE #i2 INT
SELECT #i = NULL, #i2 = NULL
IF #i = #i2
PRINT 'equal'
ELSE
PRINT 'not equal'

Resources