Case Statement in Select Not Working - sql-server

This is for SQL Server 2012 database...
I'm reading a varchar data column from a table, and depending on user-selected options the data could be either alphanumeric or numeric. I need to sort by this column so I'm trying to use a case statement, but it doesn't seem to be working. Below is a simplified example of what I'm doing, but as you can see, it's falling through to the Else of the case statement in both scenarios...any ideas on what I'm doing wrong?
Select '1st Grade Math' topic Into #temp
Declare #rptView int
Set #rptView = 1
Select Case #rptView
When 1 Then topic
Else cast(topic as int)
End
From #temp
Order by Case #rptView
When 1 Then topic
Else cast(topic as int)
End
Select Case
When #rptView = 1 Then topic
Else cast(topic as int)
End
From #temp
Order by Case #rptView
When 1 Then topic
Else cast(topic as int)
End
drop table #temp

Consider the following example based on your table:
Select Case 1 When 1 Then topic
Else 5
End
From #temp
It also fails with the following error:
Conversion failed when converting the varchar value '1st Grade Math' to data type int.
Why? Because every expression must have a well-defined data type. SQL Server deduces that the type of your first column is int, since the ELSE clause contains an int. Thus, it tries to convert topic to int as well, which fails.
In other words: You can't do it like that. The field in your result set can be varchar or int, not both.

Adding few more examples which may help..
declare #a int=1
declare #b varchar='b'
--this works
select
case when #a=1 then #a else #b end
--this also works
select
case when #a=2 then #b else #b end
--this fails
select
case when #a=1 then #b else #a end
--this fails
select
case when #a=2 then #a else #b end
Why ..?Because of data type precedence ,SQL tries to convert everything to type with higher precedence

Related

Conversion failed when converting the varchar value '$400,000.00' to data type int

