MySQL CASE based on previous CASE value - mysql

In MySQL, is it possible to have two CASE statements in the SELECT clause, where the second CASE statement relies on the first CASE statement?
For example, consider the following query:
SELECT CASE WHEN `user`.`id` < 500 THEN 'awesome' ELSE 'lame' END
AS `status`
, CASE WHEN `status` = 'awesome' THEN 'You rock' ELSE 'You stink' END
AS `message`
FROM `user`
Basically, the user ID determines the status, and then the status determines the message.
However, as you might have guessed, this query generates this error:
Unknown column 'status'
The only solution I have found so far is two generate a temporary table, view, or subquery, and then the message is determined by the status returned in this subquery.
Is there a way to write this query without the use of a temporary table, view or subquery? I'm trying to avoid these constructs to keep the query simple and optimized if possible. Thank you!

You can, using temporary variables:
select
#status1 := (case
when user.id < 500 then 'awesome'
else 'lame'
end) as `status`,
(case
when #status1 = 'awesome' then 'You rock'
else 'You stink'
end) as message
from
user;
Some things you must know about temp variables:
They are always preceded by #
Avoid using reserved words, just in case (that's the reason I named the variable #status1
After the # symbol, they must begin with a letter, and must not have spaces
When you update them in a single query, they are updated "left-to-right" (talking about columns) and "first-to-last" (talking about rows). That can help you calculate cummulative sums or averages.
Example (for point 2):
select #t := 1, #t := #t + 1;
#t1 | #t2
----+----
1 | 2
Example (for point 3):
select myTable.x, #t := #t + myTable.x as cummulative_x
from
(select #t := 0) as init, -- You need to initialize the variable,
-- otherwise the results of the evaluation will be NULL
myTable
order by myTable.x -- Always specify how to order the rows,
-- or the cummulative values will be quite odd
-- (and maybe not what you want)
;
x | cummulative_x
---+---------------
1 | 1
1 | 2
2 | 4
3 | 7
Temporary variables can help you do some awesome things... feel free to play around ;)
Update
If you want to define conditions on the result of this query, there are two ways to do it:
Use the above query as a data-source for a second query (i.e. make it a subquery in the from clause of another query
Create a temp table and query on it
Option 1:
select a.*
from (
-- The query with temp variables defined
)
where -- ATTENTION: you need to write the references to the column names of the subquery
Option 2: (my personal favorite)
drop table if exists temp_my_temp_table;
create temporary table temp_my_temp_table
select
#status1 := (case
when user.id < 500 then 'awesome'
else 'lame'
end) as `status`,
(case
when #status1 = 'awesome' then 'You rock'
else 'You stink'
end) as message
from
user;
-- Add all appropriate indexes to this newly created table:
-- alter table temp_my_temp_table
-- add index idx_status(`status`),
-- add index idx_mess(message);
-- Make your queries on this new temp table
select * from temp_my_temp_table
-- where ...
;
Things you must know about a temp table:
They are created on RAM (by default, and only if the table is not too big)
They are only visible to the connection that created it
They are eliminated once the connection that created it is closed (or terminated in any way)
You can't use it more than once in a FROM clause. Other than that, you can use it as any other table in your database
Another update
Just by chance I came across this question and its answer. If you want to use the result of your column (calculated with temp variables) as a condition, MySQL allows this:
select
#status1 := (case
when user.id < 500 then 'awesome'
else 'lame'
end) as `status`,
(case
when #status1 = 'awesome' then 'You rock'
else 'You stink'
end) as message
from
user
having
`status` = 'awesome';
Instead of using where use having, and refer not to the temp variable, but to the alias of the column.

Related

Add a column which is the divisible by 10 of another column

A table called test has columns :
person_id (Unique) & special_num
Table:
Outcome :
Would like to create a new column called div to determine which person got the special_num that is divisible by 10 and which person did not instead of true or false would like to have a yes or no.
I am new to MySQL and have never tried it but gave it a shot please tell me how to get this:
SELECT * FROM test WHERE special_num % 10 = 0 AS div from test;
I am unable to figure out how to input values and if it is the right way of doing it
If you want a column, then it goes in the select:
select t.*,
( (special_num % 10) = 0) as div
from t;
You need a CASE expression:
SELECT *,
CASE WHEN special_num % 10 = 0 THEN 'Yes' ELSE 'No' END AS `div`
FROM test
Here is my SQLFiddle to mimic this problem.
First step is to create a new column by ALTERing the table.
ALTER TABLE PERSONS
ADD is_special VARCHAR(3);
Now run an UPDATE query that checks if the special_num % 10 is zero or not, and if it is, then set is_special to yes.
UPDATE PERSONS SET is_special = CASE WHEN special_num % 10 = 0 THEN 'yes' ELSE 'no' END;
Having said that, it is a bad idea to store values that are derived or calculated from other fields. You may want to use this in a view, but not in a table.

