CAST and CASE in SQL SELECT statement - sql

I'm trying to return a string when certain conditions are true, but the I'm running into a data type issue... LI.num_seats_pur and LI.num_seats_ret are both smallint data types...
Here's where I'm stuck:
SELECT
CASE
WHEN (LI.num_seats_ret = LI.num_seats_pur)
THEN 'RET'
ELSE (LI.num_seats_pur - LI.num_seats_ret)
END as 'Seats'
FROM T_LINEITEM LI;
I understand that 'RET' is obviously not a smallint, but every combination of CAST I use here is still causing an error. Any ideas?

When using a CASE expression, if the return values have different data types, they will be converted to the one with the higher data type precedence. And since SMALLINT has a higher precedence than VARCHAR, the return value of the ELSE part, 'RET' gets converted to SMALLINT. This will then proceed to a conversion error:
Conversion failed when converting the varchar value 'RET' to data type smallint.
In order to achieve the desired result, you need to CAST the ELSE part to VARCHAR:
SELECT
CASE
WHEN (LI.num_seats_ret = LI.num_seats_pur)
THEN 'RET'
ELSE
CAST((LI.num_seats_pur - LI.num_seats_ret) AS VARCHAR(10))
END AS 'Seats'
FROM T_LINEITEM LI;

Related

Select case returning an error when both elemements not varchar in some cases

