SQL "select case" gives unexpected output - sql

For the love of god can someone please explain me what is going on here.
I am working on some stored procedure bug fixes, after a lot of struggle, I was able to find out where this strange bug is happening so I've made a simple example for the sake of demonstration.
Case 1
DECLARE #test VARCHAR(45) ='0001'
SELECT CASE 0
WHEN -1 THEN ''
WHEN 0 THEN #test
WHEN 1 THEN 12345
END AS 'output'
Case 2
DECLARE #test VARCHAR(45) ='0001'
SELECT CASE 0
WHEN -1 THEN ''
WHEN 0 THEN #test
END AS 'output'
Case 1: output is 1
Case 2: output is 0001 as expected
What happened to the zeros?
strangely it only removes zeros before the number (no matter what number comes at the end), for example DECLARE #test VARCHAR(45) ='1000' works fine, as far case is concerned it is clear that case is zero, if I remove WHEN 0 THEN #test output is null as expected.
Done some research, found only this.
I am using SQL SERVER RC1 2017 as well as SQL SMS.
Thank you for your time.

The difference here is that you have mutliple data types in your THEN/ELSE expressions. In your "CASE 1" you have both int and and varchar datatypes.
int has a higher Data type precedence (Transact-SQL) than a varchar, so the values are returned as an int.
Use the same datatype through out, and this works as you want it to:
DECLARE #test VARCHAR(45) ='0001';
SELECT CASE 0 WHEN -1 THEN ''
WHEN 0 THEN #test
WHEN 1 THEN '12345' --Note that the value in contained in single quotes
END AS [output];

This is because of data type precedence.
The CASE expression documentation says about the returned data type:
Returns the highest precedence type from the set of types in
result_expressions and the optional else_result_expression
Meaning it looks every set of types in the result expressions. Since you have one where the returned data is an INT, then the result of the whole CASE expression is INT (since it has a higher precedence than VARCHAR)

This is happening because the type of the value returned in your last case statement is numeric, so try to change it to
DECLARE #test VARCHAR(45) ='0001'
SELECT CASE 0
WHEN -1 THEN ''
WHEN 0 THEN #test
WHEN 1 THEN '12345'
END AS 'output'

Related

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

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

TSQL CASE unexpectedly processing multiple THEN statements

