SQL COALESCE and IS NULL are not returning a space when when the query returns NULL - sql

I am trying to optimize a humongous SQL query that was written by a self taught developer that used a ton of functions instead of JOINS. Anyway, I am having trouble displaying a space or a empty string('') when there is no value in the field selected. I've included only the SELECT in question. I am having the weirdest problem or just overlooking the correct answer in troubleshooting. Whenever I use COALESCE, when the field is supposed to be a blank string, it displays a zero. And when I use IS NULL, I get back NULL. All info online seems to point toward using COALESCE(value, '') as depicted in the code. But I am getting a 0 instead of ''. Does anyone see what I'm doing wrong? I'm using SSMS.
SELECT
pss8.dbo.xml_StripIllegalChars(dbo.rpt_get_series_volume(b.bookkey)) AS p_seriesvol --SELECT to be replaced that works but is slow due to function use I am told
,COALESCE(bd.seriesvolume, '') AS p_seriesvol --my SELECT that won't work!
FROM
bookdetail bd
WHERE
--bd.bookkey='303177'
bd.bookkey='6002'
The bookkeys at the bottom are for testing as I know the top one returns a 1 and the bottom one returns a '' previously when it worked. The SELECT above my commented SELECT is the code that works but is slow... According to what I read online, I am saying 'if there isn't a series volume number, then it equals an empty string.' Does COALESCE not work like this? Can it only return a 0 if the field has no value, or in this case, has no volume number? All help much appreciated. I'm very curious to hear a solution!
Here's more intel. This is how the this SELECT works:
pss8.dbo.xml_StripIllegalChars(dbo.rpt_get_series_volume(b.bookkey)) AS p_seriesvol
The
.rpt_get_series_vol
function manages to create an empty string with this code... Does this reveal anything?
DECLARE #RETURN
VARCHAR(5)
DECLARE #v_desc
VARCHAR(5)
DECLARE #i_volumenumber INT
SELECT #i_volumenumber = volumenumber
FROM bookdetail
WHERE bookkey = #i_bookkey and volumenumber <> 0
IF #i_volumenumber > 0
BEGIN
SELECT #RETURN = CAST(#i_volumenumber as varchar(5))
END
ELSE
BEGIN
SELECT #RETURN = ''
END
RETURN #RETURN
END

As you are looking for a '0' not a NULL COALESCE()is not useful, instead use a simple CASE:
select
...,
case bd.seriesvolume when '0' then '' else bd.seriesvolume end as p_seriesvol
from
...
Or if you want '' for 0 or NULL
case when bd.seriesvolume is null or bd.seriesvolume = '0' then '' else bd.seriesvolume end as p_seriesvo

COALESCE() function returns the 1st non null value
SELECT COALESCE(NULL, NULL, 'third_value', 'fourth_value'); returns the third value because the third value is the first value that is not null.
So in your case COALESCE(bd.seriesvolume, '') AS p_seriesvol if seriesvolume colum value is null then it will return blank string

Related

Best way to check input parameter isnullorwhitespace in tbv function