I have the following part of query that works fine:
CONVERT(varchar(15), CONVERT(money, AmountOfInsurance), 1) AS AmountOfInsurance
I want to prevent anounts that are equal to 0 to show up formated, just to show up: 0, so I added this CASE statement but I get the following error:
CASE WHEN AmountOfInsurance > 0 THEN '$' + CONVERT(varchar(15), CONVERT(money, AmountOfInsurance), 1) ELSE 0 END AS AmountOfInsurance
Any idea?
Your ELSE should be '' because you want to return a varchar. Now the CASE expression has two data types and INT takes precedence over varchar, that's why it tries to convert the varchar back to INT.
Added for reference:
Data Type Precedence
Just another option (if 2012+) is Format() with a conditional format.
Declare #YourTable table (AmountOfInsurance money)
Insert Into #YourTable values
(400000),
(2500),
(0)
Select format(AmountOfInsurance,IIF(AmountOfInsurance>0,'$#,##0.00','0'))
From #YourTable
Returns
$400,000.00
$2,500.00
0
Use the CAST function to cast
DECLARE #text AS NVARCHAR(10)
SET #text = '100$'
SELECT CASE WHEN AmountOfInsurance > 0 THEN CAST(#text AS MONEY) ELSE 0 END

Using ISNUMERIC fails in condition

I have a table like this (simplified):
CREATE TABLE #table (Id INT, Field NVARCHAR(MAX))
INSERT INTO #table VALUES (1, 'SomeText')
INSERT INTO #table VALUES (2, '1234')
For some reasons I need to query this table and get the sum of Field if it is numeric and return '' if it is not. I tried it like this:
SELECT CASE WHEN ISNUMERIC(Field) = 1 THEN SUM(CONVERT(MONEY, Field)) ELSE '' END
FROM #table
GROUP BY Field
But this query leads to the following exception:
Cannot convert a char value to money. The char value has incorrect syntax.
I even changed the ELSE case from '' to 0 but I still get the same message.
Why do I get the exception? As far as I know, SUM(...) should not be executed when ISNUMERIC(Field) returns 0.
Select sum(case when ISNUMERIC(Field)=1 then cast(field as money) else 0 end)
from #table
Group By Field
Returns
(No column name)
1234.00
0.00
Working with mixed datatypes can be a real pain. Where possible, consider table designs that avoid this. To further complicate matters, IsNumeric does not always return what you might expect.
Filtering out the non-numerics before aggregating is one way to go:
SELECT
SUM(CONVERT(MONEY, Field))
FROM
#table
WHERE
ISNUMERIC(Field) = 1
GROUP BY
Field
;

SQL Server 2008 Case When Cast

I'm having trouble figuring this statement out. It seems that SQL Server is still executing the THEN part in the CASE WHEN statement. Please see this query.
SELECT
CASE
WHEN ISNUMERIC('INC') = 1
THEN CAST('INC' as numeric(10,2))
ELSE 'FALSE'
END AS foo
SQL Server is returning
"Error converting data type varchar to numeric"
From this query it should return FALSE and not return an Error since the THEN part was not executed.
What is wrong with my query?
The problem is that you are returning two different data types from the same column. So try this one -
DECLARE #value CHAR(3)
SET #value = 66
SELECT
CASE WHEN ISNUMERIC(#value) = 1
THEN CAST(CAST(#value AS NUMERIC(10,2)) AS VARCHAR(30))
ELSE 'FALSE'
END AS foo
That is because, your query is trying to
CAST 'FALSE' as Numeric(10,2)
Try this
SELECT CASE WHEN ISNUMERIC('INC') = 1 THEN
CAST(CAST('INC' as numeric(10,2)) AS varchar(5))
ELSE 'FALSE' END AS foo
The problem is that one branch of you CASE branches returns VARCHAR and the other a number.
Try the following:
CASE WHEN ISNUMERIC('INC') = 1
THEN CAST(CAST('INC' AS NUMERIC(10,2)) AS NVARCHAR)
ELSE 'FALSE'
END AS foo

CASE in sql server -implementation Clarification?

I have this simple table in sql server :
DECLARE #tbl table( a int , b NVARCHAR(100), isCalcByA bit)
INSERT INTO #tbl
SELECT 1,'c',1
UNION ALL
SELECT 2,'d',0
Ok.
If I run this :
SELECT CASE
WHEN isCalcByA = 1 THEN a
ELSE b
END FROM #tbl
It yields an error :
Msg 245, Level 16, State 1, Line 9
Conversion failed when converting the nvarchar value 'd' to data type int.
I can understand why it is happening :
Because the data which is being accumulated (to be displayed) can't attach both int and string at the same column .
Ok
But what about this :
SELECT 'dummy'
FROM #tbl
WHERE CASE
WHEN isCalcByA = 1 THEN a
ELSE b
END IS NOT NULL
Here -
I always display string
I don't accumulate different displaying results of different types.
I'm checking them against not null rather than a string or int value.
But still I get the same error .
What am I missing ?
NB
I know I can/should do this :
SELECT 'dummy'
FROM #tbl
WHERE
(isCalcByA = 1 AND a IS NOT NULL)
OR
(isCalcByA <> 1 AND b IS NOT NULL)
(which works fine)
But I'm asking why it is not working in the first CASE situation
CASE is an expression - it returns a value of a specific type. All possible values it might return must all be convertible to some common type. The system uses the type precedences rules to consider the types of all possible return values and decide what that common type is. int has higher precedence and wins.
CASE
WHEN isCalcByA = 1 THEN CONVERT(nvarchar(100),a)
ELSE b
END
would work because now the common type selected is unambiguously nvarchar(100).
No matter if you use CASE in the SELECT or the WHERE clause. CASE expressions should return the same datatype always. So, convert both columns to a datatype that can hold both:
CASE
WHEN isCalcByA = 1 THEN CAST(a AS NVARCHAR(100))
ELSE b
END
From the CASE expression documentation:
Returns the highest precedence type from the set of types in result_expressions and the optional else_result_expression.
When the various WHEN and the ELSE part have different datatypes as results, the highest precedence is chosen from this list: Data Type Precedence and all results are converted to that datatype.
Your queries fail because int has higher precedence than nvarchar.
DECLARE #tbl table( a int , b NVARCHAR(100), isCalcByA bit)
INSERT INTO #tbl
SELECT 1,'c',1
UNION ALL
SELECT 2,'d',0
UNION ALL
SELECT null,'d',1
SELECT CASE
WHEN isCalcByA = 1 THEN CAST(a AS VARCHAR(30))
ELSE b
END FROM #tbl
Above, you are selecting two different data types based on the select.
SELECT 'dummy'
FROM #tbl
WHERE CASE
WHEN isCalcByA = 1 THEN CAST(a AS VARCHAR(30))
ELSE b
END IS NOT NULL
Above, you are selecting 'dummy' every time, regardless of condition.
So, in the first statement, you are setting the return type based on the case and the case can return two different types. In the second query, the return type is always the same type.
Don't think about CASE like it is built-in IF from regular language. It's more like ... ? ... : ... operator with strong typing - it has to result in a specific singular type. If you want to mix columns you need to cast it to for example nvarchar.
You can also think about it like the result of SELECT should be possible to be defined by CREATE TABLE.

SQL Server : converting varchar to INT

I am stuck on converting a varchar column UserID to INT. I know, please don't ask why this UserID column was not created as INT initially, long story.
So I tried this, but it doesn't work. and give me an error:
select CAST(userID AS int) from audit
Error:
Conversion failed when converting the varchar value
'1581............................................................................................................................' to data type int.
I did select len(userID) from audit and it returns 128 characters, which are not spaces.
I tried to detect ASCII characters for those trailing after the ID number and ASCII value = 0.
I have also tried LTRIM, RTRIM, and replace char(0) with '', but does not work.
The only way it works when I tell the fixed number of character like this below, but UserID is not always 4 characters.
select CAST(LEFT(userID, 4) AS int) from audit
You could try updating the table to get rid of these characters:
UPDATE dbo.[audit]
SET UserID = REPLACE(UserID, CHAR(0), '')
WHERE CHARINDEX(CHAR(0), UserID) > 0;
But then you'll also need to fix whatever is putting this bad data into the table in the first place. In the meantime perhaps try:
SELECT CONVERT(INT, REPLACE(UserID, CHAR(0), ''))
FROM dbo.[audit];
But that is not a long term solution. Fix the data (and the data type while you're at it). If you can't fix the data type immediately, then you can quickly find the culprit by adding a check constraint:
ALTER TABLE dbo.[audit]
ADD CONSTRAINT do_not_allow_stupid_data
CHECK (CHARINDEX(CHAR(0), UserID) = 0);
EDIT
Ok, so that is definitely a 4-digit integer followed by six instances of CHAR(0). And the workaround I posted definitely works for me:
DECLARE #foo TABLE(UserID VARCHAR(32));
INSERT #foo SELECT 0x31353831000000000000;
-- this succeeds:
SELECT CONVERT(INT, REPLACE(UserID, CHAR(0), '')) FROM #foo;
-- this fails:
SELECT CONVERT(INT, UserID) FROM #foo;
Please confirm that this code on its own (well, the first SELECT, anyway) works for you. If it does then the error you are getting is from a different non-numeric character in a different row (and if it doesn't then perhaps you have a build where a particular bug hasn't been fixed). To try and narrow it down you can take random values from the following query and then loop through the characters:
SELECT UserID, CONVERT(VARBINARY(32), UserID)
FROM dbo.[audit]
WHERE UserID LIKE '%[^0-9]%';
So take a random row, and then paste the output into a query like this:
DECLARE #x VARCHAR(32), #i INT;
SET #x = CONVERT(VARCHAR(32), 0x...); -- paste the value here
SET #i = 1;
WHILE #i <= LEN(#x)
BEGIN
PRINT RTRIM(#i) + ' = ' + RTRIM(ASCII(SUBSTRING(#x, #i, 1)))
SET #i = #i + 1;
END
This may take some trial and error before you encounter a row that fails for some other reason than CHAR(0) - since you can't really filter out the rows that contain CHAR(0) because they could contain CHAR(0) and CHAR(something else). For all we know you have values in the table like:
SELECT '15' + CHAR(9) + '23' + CHAR(0);
...which also can't be converted to an integer, whether you've replaced CHAR(0) or not.
I know you don't want to hear it, but I am really glad this is painful for people, because now they have more war stories to push back when people make very poor decisions about data types.
This question has got 91,000 views so perhaps many people are looking for a more generic solution to the issue in the title "error converting varchar to INT"
If you are on SQL Server 2012+ one way of handling this invalid data is to use TRY_CAST
SELECT TRY_CAST (userID AS INT)
FROM audit
On previous versions you could use
SELECT CASE
WHEN ISNUMERIC(RTRIM(userID) + '.0e0') = 1
AND LEN(userID) <= 11
THEN CAST(userID AS INT)
END
FROM audit
Both return NULL if the value cannot be cast.
In the specific case that you have in your question with known bad values I would use the following however.
CAST(REPLACE(userID COLLATE Latin1_General_Bin, CHAR(0),'') AS INT)
Trying to replace the null character is often problematic except if using a binary collation.
This is more for someone Searching for a result, than the original post-er. This worked for me...
declare #value varchar(max) = 'sad';
select sum(cast(iif(isnumeric(#value) = 1, #value, 0) as bigint));
returns 0
declare #value varchar(max) = '3';
select sum(cast(iif(isnumeric(#value) = 1, #value, 0) as bigint));
returns 3
I would try triming the number to see what you get:
select len(rtrim(ltrim(userid))) from audit
if that return the correct value then just do:
select convert(int, rtrim(ltrim(userid))) from audit
if that doesn't return the correct value then I would do a replace to remove the empty space:
select convert(int, replace(userid, char(0), '')) from audit
This is how I solved the problem in my case:
First of all I made sure the column I need to convert to integer doesn't contain any spaces:
update data set col1 = TRIM(col1)
I also checked whether the column only contains numeric digits.
You can check it by:
select * from data where col1 like '%[^0-9]%' order by col1
If any nonnumeric values are present, you can save them to another table and remove them from the table you are working on.
select * into nonnumeric_data from data where col1 like '%[^0-9]%'
delete from data where col1 like '%[^0-9]%'
Problems with my data were the cases above. So after fixing them, I created a bigint variable and set the values of the varchar column to the integer column I created.
alter table data add int_col1 bigint
update data set int_col1 = CAST(col1 AS VARCHAR)
This worked for me, hope you find it useful as well.

Resources