MySQL - tell if column _all_ has same value

I'm trying to write a query like
if (select count(*) from Users where fkId=5000 and status='r') =
(select count(*) from Users where fkId=5000) then ..
in just one query.
What this means is, if all the rows that have fkId=5000 also have status=r, then do something.
There can be any number of rows with fkId=5000, and any fraction of those rows could have status=r, status=k, status=l, status=a etc. I'm interested in the case where ALL the rows that have fkId=5000 also have status=r (and not any other status).
The way I'm doing it now is
how many rows with id=5000 and status = 'r'?
how many rows with id=5000?
are those numbers equal? then ..
I'm trying to figure out how to rewrite this query using only 1 query, instead of 2. Keyword ALL didn't seem to be able to write such a query (<> ALL is equivalent to NOT IN). I tried a couple of GROUP BY formulations but could not get the correct result to appear.
The most efficient way to do this is:
if not exists (select 1
from users
where fkid = 5000 and (status <> 'r' or status is null)
)
This will stop the query at the first non-matching row.
I suggest you to check for any rows with status not equal to 'r'
SELECT count(*)>0 FROM Users WHERE fkId = 5000 AND status != 'r'
In the following case, if the number 1 is "true" (which it is) then you'll get Yes back, and if not you'll get No back:
SELECT IF(1, 'Yes', 'No') AS yesorno
(Go ahead -- try it!)
In your case however, the following would be more appropriate:
SELECT IF (
(SELECT COUNT(*) FROM Users WHERE fkId=5000 AND status IN('r') AND status NOT IN('1', 'a', 'k')) = (SELECT COUNT(*) FROM Users WHERE fkId=5000),
'They are equal.',
'They are not equal.'
)
AS are_they_equal
By adding AS, you can manipulate the name of the "column" that's returned to you.
Hope that helps... Also, see this page if you'd like more info.
:)
EASY!
Simply join back to the same table. Here is the complete code for testing:
CREATE TABLE Users(id int NOT NULL AUTO_INCREMENT, fkID int NOT NULL, status char(1), PRIMARY KEY (id));
INSERT Users (fkID, status) VALUES (5000, 'r');
INSERT Users (fkID, status) VALUES (5000, 'r');
INSERT Users (fkID, status) VALUES (5000, 'r');
-- The next query produces "0" to indicate no miss-matches
SELECT COUNT(*) FROM Users u1 LEFT JOIN Users u2 ON u1.id=u2.id AND u2.status='r' WHERE u1.fkID=5000 AND u2.id IS NULL;
-- now change one record to create a miss-match
UPDATE Users SET status='l' WHERE id=3 ;
-- The next query produces "1" to indicate 1 miss-match
SELECT COUNT(*) FROM Users u1 LEFT JOIN Users u2 ON u1.id=u2.id AND u2.status='r' WHERE u1.fkID=5000 AND u2.id IS NULL;
DROP TABLE Users;
So all you need to test for in the result is that it's 0 (zero) meaning everything has fkID=5000 also has status='r'
If you properly index your table then joining back to the same table is not an issue and certainly beats having to do a 2nd query.
Besides the NOT EXISTS version - which should be the most efficient as it does no counting at all and exits as soon as it finds a value that doesn't match the conditions, there is one more way, that will work if status is not nullable and will be efficient if there is an index on (fkId, status):
IF EXISTS
( SELECT 1
FROM Users
WHERE fkId = 5000
HAVING MIN(status) = 'r'
AND MAX(status) = 'r'
)
There is one difference though. The above will show false if there are no rows at all with fkId=5000, while the NOT EXISTS version will show true - which is probably what you want anyway.

Compound conditions in SQL Server 2008 stored procedure