I have a TBV function. And that function getting couple of input parameters.
CREATE FUNCTION [dbo].[fn]
(
#id NVARCHAR(50),
...
...
)
For example i want to check that #id is not null and is not whitespace.
I was tinking to do like this
SELECT * FROM [FN_Table]() WHERE
COALESCE(#Id,'') !='' AND NULLIF(#id,'') !=null AND #Id=Id
But this is a tedious way i am sure that there would be more elegant and effective way i just newbbe in SQL and do not know best practices.
If you want id to be not null or white space, you can use ltrim():
where ltrim(#id) <> ''
This does the NULL check as well, implicitly.
I'm not sure why you have a comparison to zero. Based on your question this is not necessary. If you are passing in numbers, you should not be using a string type.
Try this, Check with ISNULL and then Get BlankSpace Character Index
SELECT
CASE WHEN ISNULL(#ID,'')='' THEN 1
WHEN CHARINDEX(' ',#ID) >0 THEN 1
ELSE 0
END

SQL: If field is empty, return empty string, otherwise cast as number

I'm running SQL Server 2008 (otherwise I would use Try_Parse) and I need to cast a field as a number in cases where it is non-empty. If the field is empty, an empty string or NULL should be returned. I would prefer if the field was stored as a number in the database, but I have no control over that at this time.
Here is what I have tried:
SELECT CASE WHEN AccountNumber='' THEN '' ELSE CAST(AccountNumber AS bigint) END AS AccountNumber FROM Accounts
OR
SELECT CASE WHEN CAST(AccountNumber AS bigint)=0 THEN '' ELSE CAST(AccountNumber AS bigint) END AS AccountNumber FROM Accounts
But both of these bring back 0 for empty account numbers. I feel that this should be possible but I am going crazy trying to figure it out! Thanks!
You can't select both numbers and empty strings into the same column, because they are incompatible types. That's why the empty strings get converted automatically to 0. NULL should work, however.
SELECT CASE WHEN AccountNumber='' OR AccountNumber IS NULL THEN NULL
ELSE CAST(AccountNumber AS bigint) END AS AccountNumber
FROM Accounts
You can use ISNUMERIC function:
SELECT CASE WHEN ISNUMERIC(AccountNumber) = 1 THEN CAST(AccountNumber AS BIGINT) ELSE NULL END
FROM Accounts

REPLACE empty string

I discover some behavior I didn't know before. Why this line of code does not work?
SELECT REPLACE('','','0') ==> returns ''
I can't even have '' in where condition. It just doesn't work. I have this from imported Excel where in some cells are no values but I'm not able to remove them unless I used LEN('') = 0 function.
There is nothing to replace in an empty string. REPLACE replaces a sequence of characters in a string with another set of characters.
You could use NULLIF to treat it as NULL + COALESCE (or ISNULL):
declare #value varchar(10);
set #value = '';
SELECT COALESCE(NULLIF(#value,''), '0')
This returns '0'.
You can use CASE for this.
(CASE WHEN *YOURTHING* = '' THEN '0' ELSE *YOURTHING* END)
AS *YOURTHING*
It does work. There are two proper behaviors here - first one is to return back empty string (what it does already), second one is to return infinite string full of zeroes.
Solved! > Check multiple scenarios like '', remove spaces, Null, string/numeric result
SELECT CASE WHEN LTRIM(RTRIM(ISNULL(mob, 0))) = '' THEN '0' ELSE LTRIM(RTRIM(ISNULL(mob, 0))) END MobileNo
FROM table1 WHERE emp_no = '01111'

Why does the number 0 evaluate to a blank space

This is something that has baffled me before but I have never found an explanation for it. I have a column in a SQL Server 2008 database that is of type smallint. I want to look for any rows where the value is NULL or blank, so I say this:
SELECT *
FROM products
WHERE warranty_dom IS NULL
OR warranty_dom = ''
This returns rows with a value of 0
So why is 0 treated as the equivalent of '' ?
0 is not treated as '' per se. Instead, '' is implicitly cast to an integer, and that cast makes it 0.
Try it yourself:
SELECT CAST(0 AS varchar) -- Output: '0'
SELECT CAST('' AS smallint) -- Output: 0
Also, as mentioned elsewhere: If warranty_dom is of type smallint, then it's not possible for it to be blank in the first place.

SQL Server, where field is int?

how can I accomplish:
select * from table where column_value is int
I know I can probably inner join to the system tables and type tables but I'm wondering if there's a more elegant way.
Note that column_value is a varchar that "could" have an int, but not necessarily.
Maybe I can just cast it and trap the error? But again, that seems like a hack.
select * from table
where column_value not like '[^0-9]'
If negative ints are allowed, you need something like
where column_value like '[+-]%'
and substring(column_value,patindex('[+-]',substring(column_value,1))+1,len(column_value))
not like '[^0-9]'
You need more code if column_value can be an integer that exceeds the limits of the "int" type, and you want to exclude such cases.
Here if you want to implement your custom function
CREATE Function dbo.IsInteger(#Value VARCHAR(18))
RETURNS BIT
AS
BEGIN
RETURN ISNULL(
(SELECT CASE WHEN CHARINDEX('.', #Value) > 0 THEN
CASE WHEN CONVERT(int, PARSENAME(#Value, 1)) <> 0 THEN 0 ELSE 1 END
ELSE 1
END
WHERE ISNUMERIC(#Value + 'e0') = 1), 0)
END
ISNUMERIC returns 1 when the input
expression evaluates to a valid
integer, floating point number, money
or decimal type; otherwise it returns
0. A return value of 1 guarantees that expression can be converted to one of
these numeric types.
I would do a UDF as Svetlozar Angelov suggests, but I would check for ISNUMERIC first (and return 0 if not), and then check for column_value % 1 = 0 to see if it's an integer.
Here's what the body might look like. You have to put the modulo logic in a separate branch because it will throw an exception if the value isn't numeric.
DECLARE #RV BIT
IF ISNUMERIC(#value) BEGIN
IF CAST(#value AS NUMERIC) % 1 = 0 SET #RV = 1
ELSE SET #RV = 0
END
ELSE SET #RV = 0
RETURN #RV
This should handle all cases without throwing any exceptions:
--This handles dollar-signs, commas, decimal-points, and values too big or small,
-- all while safely returning an int.
DECLARE #IntString as VarChar(50) = '$1,000.'
SELECT CAST((CASE WHEN --This IsNumeric check here does most of the heavy lifting. The rest is Integer-Specific
ISNUMERIC(#IntString) = 1
--Only allow Int-related characters. This will exclude things like 'e' and other foreign currency characters.
AND #IntString NOT LIKE '%[^ $,.\-+0-9]%' ESCAPE '\'--'
--Checks that the value is not out of bounds for an Integer.
AND CAST(REPLACE(REPLACE(#IntString,'$',''),',','') as Decimal(38)) BETWEEN -2147483648 AND 2147483647
--This allows values with decimal-points for count as an Int, so long as there it is not a fractional value.
AND CAST(REPLACE(REPLACE(#IntString,'$',''),',','') as Decimal(38)) = CAST(REPLACE(REPLACE(#IntString,'$',''),',','') as Decimal(38,2))
--This will safely convert values with decimal points to casting later as an Int.
THEN CAST(REPLACE(REPLACE(#IntString,'$',''),',','') as Decimal(10))
END) as Int)[Integer]
Throw this into a Scalar UDF and call it ReturnInt().
If the value comes back as NULL, then it's not an int (so there's your IsInteger() requirement)
If you don't like typing "WHERE ReturnInt(SomeValue) IS NOT NULL", you could throw it into another scalar UDF called IsInt() to call this function and simply return "ReturnInt(SomeValue) IS NOT NULL".
The cool thing is, the UDF can serve double duty by returning the "safely" converted int value.
Just because something can be an int doesn't mean casting it as an int won't throw a huge exception. This takes care of that for you.
Also, I'd avoid the other solutions because this universal approach will handle commas, decimals, dollar signs, and checks the acceptable Int value's range while the other solutions do not - or they require multiple SET operations that prevent you from using the logic in a Scalar-Function for maximum performance.
See the examples below and test them against my code and others:
--Proves that appending "e0" or ".0e0" is NOT a good idea.
select ISNUMERIC('$1' + 'e0')--Returns: 0.
select ISNUMERIC('1,000' + 'e0')--Returns: 0.
select ISNUMERIC('1.0' + '.0e0')--Returns: 0.
--While these are numeric, they WILL break your code
-- if you try to cast them directly as int.
select ISNUMERIC('1,000')--Returns: 1.
select CAST('1,000' as Int)--Will throw exception.
select ISNUMERIC('$1')--Returns: 1.
select CAST('$1' as Int)--Will throw exception.
select ISNUMERIC('10.0')--Returns: 1.
select CAST('10.0' as Int)--Will throw exception.
select ISNUMERIC('9999999999223372036854775807')--Returns: 1. This is why I use Decimal(38) as Decimal defaults to Decimal(18).
select CAST('9999999999223372036854775807' as Int)--Will throw exception.
Update:
I read a comment here that you want to be able to parse a value like '123.' into an Integer. I have updated my code to handle this as well.
Note: This converts "1.0", but returns null on "1.9".
If you want to allow for rounding, then tweak the logic in the "THEN" clause to add Round() like so:
ROUND(CAST(REPLACE(REPLACE(#IntString,'$',''),',','') as Decimal(10)), 0)
You must also remove the "AND" that checks for "decimal-points" to allow for Rounding or Truncation.
Why not use the following and test for 1?
DECLARE #TestValue nvarchar(MAX)
SET #TestValue = '1.04343234e5'
SELECT CASE WHEN ISNUMERIC(#TestValue) = 1
THEN CASE WHEN ROUND(#TestValue,0,1) = #TestValue
THEN 1
ELSE 0
END
ELSE null
END AS Analysis
If you are purely looking to verify a string is all digits and not just CAST-able to INT you can do this terrible, terrible thing:
select LEN(
REPLACE( REPLACE( REPLACE( REPLACE( REPLACE( REPLACE( REPLACE( REPLACE( REPLACE( REPLACE(
'-1.223344556677889900e-1'
,'0','') ,'1','') ,'2','') ,'3','') ,'4','') ,'5','') ,'6','') ,'7','') ,'8','') ,'9','')
)
It returns 0 when the string was empty or pure digits.
To make it a useful check for "poor-man's" Integer you'd have to deal with empty string, and an initial negative sign. And manually make sure it isn't too long for your variety of INTEGER.