use case when to show column conditional value in MySQL - mysql

I would like to add a column and show customer rewards status based on their points.
However, MySQL states there is an error in the syntax which I cannot figure out why.
My code is as below, I got the error msg that SELECT is not valid at this position.
SELECT customer_id, first_name,
CASE points
WHEN > 3000 THEN 'Gold'
WHEN BETWEEN 2000 to 3000 THEN 'Silver'
ELSE 'Bronze'
END AS rewards_status
FROM customers

The short syntax for case, which you are using here (like: case <expr> when <val> then ... end) only supports equality condition; this does not fit your use case, which requires inequality conditions. You need to use the long case syntax (like: case when <condition> then ... end).
Also, there is no need for between in the second condition. Branches of a case expression are evaluated sequentially, and the process stops as soon as a condition is fullfilled, so you can do:
select
customer_id,
first_name
case
when points > 3000 then 'Gold'
when points > 2000 then 'Silver'
else 'Bronze'
end as reward_status
from customers

You may use the alternative syntax for CASE expressions here:
SELECT
customer_id,
first_name,
CASE WHEN points > 3000 THEN 'Gold'
WHEN points BETWEEN 2000 to 3000 THEN 'Silver'
ELSE 'Bronze' END AS rewards_status
FROM customers;

Related

count and sum in case statement

What is the difference below if I use case instead of sum? I believe I would get the same output?
SELECT SUM(CASE WHEN salary > 100000 THEN 1 ELSE 0 END) AS Total
SELECT COUNT(CASE WHEN salary > 100000 THEN 1 END) AS Total
SELECT COUNT(CASE WHEN salary > 100000 THEN 1 ELSE NULL END) AS Total
Thanks!
Per the other answer, all forms are equivalent. There also a couple of other form that are more compact and achieve the same result:
count_if(salary > 100000)
count(if(salary > 100000, 1))
However, the idiomatic and more general way to do this in Trino (formerly known as Presto SQL) is:
SELECT count(*) FILTER (WHERE salary > 100000) AS Total
FROM ...
See the documentation for more details about filtered aggregations.
All other forms except for the one based on SUM should, per the SQL specification, raise a warning to indicate that null values have been eliminated. This behavior is not yet implemented in Trino, but will be added at some point in the future.
The three are equivalent. All of them count the number of rows that meet the particular condition (salary > 100000). All return 0/1 and would not return NULL values for the column.
From a performance perspective, all should be equivalent as well. I have a personal preference for the first version. I consider the third to be unnecessarily verbose because else NULL is the default for a case expression.

How to write proper MySQL case statement

I’ve typed out the query below and I get a syntax error after the first when of the the CASE part. Please help correct.
SELECT state.name, country.name, AVG(state_weather_stats.humidity) AS average_monthly_humidity,
CASE AVG(state_weather_stats.temperature)
WHEN >=0 AND < 15 THEN “COLD”
WHEN >= 15 AND <30 THEN “WARM”
ELSE “HOT”
END AS weather_type
If you're using inequalities, you can't use the CASE <expr> WHEN ... syntax. That syntax is only usable when you're doing equality comparisons.
So you can use the CASE WHEN <expr> ... syntax instead:
SELECT state.name, country.name, AVG(state_weather_stats.humidity) AS average_monthly_humidity,
CASE
WHEN average_monthly_humidity >= average_monthly_humidity THEN 'COLD'
WHEN average_monthly_humidity >= 15 AND average_monthly_humidity <30 THEN ...something...
ELSE 'HOT'
END AS weather_type
I don't know what you want where I put "...something..." but you do need a THEN clause before the ELSE.

SQL Server (T-SQL): Avoid call scalar function multiple times

I have a stored procedure which does a SELECT to return some rows. Within the SELECT, I need to check a condition in order to return the correct value for some columns. This condition consists on a scalar function. All times scalar function is called with the same parameter for the row being processed, see below:
SELECT
Id,
Name,
Surname,
CASE WHEN (dbo.GetNumTravels(Id) >= 50)
THEN 10
ELSE 99
END as Factor1,
CASE WHEN (dbo.GetNumTravels(Id) >= 50)
THEN 15
ELSE -1
END as Factor2,
CASE WHEN (dbo.GetNumTravels(Id) >= 50)
THEN 30
ELSE 70
END as Factor3
FROM
Employees
WHERE
DepartmentId = 100
I am worried about performance, I mean, I do not like to call scalar function dbo.GetNumTravels multiples times, so how to avoid this and only call it once and then used it all the times I need it?
Scalar user defined functions are infamous for poor performance. If you can convert it to an inline table-valued function you can expect to see performance gains.
If you convert your scalar function to an inline table-valued function you can call it once for each row using cross apply() like so:
select
Id,
Name,
Surname,
case when x.NumTravels >= 50
then 10
else 99
end as Factor1,
case when x.NumTravels >= 50)
then 15
else -1
end as Factor2,
case when x.NumTravels >= 50
then 30
else 70
end as Factor3
from Employees
cross apply dbo.GetNumTravels_itvf(e.Id) x
where DepartmentId = 100
Reference:
When is a sql function not a function? "If it’s not inline, it’s rubbish." - Rob Farley
Inline Scalar Functions - Itzik Ben-Gan
Scalar functions, inlining, and performance: An entertaining title for a boring post - Adam Machanic
tsql User-Defined Functions: Ten Questions You Were Too Shy To Ask - Robert Sheldon
You can achieve this by using derived table concept, In derived table we once called to function dbo.GetNumTravels(Id) only once and used its output in outer query, this may help to gaining performance at some level by avoiding multiple calls to same function.
SELECT
Id,
Name,
Surname,
CASE WHEN (NumTravelsID >= 50) THEN 10 ELSE 99 END as Factor1,
CASE WHEN (NumTravelsID >= 50) THEN 15 ELSE -1 END as Factor2,
CASE WHEN (NumTravelsID >= 50) THEN 30 ELSE 70 END as Factor3
FROM (
SELECT
Id,
Name,
Surname,
dbo.GetNumTravels(Id) as NumTravelsID
FROM Employees
WHERE DepartmentId = 100
)M
I am not sure about performances (anyway, do your tests considering other answers too), but I would like to test this. I tried to reduce use of function and use of CASE too. Pls let me know
SELECT A.*
, 10*F0+99*~F0 AS FACTOR1
, 15*F0-1*~F0 AS FACTOR2
, 30*F0+70*~F0 AS FACTOR3
FROM (
SELECT
Id,
Name,
Surname,
CAST(CASE WHEN (dbo.GetNumTravels(Id) >= 50) THEN 1 ELSE 0 END AS BIT) AS F0
FROM Employees
WHERE DepartmentId = 100
) A