I wanted to return a value formatted with commas at every thousand if a number or just the value if it wasn't a number
I used the following statement which returned the error:
Conversion failed when converting the nvarchar value '1,000' to data type int.
Declare #QuantityToDelete int = 1000
SELECT CASE
WHEN ISNUMERIC(#QuantityToDelete)=1
THEN format(cast(#QuantityToDelete as int),'N0')
ELSE #QuantityToDelete
END [Result]
I can get it to work by using the following
SELECT CASE
WHEN ISNUMERIC(#QuantityToDelete)=1
THEN format(cast(#QuantityToDelete as int),'N0')
ELSE cast(#QuantityToDelete as varchar)
END [Result]
Result=1,000
Why doesn't the first example work when the ELSE #QuantityToDelete part of the statement isn't returned?
If I use the below switching the logic condition
SELECT CASE
WHEN ISNUMERIC(#QuantityToDelete)=0
THEN format(cast(#QuantityToDelete as int),'N0')
ELSE #QuantityToDelete
END [Result]
Result=1000
Which is expected, but no error, the case statement still has unmatched return types an nvarchar and an int as in the first example just different logic?
The important point to note is that a case expression returns a single scalar value, and that value has a single data type.
A case expression is fixed, it must evaluate the same and work the same for that query at runtime no matter what data flows through the query - in other words, the result of the case expression cannot be an int for some rows and a string for others.
Remember that the result of a query can be thought of, and used as, a table - so just like a table where you you define a column as being a specific data type, you cannot have a column where the data type can be different for rows of data.
Therefore with a case expression, SQL Server must determine at compile time what the resulting data type will be, which it does (if necessary) using data type precedence. If the case expression has different data types returned in different execution paths then it will attempt to implicitly cast them to the type with the highest precedence.
Hence your case expression that attempts to return two different data types fails because it's trying to return both a nvarchar and int and SQL Server is implicitly casting the nvarchar value to an int - and failing.
The second one works because you are controlling the casting and both paths result in the same varchar data type which works fine.
Also note that when defining a varchar it's good practice to define its length also, you can easily get complacent as it works here because the default length is 30 when casting however the default is 1 otherwise.
See the relevant part of the documentation

When using cast - error: invalid input syntax for type numeric: "" (postgreSQL)

Duration column in the table is given with 'varchar' data type. It contains decimal values. So I am trying to cast varchar to float/numeric/decimal/double/double precision. But none of those works. why is it not working?
select runner_id,
sum(case when cast(duration as decimal) <> '' then 1
else 0 end) as delivered, count(order_id) as total_orders
from t_runner_orders
group by runner_id
The reason it's not working is because your duration column contains values which cannot be cast to a numeric type. The specific value throwing the error is an empty string. Also, you shouldn't be comparing a numeric type to an empty string.
Also, if you're comparing a varchar column to a character value in your CASE statement, why are you trying to cast it to a numeric type at all?
For what you're doing here, I would just write it as CASE WHEN duration <> '' THEN 1 ELSE 0 END
And if you do need to cast it to a numeric type at some point, the way to do that would be something like CASE WHEN duration = '' THEN NULL ELSE cast(duration AS DECIMAL) END (asuming that empty strings are the only values in your column which cannot be cast to decimal)
The problem is you are doing the CAST before the <> ''. The cast fails as there are empty strings in the field. You have several choices:
Use NULL instead of '' in field.
Do duration <> ''
Last and probably the best long term solution change the column type to numeric.
You can translate '' to null using NULLIF in the cast.
cast(nullif(duration,'') as decimal) is not null
However this will not solve you basic problem which is "varchar' data type. It contains decimal values" NO it does not it contains a string which you hope are decimal values, but nothing prohibits putting 'zero.zero' into it - distinctly not a decimal value. I will go #AdrianKlaver one step further.
3. The only long term solution change the column type to numeric.

t-sql Different datatype possible in a case?

I have this query
SELECT
CASE WHEN dbo.CFE_PPHY.P77 IS NOT NULL OR dbo.CFE_PPHY.P77 <>''
THEN MONTH(dbo.CFE_PPHY.P77)
WHEN dbo.CFE_PPHY.P70 IS NOT NULL OR dbo.CFE_PPHY.P70 <>''
THEN MONTH(dbo.CFE_SERVICE_EVTS.C10_2)
ELSE COALESCE(CONVERT(VARCHAR,dbo.CFE_PPHY.P77)+
CONVERT(VARCHAR,dbo.CFE_SERVICE_EVTS.C10_2),'toto') END
AS CFELiasse_DateEffetEIRL_MM_N
FROM CFE_PPHY LEFT JOIN CFE_SERVICE_EVTS ON CFE_PPHY.colA = CFE_SERVICE_EVTS.colB
The ELSE part is giving me headaches.
The columns CFE_PPHY.P77 and CFE_SERVICE_EVTS.C10_2 have date time format. I'm turning them into varchar. Yet when I'm running the query, I have the following error
Msg 245, Level 16, State 1, Line 1 Conversion failed when converting the varchar value 'toto' to data type int.
Obviously, I cannot turn toto to an integer. Fair enough. However, from my point of view, I've converted the datetime format to a varchar format, so it should do the work.
Where am I wrong?
Thanks
You have to convert all of your case expressions to varchar. SQL is deciding to case the field as int so 'toto' is invalid. If all expressions are converted to varchar this error should be solved.
http://blog.sqlauthority.com/2010/10/08/sql-server-simple-explanation-of-data-type-precedence/
Have a closer look at your case expression: in the first and second conditional branches you're returning MONTH(... which is obviously integer.
But in third branch you're returning varchar thus SQL server tries to convert it to int according to data type of previous branches and failing to do it.
Try like this,
SELECT CASE
WHEN dbo.CFE_PPHY.P77 IS NOT NULL
OR dbo.CFE_PPHY.P77 <> ''
THEN convert(VARCHAR, MONTH(dbo.CFE_PPHY.P77))
WHEN dbo.CFE_PPHY.P70 IS NOT NULL
OR dbo.CFE_PPHY.P70 <> ''
THEN convert(VARCHAR, MONTH(dbo.CFE_SERVICE_EVTS.C10_2))
ELSE COALESCE(CONVERT(VARCHAR, dbo.CFE_PPHY.P77) + CONVERT(VARCHAR, dbo.CFE_SERVICE_EVTS.C10_2), 'toto')
END AS CFELiasse_DateEffetEIRL_MM_N
FROM CFE_PPHY
LEFT JOIN CFE_SERVICE_EVTS ON CFE_PPHY.colA = CFE_SERVICE_EVTS.colB
First, when converting to a string, always include a length (in SQL Server). The default length varies by context and may not be correct.
Second, the comparison of date/time values to '' is not necessary. This is not really valid value for a date/time -- although it does get converted to a 0 which is 1900-01-01. The NULL comparison should be sufficient. Otherwise, be explicit.
Third, string concatenation will return NULL if any of the arguments are NULL.
Fourth, table aliases make a query easier to write and to read.
As far as I can tell, your case is a bit over complicated. In the ELSE, we know that dbo.CFE_PPHY.P77 is NULL, because of the first condition. So, how about:
SELECT (CASE WHEN p.P77 IS NOT NULL
THEN CAST(MONTH(p.P77) as VARCHAR(255))
WHEN p.P70 IS NOT NULL
THEN CAST(MONTH(e.C10_2) as VARCHAR(255))
ELSE 'toto'
END) AS CFELiasse_DateEffetEIRL_MM_N
FROM CFE_PPHY p LEFT JOIN
CFE_SERVICE_EVTS e
ON p.colA = e.colB;

SQL Server : conversion error numeric to varchar case

I have this query:
CASE ClaimsFees.TBA
WHEN 0 THEN CAST(IndemnityReserve AS NUMERIC(9,2))
ELSE 'TBA'
END AS 'Reserve Indemnity'
but I always get this error:
Error converting data type varchar to numeric
I have tried to convert TBA as numeric but I can't do this, I also can't convert all the results to varchar because when I transfer to Excel file the number 1.325,27 becomes 132.527,00 with the ##,##0.00 format.
Is there a method in SQL Server that I can use to solve this?
The column can only have one type, and as 'TBA' can only be a string, you'll need to make the numeric a string too.
CASE ClaimsFees.TBA WHEN 0
then cast(cast(IndemnityReserve as NUMERIC(9,2)) as varchar(12))
else 'TBA'
end as 'Reserve Indemnity',

Why am I getting `Conversion failed when converting the varchar value 'This is full' to data type int.`?

This code has been working for the past one year but suddenly, we started getting:
Conversion failed when converting the varchar value 'This is full' to data type int
Code:
CASE
WHEN L.Seating_Capacity - COALESCE(TS.TakenSeats,0) = 0 THEN 'This is full'
WHEN l.location = 'MLK High School' AND d.trainingDates = '5/14/2014' THEN 70 - COALESCE(TS.TakenSeats,0)
ELSE CAST(L.Seating_Capacity - COALESCE(TS.TakenSeats,0) AS VARCHAR)
END AS 'AvailableSeats'
Any ideas what went wrong?
Because CASE is an expression - it computes a single value, of a single type. So all of the possible THENs (and the ELSE) must all produce values that can be converted to a single type. You can't have one THEN produce a varchar and a different one produce an int.
And since int has a higher precedence than varchar, that's the single data type that SQL Server tries to convert everything to.
CASE WHEN L.Seating_Capacity - COALESCE(TS.TakenSeats,0) = 0
THEN 'This is full'
WHEN l.location='MLK High School' AND d.trainingDates = '5/14/2014'
THEN CONVERT(varchar(10),70 - COALESCE(TS.TakenSeats,0))
ELSE CONVERT(varchar(10),L.Seating_Capacity - COALESCE(TS.TakenSeats,0))
END AS 'AvailableSeats'