I have a simple SQL CASE statement that I am trying to interpret the datatype of a variable and then converting it to said format. I have the following sample:
DECLARE #P1 varchar(10) = '2';
SELECT CASE 1
WHEN ISNUMERIC(#P1) THEN (CAST(#P1 AS INT))
WHEN ISDATE(#P1) THEN (CAST(#P1 AS DateTime))
ELSE 'N' END AS Test2P1
I would expect it to return an integer value of 2. However, this is what I get.
Test2P1
1900-01-03 00:00:00.000
However, if I modify the code to just display a character instead of CASTING the variable, I get a different result:
DECLARE #P1 varchar(10) = '2';
SELECT CASE 1
WHEN ISNUMERIC(#P1) THEN 'I'
WHEN ISDATE(#P1) THEN 'D'
ELSE 'N' END AS Test2P1
Then I get a result of: I
Taking this experiment one step further, if I leave the first test returning an 'I' but casting the date, then I get an error:
DECLARE #P1 varchar(10) = '2';
SELECT CASE 1
WHEN ISNUMERIC(#P1) THEN 'I'
WHEN ISDATE(#P1) THEN (CAST(#P1 AS DateTime))
ELSE 'N' END AS Test2P1
Msg 241, Level 16, State 1, Line 3
Conversion failed when converting date and/or time from character string.
It appears that SQL is evaluating multiple instances of THEN statements. However, the ISDATE expression does NOT return true. I am stumped.
In my research, I came across this article [SQL Performance] (https://sqlperformance.com/2014/06/t-sql-queries/dirty-secrets-of-the-case-expression) What he is saying in this article makes complete sense. But I do not see where it applies in this situation as I am never changing the value of the variable.
I have also wrapped my CASE statement in COALESCE(), but it does not appear to help. I wouldn't have expected it to, but I found multiple articles referencing this as a solution to similar problems. This seems like a very simple CASE statement, but I do not know where I am going wrong. Any help is greatly appreciated.
DateTime has a higher datatype precedence in SQL Server than INT does, so your whole CASE is getting cast as DATETIME. You can only return data of a single datatype in one column, thus the need for datatype precedence.
Here is the MSDN article showing precedence.
Although I can't think of a good reason to do this, you could force the case expression to return sql_variant, which can store multiple different types, eg:
DECLARE #P1 varchar(10) = '2';
SELECT CASE 1
when 0 then cast(null as sql_variant)
WHEN ISNUMERIC(#P1) THEN (CAST(#P1 AS INT))
WHEN ISDATE(#P1) THEN (CAST(#P1 AS DateTime))
ELSE 'N' END AS Test2P1

Why is SQL Server trying to convert my nvarchar(20) datatype to an int?

I'm getting the "conversion" error in a SQL Select query.
The error is:
Msg 248, Level 16, State 1, Line 6
The conversion of the nvarchar value '7000952682' overflowed an int column.
Problem is, there are no int columns in my table!
Here is the table structure:
Here is the query:
If I set the value of #SU to NULL, then it does return all rows as expected. When the value is set to the string value of '7000952682' I get the error.
Why is SQL Server trying to convert the nvarchar value to an int?
All branches of a CASE expression have to have the same type. In this case (no pun intended), it looks like SQL Server is using an integer type and doing an implicit cast of SU to integer. The problem is that the max value for an integer in SQL Server is roughly 2.1 billion, and the example you gave is using the value 7000952682, hence the overflow.
You have two options here. You could make everything varchar:
CASE WHEN #SU IS NULL OR #SU = '' THEN '1' ELSE [SU] END
Or, you could make everything numeric, using a type that won't overflow, e.g.
CASE WHEN #SU IS NULL OR #SU = ''
THEN CAST(1 AS numeric(20, 6))
ELSE CAST([SU] AS numeric(20, 6)) END
As a side note, you could write the first part of your CASE expression more succinctly using COALESCE:
CASE WHEN COALESCE(#SU, '') = '' THEN '1' ELSE [SU] END
Don't use case in the where clause. The logic is more simply and accurately expressed as:
where (#su is null or #su = '' or #su = su)

Remove only zero after decimal sql server 2012

Consider the following numbers.
7870.2
8220.0
I need to remove decimal points if the value ends with .0. If it ends with .2 then it should keep the value as it is.
I have used ceiling but it removes all the values after decimal.
How can I write a select query in which I can add some condition for this?
Generally speaking you should not do this in your dB. This is an app or reporting side operation. The dB is made to store and query information. It is not made to format/string manipulate information.
use right within a case statement and:
DECLARE #val decimal(5,1)
SET #val = 7870.0
Select
Case
When right(#val,1)<> '0' then
cast(#val as varchar)
else
cast(cast(#val as int) as varchar)
End
output: 7870
EDIT: I could write :
Case
When right(#val,1)<> '0' then
#val
else
cast(#val as int) -- or floor(#val)
End
but because return type of case statement is the highest precedence type from the set of given types, so the output for second version is: 7870.0 not 7870, that's why I convert it to i.e varchar in when clauses, and it can be converted outside of case statement, I mean cast ((case when...then...else... end) as datatype)
Cast the number as a float, using float(24) to increase precision:
DECLARE #t table(number decimal(10,1))
INSERT #t values(7870.2),(8220.0)
SELECT cast(number as float(24))
FROM #t
Result:
7870,2
8220
Here below goes a sample:
declare #1 decimal(4,3)
select #1 = 2.9
select case when SUBSTRING (PARSENAME(#1,1), 1, 1) = 0 then FLOOR(#1) else #1 end
Change the #1 in the select statement with your database field name.
sqlfiddle
The solution seems to be simple:
SELECT CONVERT (FLOAT, PAYLOAD)

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.