DATEDIFF SQL Query

I am at the final stage of my project and have the problem to find if a job is overdue. I link this to priority for example if a job has a priority of 1 it must be complete in 1 day, a priority of 4 then 4 days.
I have come up with a CASE however this doesn't seem to work any help would be appreciated.
SELECT `defect_Id`,`Overtasked`
WHERE
CASE DATEDIFF(DD,`date_Investigaton` - `CURRENT_DATE()`) >= `priority` AS Overtasked
THEN `Overtasked` == 'YES'
ELSE `Overtasked` == 'NO'
END
Solution
`SELECT defect_Id,
CASE WHEN DATEDIFF(date_Investigated, CURDATE()) >= priority
THEN 'YES'
ELSE 'NO'
END AS Overtasked
FROM defect_report
WHERE defect_Id = '82'`
Appreciate the guidance you guys give!
You are completely mixing up SQL dialects and even there are syntax errors.
Assuming you are talking about MS SQL Server let's try this:
SELECT defect_Id,
CASE WHEN DATEDIFF(DD, date_Investigaton, getdate()) >= priority
THEN 'YES'
ELSE 'NO'
END AS Overtasked
FROM <YourTable>
WHERE <YourWhereIfAny>
If date_Investigation is a DATE column, the subtraction date_Investigation - CURRENT_DATE() produces the number of days you need.
Otherwise (if it is a DATETIME, for example) both operands are converted to float and the result is something you are totally not expecting. For such situations use the DATEDIFF() function. It interprets its arguments as DATE (ignores the time part) and returns the integer number of days between the two dates.
Your query should be like:
SELECT
`defect_Id`,
IF (DATEDIFF(`date_Investigaton`, CURRENT_DATE()) >= `priority`, 'YES', 'NO')
AS `Overtasked`
FROM [...your table name here...]
WHERE [...conditions...]
Replace the parts in square brackets ([...]) with the name of the table where to get the data from and some conditions to limit the number of returned rows (otherwise it will get the entire table which, most probably, is not what you want).
Btw, CURRENT_DATE() is also a function. If you write it in backquotes (``), MySQL will try to find a column with this name and it will fail.
Read the accepted answer for this question. It explains when to use back ticks, single quotes or double quotes in MySQL (and partially in PHP).

Why is this "where" clause not limiting sql results

I'm trying to limit results in a table to records with dates that don't overlap my data. As you can see in the screenshot below I'm trying to add a clause to filter out records that are equal to the "end" column. See the final line in the query for that.
I can't figure out why results still show the record in the screenshot. Can someone help me out explaining that? It's probably a syntax thing?
You basically have:
a OR b AND c AND d AND e AND f
... and you probably want:
(a OR b) AND c AND d AND e AND f
Reference: Operator Precedence
AND has a higher operator precedence than OR has.
That means that your AND clauses will be interpreted first so that at the end there's something like
where (...) or (... and ... and ...)
Since the first condition before the OR (('2014-01-14 18:30:00' between start and end)) is met, your row shows up. Put both sides of the OR clause in parantheses and it should work as you want to.
The problem is the first part of your WHERE clause is passing that record. I believe you need to more your brackets around a bit.
I'm not completely sure on what you are trying to achieve with the data that you have provided, but an opening bracket after the OR keyword and a closing bracket right at the end should do it.
Like this:
where
('2014-01-14 18:30:00' between start and end)
or (('2014-01-14 19:30:00' between start and end)
and ('2014-01-14 18:30:00' != start)
and ('2014-01-14 19:30:00' != end)
and ('2014-01-14 19:30:00' != start)
and ('2014-01-14 18:30:00' != end))
Because 2014-01-14 18:30 is between 2014-01-14 16:00 and 2014-01-14 18:30 which is a TRUE condition. And [(TRUE) Or (whatever else)] is also TRUE