Usage of MySQL's "IF EXISTS" - mysql

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

Related

MySQL CASE based on previous CASE value

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.

MySQL - INSERT multiple values conditionally

Ok, so I have a table which holds bets on games.
The table holds the columns: user_id, event_id, bet.
A user can send his/her (multiple) bets to the server in one request.
I need to insert multiple bets using one query, while checking that none of the bets
are on an event that already started/finished.
In case of at least 1 started/finished event, I don't really care if the whole query cancels, or just ignores the 'unqualified' bets.
Question
How can I insert multiple bets (rows) with one query, while conditioning the insert on a select statement (which checks for each of the events' statuses)?
Here is the query I would've used if it worked (and it doesn't of course):
INSERT INTO bet_on_event (user_id, event_id, bet)
VALUES (1,5,1), (1,6,2)
IF (SELECT COUNT(*) FROM events WHERE _id IN(5,6) AND status=0) = ?;
Explanation
1. As mentioned, the values are pre-made - requested by the user.
2. Games/events have status. 0 means a game hasn't started, so it's ok to bet.
3. The select statement just counts how many of the requested events have status 0.
4. The 'IF' should check if the count from (3) equals the number of events the user requested to bet on, thus confirming that all the events are ok to bet on.
The 'IF' should be replaced with something that work, and the whole statement can be replaced if you have a better idea for what I'm trying to achieve.
A simpler query (which isn't enough for my case, but works with 1 row) is:
INSERT INTO bet_on_event (user_id, event_id, bet)
SELECT 1,5,1 FROM dual
WHERE (SELECT COUNT(*) FROM events WHERE _id IN(5,6) AND status=0) = ?;
Any idea? Is this even possible? Betting is gonna be used a lot, so I want to do it as quick as possible - with 1 query.
Thank you so much.
EDIT
That is what I ended up doing, taken from Thorsten's answer (I changed it to a dynamically built query, as that is what I need):
var query='INSERT INTO bet_on_event (user_id, event_id, bet)';
for(var i=0; i<eventIds.length; i++){
query+= ' SELECT ' + userId + ',' + eventIds[i] + ',' + bets[i]
+ ' FROM dual WHERE EXISTS (SELECT * FROM events WHERE id = ' + eventIds[i]
+ ' AND Status = 0)';
if(i < eventIds.length - 1){
query += ' UNION ALL';
}
}
Where eventIds and bets are in a corresponding order (like a map)
EDIT 2
I also wanted to UPDATE the bets which already exist (in case the user wanted to...).
So there's a need to update each row with the relevant bet in the array. This is the solution:
ON DUPLICATE KEY UPDATE bet=VALUES(bet)
Just added (concatenated) to the end of the query...
Does this work for you? It inserts 1,5,1 if there is no event for id 5 that has started. Same for 1,6,1 and id 6.
INSERT INTO bet_on_event (user_id, event_id, bet)
SELECT 1,5,1 FROM dual WHERE NOT EXISTS
(SELECT * FROM events WHERE _id = 5 AND Status <> 0)
UNION ALL
SELECT 1,6,1 FROM dual WHERE NOT EXISTS
(SELECT * FROM events WHERE _id = 6 AND Status <> 0);
EDIT: If you don't want to insert anything in case one or more of the games have started, you can simply replace WHERE _id = 5 and WHERE _id = 6 with WHERE _id IN (5,6). Or have just one exists clause:
INSERT INTO bet_on_event (user_id, event_id, bet)
SELECT *
FROM
(
SELECT 1,5,1 FROM dual
UNION ALL
SELECT 1,6,1 FROM dual
) tmp
WHERE NOT EXISTS (SELECT * FROM events WHERE _id IN (5,6) AND Status <> 0);
have you tried with UNION ?
INSERT INTO bet_on_event (user_id, event_id, bet)
(SELECT 1,5,1 FROM dual
WHERE (SELECT COUNT(*) FROM events WHERE _id IN(5,6) AND status=0) = ?
UNION
SELECT 1,6,2 FROM dual
WHERE (SELECT COUNT(*) FROM events WHERE _id IN(5,6) AND status=0) = ? );

Different result in trigger and console (MYSQL 5.1.57)

I want to set 'isSomething' to 1 if the 'foo' is in column1 values, else 'isSomething' is 0
The problem is: the same query gives different result from console and from a trigger.
And the console version gives good result.
(everything is the same in the subquery, in the trigger I always get the same New.id! - test case)
-----TRIGGER (AFTER UPDATE)
...
DECLARE isSomething INT DEFAULT 0;
select if('foo' IN (select column1 from ...where id=NEW.id),1,0)
INTO isSomething;
...
LOG: isSomething:0
-----CONSOLE
select if('foo' IN (select column1 from ...where id=232),1,0)
into #isSomething;
select #isSomething;
...
CONSOLE: 1 (good result!!!)
COMMENT: i tried the following query as well
select count(*) into isSomething from ... where id = NEW.id and column1='foo'
And it behaves like the first query.
UPDATE 1
Interesting the 2nd type of query without "and column1='foo'"
select count(*) into isSomething from ... where id = NEW.id
Gives right result:3, as if 'foo' wouldnt be in the result.
SOLUTION
I dont know why, but if I use table alias (here:ft), then the query works and the result is good ...
select count(*)
into isSomething
from footable ft WHERE ft.column1 = 'foo' AND ft.id = NEW.id ....;
Thanks for everyone the effort!
Cs.

Using an IF Statement in a MySQL SELECT query

I am trying to use an IF statement in a MySQL select query.
I am getting an error after the AND statement where the first IF.
SELECT J.JOB_ID,E.COMPANY_NAME,J.JOB_DESC,JT.JOBTYPE_NAME,J.COMPENSATION,ST.STATE_NAME,MC.METRO_CITY_NAME,I.INDUSTRY_NAME,
J.JOB_CONTACT_PERSON,J.DT_INSRT,J.JOB_TITLE,J.JOB_EXP_DATE,J.SKILLS
FROM JOBS J
JOIN EMPLOYER E ON J.COMPANY_ID=E.COMPANY_ID
JOIN LOOKUP_JOBTYPE JT ON J.JOB_TYPE=JT.JOBTYPE_ID
JOIN LOOKUP_STATE ST ON J.STATE_ID=ST.STATE_ID
JOIN JOBS_LOCATION JL ON J.JOB_ID=JL.JOB_ID
JOIN LOOKUP_METRO_CITY MC ON JL.METRO_CITY_ID=MC.METRO_CITY_ID
JOIN LOOKUP_INDUSTRY I ON J.INDUSTRY_ID=I.INDUSTRY_ID
JOIN JOBS_QUALIFICATION JQ ON J.JOB_ID=JQ.JOB_ID
JOIN LOOKUP_DEGREE_QUALIFICATION LDQ ON LDQ.QUALIFICATION_ID = JQ.QUALIFICATION_ID
WHERE J.ACTIVE='Y' AND J.DT_INSRT > COALESCE(pEmailSntDt,DATE_SUB(SYSDATE(),INTERVAL 4 DAY))
AND
IF(JQ.COURSE_ID=0)
THEN
IF(JQ.DEGREE_ID=0)
THEN J.SKILLS LIKE CONCAT('%', pSkills,'%')
ELSE
JQ.DEGREE_ID=pDegreeId OR J.SKILLS LIKE CONCAT('%', pSkills,'%')
END IF
ELSE
JQ.COURSE_ID=pCourseId OR IF(JQ.DEGREE_ID=0)
THEN
J.SKILLS LIKE CONCAT('%', pSkills,'%')
ELSE
JQ.DEGREE_ID=pDegreeId OR J.SKILLS LIKE CONCAT('%', pSkills,'%')
END IF
END IF
GROUP BY J.JOB_ID ORDER BY J.DT_INSRT DESC;
Why doesn't this work and what is the proper way to do an IF statement in a MySQL query?
The IF/THEN/ELSE construct you are using is only valid in stored procedures and functions. Your query will need to be restructured because you can't use the IF() function to control the flow of the WHERE clause like this.
The IF() function that can be used in queries is primarily meant to be used in the SELECT portion of the query for selecting different data based on certain conditions, not so much to be used in the WHERE portion of the query:
SELECT IF(JQ.COURSE_ID=0, 'Some Result If True', 'Some Result If False'), OTHER_COLUMNS
FROM ...
WHERE ...
How to use an IF statement in the MySQL "select list":
select if (1>2, 2, 3); //returns 3
select if(1<2,'yes','no'); //returns yes
SELECT IF(STRCMP('test','test1'),'no','yes'); //returns no
How to use an IF statement in the MySQL where clause search condition list:
create table penguins (id int primary key auto_increment, name varchar(100))
insert into penguins (name) values ('rico')
insert into penguins (name) values ('kowalski')
insert into penguins (name) values ('skipper')
select * from penguins where 3 = id
-->3 skipper
select * from penguins where (if (true, 2, 3)) = id
-->2 kowalski
How to use an IF statement in the MySQL "having clause search conditions":
select * from penguins
where 1=1
having (if (true, 2, 3)) = id
-->1 rico
Use an IF statement with a column used in the select list to make a decision:
select (if (id = 2, -1, 1)) item
from penguins
where 1=1
--> 1
--> -1
--> 1
If statements embedded in SQL queries is a bad "code smell". Bad code has high "WTF's per minute" during code review. This is one of those things. If I see this in production with your name on it, I'm going to automatically not like you.
try this code worked for me
SELECT user_display_image AS user_image,
user_display_name AS user_name,
invitee_phone,
(CASE WHEN invitee_status = 1 THEN "attending"
WHEN invitee_status = 2 THEN "unsure"
WHEN invitee_status = 3 THEN "declined"
WHEN invitee_status = 0 THEN "notreviwed"
END) AS invitee_status
FROM your_table

Another select statement if the value is NULL

I want to send completely another query if the value of the given field is NULL is it possible ?
Something like this :
IF the value is null
SELECT * FROM `locations`
ELSE
SELECT * FROM `companies`
ENDIF
I want to use the output of this query in another query. So it is going to be like this. If the field is not null in the first table take it else take another field from another table and lastly another query from the result of this if.
SELECT * FROM `locations` WHERE 1 = (CASE WHEN value IS NULL THEN 1 ELSE 0 END)
UNION ALL
SELECT * FROM `companies` WHERE 1 = (CASE WHEN value IS NOT NULL THEN 1 ELSE 0 END)
not tested but should work.
Similar to aleroot's contribution but avoiding the (IMHO) ugly case: (also untested)
SELECT * FROM (SELECT * FROM locations WHERE value IS NULL)
UNION ALL
SELECT * FROM (SELECT * FROM companies WHERE value IS NOT NULL)
SELECT
CASE WHEN L.value IS NULL THEN C.x ELSE L.x END AS x,
CASE WHEN L.value IS NULL THEN C.y ELSE L.y END AS y,
CASE WHEN L.value IS NULL THEN C.z ELSE L.z END AS z,
...
FROM
locations L
LEFT JOIN companies C
ON L.LocationID=C.LocationID;
I do not know how you will have to join your tables in your case, but this gives you an idea of how you can solve the problem. In my solution, the decision whether the fields are taken from locations or companies, is taken on a record-by-record basis.
If i understand, you want to retrive one, or another query, with different table definitions, in one call, if something is null? i think is not a good practice...
Using MysqlI and a stored procedure, you can read the last select executed, but is weird:
DELIMITER //
CREATE PROCEDURE make_weird_query() BEGIN
IF something is null THEN
SELECT * FROM `locations`;
ELSE
SELECT * FROM `companies`;
ENDIF;
END;//
In PHP:
$query = $mysql->query("CALL make_weird_query()");
while($row = $query->fetch_assoc()){
// ...
}