Can you please let me know if stored procedure allow to handle compound condition as below:
if(
( (select Count(*) from dbo.Membership where EmailID=#emailID) >0)
||
((select Count(*) from dbo.Allocation where ResourceEmail=#emailID)>0))
)
Use OR instead of ||
More optimal if just checking for existence, I'd use EXISTS instead of COUNT as it will stop when it first the first existence, instead of counting them all...
IF EXISTS(SELECT 1 FROM dbo.Membership WHERE EmailId = #emailID)
OR EXISTS(SELECT 1 FROM dbo.Allocation where ResourceEmail=#emailID)
BEGIN
-- emailID exists in one of the 2 tables
END

mysql return single result from single table with complex condition

I have this table :
table(fied1 int,field2 int,field3 varchar(40));
I would like to obtion without a stored procedure something like:
declare int nr default 0;
select coalesce(max(field1),0) into nr from table where field2=? and field3=?;
if(nr = 0) then
select coalesce(max(field1),0)+1 into nr from table;
end if;
i want the value of nr from single select.
pls help !!!
Only use the following query to get one plus to last inserted value in field1 column if no match found as per where clause else it returns the max value stored in field1:
This query is handling the case if there are no records in table.
the filter crieteria "and field1<>0" in following statement is required if field1 can have value 0 also otherwise remove this filter (and field1<>0) from following query
Select coalesce(
max(field1),
(select coalesce(max(field1),0)+1 from table)
)
from table where field2=? and field3=? and field1<>0;
I think this captures your requirements:
SELECT
CASE
WHEN (max(field1) IS NULL OR max(field1) = 0) AND field2=? AND field3=? THEN (max(field1)+1)
ELSE max(field1)
END AS `field1_max`
FROM table

Usage of MySQL's "IF EXISTS"

Here are two statements that I'd like to work, but which return error messages:
IF EXISTS (SELECT * FROM gdata_calendars WHERE `group` = ? AND id = ?) SELECT 1 ELSE SELECT 0
and
IF ((SELECT COUNT(*) FROM gdata_calendars WHERE `group` = ? AND id = ?) > 0) SELECT 1 ELSE SELECT 0;
The question marks are there because I use parametrized, prepared, statements with PHP's PDO. However, I have also tried executing this with data manually, and it really does not work.
While I'd like to know why each of them doesn't work, I would prefer to use the first query if it can be made to work.
You cannot use IF control block OUTSIDE of functions. So that affects both of your queries.
Turn the EXISTS clause into a subquery instead within an IF function
SELECT IF( EXISTS(
SELECT *
FROM gdata_calendars
WHERE `group` = ? AND id = ?), 1, 0)
In fact, booleans are returned as 1 or 0
SELECT EXISTS(
SELECT *
FROM gdata_calendars
WHERE `group` = ? AND id = ?)
I found the example RichardTheKiwi quite informative.
Just to offer another approach if you're looking for something like IF EXISTS (SELECT 1 ..) THEN ...
-- what I might write in MSSQL
IF EXISTS (SELECT 1 FROM Table WHERE FieldValue='')
BEGIN
SELECT TableID FROM Table WHERE FieldValue=''
END
ELSE
BEGIN
INSERT INTO TABLE(FieldValue) VALUES('')
SELECT SCOPE_IDENTITY() AS TableID
END
-- rewritten for MySQL
IF (SELECT 1 = 1 FROM Table WHERE FieldValue='') THEN
BEGIN
SELECT TableID FROM Table WHERE FieldValue='';
END;
ELSE
BEGIN
INSERT INTO Table (FieldValue) VALUES('');
SELECT LAST_INSERT_ID() AS TableID;
END;
END IF;
The accepted answer works well and one can also just use the
If Exists (...) Then ... End If;
syntax in Mysql procedures (if acceptable for circumstance) and it will behave as desired/expected. Here's a link to a more thorough source/description: https://dba.stackexchange.com/questions/99120/if-exists-then-update-else-insert
One problem with the solution by #SnowyR is that it does not really behave like "If Exists" in that the (Select 1 = 1 ...) subquery could return more than one row in some circumstances and so it gives an error. I don't have permissions to respond to that answer directly so I thought I'd mention it here in case it saves someone else the trouble I experienced and so others might know that it is not an equivalent solution to MSSQLServer "if exists"!
If your table has an auto-incrementing primary key, you can use REPLACE INTO ... VALUES
SELECT #id := id FROM tableName WHERE fieldName='criteria value' LIMIT 1;
REPLACE INTO tableName(id, fieldName, col1, col2)
VALUES (#id, 'criteria value', 'value1', 'value2')
If the select statement returns NULL, then a new row is inserted.
Otherwise, if a row is found, it will update the row with key #id.
SELECT IF((
SELECT count(*) FROM gdata_calendars
WHERE `group` = ? AND id = ?)
,1,0);
For Detail explanation you can visit here