How do I use an IF statement in an MySQL join query? - mysql

I have a SQL query that left joins a table in different ways depending on a condition.
SELECT m.id, u.first_name AS otherUser
FROM matches AS m
IF (u.id=m.user2ID)
LEFT JOIN users AS u ON u.id = m.user1ID
ELSE
LEFT JOIN users AS u ON u.id = m.user2ID
ENDIF
WHERE m.user1ID=2 OR m.user2ID=2
matches is a table with integer columns user1ID and user2ID. users is a table containing users of my web application. users has a VARCHAR field called first_name.
The intention of this query is to get the names of the users matched with the current user.
However, MySQL returns this error:
#1064 - You have an error in your SQL syntax;
check the manual that corresponds to your MySQL server version for
the right syntax to use near 'IF (u.id=m.user2ID) LEFT JOIN users
AS u ON u.id = m.user1ID ELSE LEFT JOIN user' at line 3
Why?

Specify the condition as part of the ON clause:
SELECT m.id, u.first_name AS otherUser
FROM matches AS m
LEFT JOIN users AS u ON (u.id=m.user2ID and u.id = m.user1ID) or (u.id<>m.user2ID and u.id = m.user2ID)
WHERE m.user1ID=2 OR m.user2ID=2
Another way to do the same thing:
SELECT m.id, u.first_name AS otherUser
FROM matches AS m
LEFT JOIN users AS u ON IF(u.id=m.user2ID,u.id = m.user1ID,u.id = m.user2ID)
WHERE m.user1ID=2 OR m.user2ID=2

Use this query:
SELECT m.id, u.first_name AS otherUser FROM matches AS m LEFT
JOIN users AS u ON u.id = m.user1ID AND u.id=m.user2ID LEFT JOIN users
AS u1 ON u1.id = m.user2ID WHERE m.user1ID=2 OR m.user2ID=2
Updated query:
SELECT m.id, u.first_name AS otherUser
FROM matches AS m
LEFT JOIN users AS u ON u.id = (CASE WHEN u.id=m.user2ID
THEN m.user1ID
ELSE m.user2ID
END)
WHERE m.user1ID=2 OR m.user2ID=2
Check this Source.
Also check this MYSQL Inner Join if statement . It might be helpful for you.

You can also have two LEFT JOIN on the same table like this...
LEFT JOIN users AS u ON u.id = m.user1ID
LEFT JOIN users AS u2 ON u2.id = m.user2ID
This is an alternative to using IF statements.

You can not use the IF THEN ELSE END IF-stuff in a SELECT in this way. However, you can use it in stored procedures and functions.
I would JOIN u.id with both m.user1ID and m.user2ID and use DISTINCT to avoid duplicates.
There is a IF() which you can use in SELECTs, but you can not do flow control with IF().

Thanks,
It worked for me. Tried something else like below :
**
Create table TEMP1 (ID INT,T_MONTH INT,T_YEAR INT)
INSERT INTO TEMP1 VALUES(1,1,2001)
INSERT INTO TEMP1 VALUES(2,2,2001)
INSERT INTO TEMP1 VALUES(3,3,2001)
**
CREATE TABLE TEMP2 (T_MONTH INT,T_YEAR INT,FREQUENCY CHAR(1),VAL FLOAT)
INSERT INTO TEMP2 VALUES(1,2001,'M',1.1)
INSERT INTO TEMP2 VALUES(3,2001,'M',1.2)
INSERT INTO TEMP2 VALUES(3,2001,'Q',1.3)
INSERT INTO TEMP2 VALUES(12,2001,'A',1.4)
SELECT * FROM TEMP1 L JOIN TEMP2 H
ON L.T_YEAR = H.T_YEAR
OR (L.T_MONTH = (CASE WHEN H.FREQUENCY='M'
THEN H.T_MONTH
END)
OR dbo.GetQuarterFromMonth(L.T_MONTH) = (CASE WHEN H.FREQUENCY = 'Q'
THEN dbo.GetQuarterFromMonth(H.T_MONTH)
END))
WHERE H.FREQUENCY = 'Q'
Here GetQuarterFromMonth is a user defined funtion which returns Quarter for a particular month.
The objective of above query is to inflate the value of Quarter for all the months in table 1. If frequency is annual then in that case the value should be inflated for all the months 1-12 of table 1 which is achieved through first join condition. In case frequency is Monthly then each month from table1 should be mapped to table2. The only unique case is handling quarter frequency.

Related

SQL - how to remove whole row if one of the column in subquery return NULL

I am stuck in 1 SQL query
SELECT u.*,
um2.meta_value as parent_user_id,
( select u.user_email FROM wp_users u WHERE u.ID = um2.meta_value ) AS parent_user_email
FROM
wp_users u
JOIN wp_usermeta um2 ON u.ID = um2.user_id
AND um2.meta_key = 'parent_user_id'
GROUP BY
u.ID
This query return 4 row ( As shown in the screenshot )
I want a scenario like : If subquery return NULL , then the whole row will not be shown.
So in this example "childthree" should not be shown , as "parent_user_email" is NULL , so the whole 3rd row need to remove
Use a join instead:
SELECT u.*, um2.meta_value as parent_user_id,
u2.user_email as parent_user_email
FROM wp_users u JOIN
wp_usermeta um2
ON u.ID = um2.user_id AND
um2.meta_key = 'parent_user_id' JOIN
wp_users u2
ON u2.ID = um2.meta_value
GROUP BY u.ID;
Note: This assumes that the email value itself is never NULL. If that is possible, add WHERE u2.user_email IS NOT NULL.
Also, your query should fail because the GROUP BY columns are inconsistent with the SELECT. However, logically it seems ok, because there is only one parent and user email per user. However, I would include those columns in the GROUP BY.

About Mysql join case when error

select t.* FROM user_tq t join
CASE when t.blogid = 0 then user_dp ELSE user_blog END b
on t.uid = b.uid where ***;
I want to join different table according to blogid, when blogid is 0, join user_dp, else join user_blog.But it returns 1064 error.
How to solve this problem?
You can do this with left join and filter the right result by COALESCE
select t.*
FROM user_tq t
left join user_dp ud on t.blogid = 0 and t.uid = ud.uid
left join user_blog ub on t.blogid != 0 and t.uid = ub.uid
where
COALESCE(ud.uid, ub.uid) IS NOT NULL and
***;
For more info refer : MySQL query where JOIN depends on CASE
If you are trying to inner join to filter data on condition then;
select * from (
select t.*
FROM user_tq t
join user_dp ud on t.blogid = 0 and t.uid = ud.uid
union all
select t.*
FROM user_tq t
join user_blog ub on t.blogid != 0 and t.uid = ub.uid
) as x
where ****;
USE:
select * FROM users t
left join user_role r ON t.user_id = 0 AND r.user_id = t.user_id
left join product_category c ON t.user_id != 0 AND c.user_id = t.user_id
WHERE COALESCE(r.user_id, c.user_id) IS NOT NULL
WHERE ***;
Replace users, user_role and product_category with your tables.
The other answers posted here are the correct solutions.
I'll answer the "Why do I get 1064" for you.
The syntax you've written is invalid. You cannot "conditionally" join to a table like this.
What you can do is LEFT JOIN - which as opposed to [INNER] JOIN means "return NULL for all fields on this table if no record matches the join condition".
There's lots on the Internet about LEFT vs INNER joins, and MySQL's website documents the COALESCE function very well.
Have a play and a read, you'll figure it out why the other solutions work.
Upon reflection, I felt this could benefit from some additional explanation.
See this SQLFiddle: http://sqlfiddle.com/#!9/043b7/6

MySQL: Find users with no submission

I'm struggling a little bit with a query and hope you can help.
I have two tables. On with all the users and one with information from submitted forms.
Both contain the user ID.
What I would need to find out is which user from the users table does not appear on the report table.
This is what I have so far:
SELECT u.ID, u.display_name, u.user_email, r.user_id
FROM users AS u
LEFT JOIN report AS r ON u.ID = r.user_id
WHERE NOT EXISTS(
SELECT *
FROM report AS rr
WHERE u.ID = rr.user_id
)
This seems to be fine for the users who absolutely have never submitted the form.
But the reports table also contains a date column and I was wondering how I can get this grouped by day.
In the front end then I will hopefully have a table which shows:
date: user:
2015-01-01 user a
2015-01-01 user f
2015-01-02 user g
2015-01-02 user a
2015-01-03 user z
2015-01-03 user x
Where the users are those who have not submitted the form that day.
Hope you can help. Thank in advance!
If you want to get a list of users that doesn't have any rows in the report table then you can generate a set that is the Cartesian product of the users and the dates that are present in the report table, and then do a left join with that set and check for null.
The Cartesian set formed by the cross join will contain all possible combinations of dates and users; that is would the report table would contain is all users had added reports on all available dates.
select r.date, u.user_id
from report r
cross join users u
left join (select r.date, r.user_id from users as u join report as r on u.id = r.user_id)
a on a.date = r.date and a.user_id = u.user_id
where a.date is null
Sample SQL Fiddle
With most other databases this could have been done with a set difference operator (minus or except) instead of a left join.
I'm making assumptions about column names in your report table for this answer:
SELECT x.report_date, u.user_id, u.display_name
FROM users u
JOIN (
SELECT DISTINCT report_date
FROM reports
) x
LEFT JOIN reports r
ON r.user_id = u.user_id
AND r.report_date = x.report_date
WHERE r.report_date IS NULL
ORDER BY x.report_date, u.user_id
Check out this fiddle: http://sqlfiddle.com/#!9/407ac/5
Left outer join with where clause...
Here is a good link ...
http://blog.codinghorror.com/a-visual-explanation-of-sql-joins/
SELECT * FROM `users`
LEFT OUTER JOIN `report`
ON `users`.`ID` = `report`.`user_id`
WHERE `report`.`user_id` IS null
ORDER BY `report`.`Date`
Surely you could just pass in the date you wanted to check?
so something like this (using #reportDate as the parameter):
SELECT * FROM users
LEFT OUTER JOIN report
ON users.ID = report.user_id
WHERE report.user_id IS NULL
AND report.Date = #reportDate
You can get the pairs of users/dates without reports. Generate all possible rows using a cross join and then filter out the ones that exist:
select u.*, r.date
from users u cross join
(select distinct date from reports r) d left join
reports r
on u.id = r.user_id and d.date = r.date
where r.userid is null;

MYSQL: Using a join after and AND

the MYSQL query below combines a number of tables. However, as you can see, I would like to add a LEFT JOIN at the end on the receipt table. The query returns an error when I add the LEFT JOIN. Anybody know the best way to LEFT JOIN the receipt table to the rest of the query. Sorry if this is a newbie question. Thanks !!
SELECT user_name, expense_category, merchant_name, expense_cost, expense_date, expense_status, receipt_image, expense_comment
FROM users, expenses, merchants, receipts
WHERE ".$adminId." = expenses.admin_id
AND expenses.user_id = users.user_id
AND expenses.merchant_id = merchants.merchant_id
AND LEFT JOIN (receipts)
ON expenses.receipt_id = receipts.receipt_id
Here is a clean approach of doing it, note that I have added alias for the tables for better readability so you may use the alias name in the select statement to fetch the column from the proper table.
SELECT
u.user_name,
ex.expense_category,
mer.merchant_name,
ex.expense_cost,
ex.expense_date,
ex.expense_status,
re.receipt_image,
ex.expense_comment
FROM users u
JOIN expenses ex on ex.user_id = u.user_id
JOIN merchants mer on mer.merchant_id = ex.merchant_id
LEFT JOIN receipts re on re.receipt_id = ex.receipt_id
where
ex.admin_id = '$adminId'
Try this,
SELECT user_name, expense_category, merchant_name, expense_cost, expense_date, expense_status, receipt_image, expense_comment
FROM users, expenses, merchants, receipts
LEFT JOIN receipts ON expenses.receipt_id = receipts.receipt_id
WHERE ".$adminId." = expenses.admin_id
AND expenses.user_id = users.user_id
AND expenses.merchant_id = merchants.merchant_id
Use join clauses instead of where clause. I.e.
SELECT user_name, expense_category, merchant_name, expense_cost, expense_date, expense_status, receipt_image, expense_comment
FROM users
INNER JOIN expenses on users.user_id = expenses.expenses_id
INNER JOIN merchants on merchants.merchant_id = expenses.merchant_id
LEFT JOIN (receipts)
ON expenses.receipt_id = receipts.receipt_id
WHERE ".$adminId." = expenses.admin_id
Note that any columns from the receipts will be NULL in the select statement whenever there's no matching record.

mysql join 2 tables with on condition , keep some values

I have 2 tables.
users(id,username) and links(id,usernameORid).
Example of rows: users{ [1,test] , [2,stack] } and links{ [1,overflow] , [2, 1] }
So, table links may contain username or id from table users. As you can see in the example,
usernameORid from links may not contain the id or username from users.
I hope you understood my example.
Now, i have this query:
SELECT l.usernameORid, u.username, u.id
FROM links l
LEFT JOIN users u
ON l.usernameORid= u.id
LEFT JOIN user_roles ur
ON ur.userID = u.id
WHERE ur.roleID < 4
group by u.id
But this query does not return rows from links if usernameORid is not an actual username or id from users.
In the previous example, will not return row [1,overflow]. I want that row too.
How can i achieve that?
EDIT: The problem is partialy related to
LEFT JOIN user_roles ur
ON ur.userID = u.id
WHERE ur.roleID < 4
but still, how can i achieve that?
user_roles ( id,userID,roleID)
Change your final WHERE condition to:
WHERE ur.roleID < 4 OR u.id IS NULL
This will allow it to return rows that didn't have a match in users. Normally a LEFT JOIN does that by itself, but since you're doing an additional join on that table, the WHERE clause is filtering those non-matching rows out because they don't have a roleID.
You can use an OR statement in your join between links and users. This will allow you to pick up users records where the link.usernameORid is equal to either the users.id or the users.username
SELECT l.usernameORid,
u.username,
u.id
FROM links l
LEFT JOIN users u ON
l.usernameORid = u.id OR
lusernameORid = u.username
LEFT JOIN user_roles ur
ON ur.userID = u.id
WHERE ur.roleID < 4
GROUP BY u.id
This will still cause records to drop if the found users->user_roles.roleID is less than 4. If you wanted to have link records maintained regardless of whether of a user was found by username or ID then you would need to subquery the users and user_roles table joins and apply your WHERE statement there instead. This query is below:
SELECT
l.usernameORid,
u.username,
u.id
FROM links l
LEFT JOIN
(
SELECT
users.username,
users.idusers
FROM
users
LEFT JOIN user_roles ON
user_roles.userID = users.id
WHERE
user_roles.roleID < 4
) u ON
l.usernameORid= u.id OR
l.usernameORid = u.username
group by u.id
Furthermore, if you wish the 2nd or 3rd column of your return to hold the value that is in l.usernameORid when the users table lacks a match... if your users.id is always numeric you could do some trickery with a CASE statement:
SELECT
l.usernameORid,
Coalesce(u.username, CASE WHEN .lusernameORid REGEXP '^[0-9]+$' THEN NULL ELSE l.usernameORid END) as username,
Coalesce(u.username, CASE WHEN .lusernameORid REGEXP '^[0-9]+$' THEN l.usernameORid ELSE NULL END) as userid
FROM links l
LEFT JOIN
(
SELECT
users.username,
users.idusers
FROM
users
LEFT JOIN user_roles ON
user_roles.userID = users.id
WHERE
user_roles.roleID < 4
) u ON
l.usernameORid= u.id OR
l.usernameORid = u.username
group by u.id
Keep in mind though, that if the users table doesn't have a match for the links.usernameORid then only the username OR the id could be determined, so you will have a NULL in one of the two fields.