Joining INTO many-to-many table (From Primary Key Table) - mysql

This is how far i have gotten, though i don't think it can be done with 1 SQL-statement, i just want to confirm whether or not it is possible to do this with ONLY 1 statement:
SELECT * FROM users
INNER JOIN users_mentors ON users_mentors.id=users.mentoruser_id
INNER JOIN mentor_types ON (mentor_types.id=users_mentors.mentor_type OR users_mentors.mentor_type IS NULL)
INNER JOIN mentor_geographies ON mentor_geographies.mentor_id=users_mentors.id
INNER JOIN communes ON communes.id=mentor_geographies.commune_id
LIMIT 0,10
users table with foreignkey to users_mentors:
+------+---------+---------------+
| id | user_id | mentoruser_id |
+------+---------+---------------+
| 1886 | NULL | 4 |
| 1885 | NULL | NULL |
| 1884 | NULL | NULL |
| 1883 | NULL | NULL |
| 1882 | NULL | NULL |
+------+---------+---------------+
users_mentors table (in a many-to-many relationship with communes):
+----+-------------+
| id | mentor_type |
+----+-------------+
| 4 | NULL |
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
+----+-------------+
communes table (in a many-to-many relationship with users_mentors):
+----+--------------+-------+----------+
| id | name | short | contract |
+----+--------------+-------+----------+
| 1 | København | NULL | 0 |
| 2 | Aarhus | NULL | 0 |
| 3 | Aalborg | NULL | 0 |
| 4 | Odense | NULL | 0 |
| 5 | Esbjerg | NULL | 0 |
+----+--------------+-------+----------+
mentor_geographies table (the m2m table that has FK to communes & users_mentors):
+----+-----------+------------+
| id | mentor_id | commune_id |
+----+-----------+------------+
| 1 | 4 | 1 |
| 2 | 4 | 2 |
+----+-----------+------------+
Is it possible to get all rows from users_mentors and a list of all their commune.type's, IF THEY EXIST (if mentor_geographies is empty, i want empty list of commune.type). In all cases i want the user.

If you want all users, use left join:
SELECT *
FROM users LEFT JOIN
users_mentors
ON users_mentors.id = users.mentoruser_id LEFT JOIN
mentor_types
ON mentor_types.id=users_mentors.mentor_type OR
users_mentors.mentor_type IS NULL LEFT JOIN
mentor_geographies
ON mentor_geographies.mentor_id = users_mentors.id LEFT JOIN
communes
ON communes.id = mentor_geographies.commune_id
LIMIT 0, 10;
I would also recommend that you use table aliases. They make the queries easier to write and to read:
SELECT *
FROM users u LEFT JOIN
users_mentors um
ON um.id = u.mentoruser_id LEFT JOIN
mentor_types mt
ON mt.id = um.mentor_type OR
um.mentor_type IS NULL LEFT JOIN
mentor_geographies mg
ON mg.mentor_id = um.id LEFT JOIN
communes c
ON c.id = mg.commune_id
LIMIT 0, 10

Related

Do not return a record during COUNT() operation if there are no results

I am trying to get the how many students are enrolled in a class. The above works if there ARE records.
However, if the there are no results a single "record" is returned where all fields are NULL except the students_count which returns zero
+---+------+------+----------------+----------------+
| | id | name | professor_name | students_count |
+---+------+------+----------------+----------------+
| 1 | null | null | null | 0 |
+---+------+------+----------------+----------------+
I would like for there to be NO records returned.
If a record returns it looks like this.
+---+----+-------------+----------------+----------------+
| | id | name | professor_name | students_count |
+---+----+-------------+----------------+----------------+
| 1 | 1 | Science 101 | Atkins | 16 |
+---+----+-------------+----------------+----------------+
I have tried variations on the LEFT JOIN and a combination of IFNULL(COUNT(DISTINCT students.id), null) AS students_count
But I can't seem to get it to work.
Any help?
SELECT classes.*,
professors.name AS professor_name,
COUNT(DISTINCT students.id) AS students_count
FROM classes
INNER JOIN professors ON classes.professor_id = professors.id
LEFT JOIN students ON classes.id = students.class_id AND classes.class_id IS NOT NULL
WHERE classes.class_id = 3
Using the generic data below, with class_id of 3, the result SHOULD be no records. But the null record with the count as 0 is returned.
+---+------+------+----------------+----------------+
| | id | name | professor_name | students_count |
+---+------+------+----------------+----------------+
| 1 | null | null | null | 0 |
+---+------+------+----------------+----------------+
Using the class_id of 1 will return:
+---+----+-------------+----------------+----------------+
| | id | name | professor_name | students_count |
+---+----+-------------+----------------+----------------+
| 1 | 1 | Science 101 | Atkins | 4 |
+---+----+-------------+----------------+----------------+
Generic Data
Classes
+---+-------------+---------------+
| | name | professors_id |
+---+-------------+---------------+
| 1 | Science 101 | 1 |
+---+-------------+---------------+
| 2 | Math | 2 |
+---+-------------+---------------+
| 3 | English | 3 |
+---+-------------+---------------+
Professors
+----+--------+
| id | name |
+----+--------+
| 1 | Atkins |
+----+--------+
| 2 | Button |
+----+--------+
| 3 | Castor |
+----+--------+
Students
+----+-------+------------+
| id | name | classes_id |
+----+-------+------------+
| 1 | Adam | 1 |
+----+-------+------------+
| 2 | Beth | 1 |
+----+-------+------------+
| 3 | Chris | 1 |
+----+-------+------------+
| 4 | David | 1 |
+----+-------+------------+
| 5 | Erma | 2 |
+----+-------+------------+
You can try to use INNER JOIN instead of OUTER JOIN, because of LEFT JOIN will base on classes table.
SELECT classes.*,
professors.name AS professor_name,
COUNT(DISTINCT students.id) AS students_count
FROM classes
INNER JOIN professors ON classes.professor_id = professors.id
INNER JOIN students ON classes.id = students.class_id
WHERE classes.class_id = 3
sqlfiddle
EDIT
HAVING clause is for aggregate function condition. but you use HAVING classes.class_id IS NOT NULL that can move to where
SELECT classes.*,
professors.name AS professor_name,
COUNT(DISTINCT students.id) AS students_count
FROM classes
INNER JOIN professors ON classes.professor_id = professors.id
LEFT JOIN students ON classes.id = students.class_id
WHERE classes.class_id = 3 AND classes.class_id IS NOT NULL

How to join get data from 2 tables and left join into 1 table in mysql

I have this table
tbl_emp
ID| name |
1 | a |
2 | b |
3 | c |
4 | d |
tbl_remit
ID| remit |
1 | 2012-01-01|
2 | 2013-01-01|
3 | 2012-05-01|
tbl_report
ID| report |
1 | 2012-01-01|
2 | 2013-01-01|
3 | 2012-05-01|
I need to join all 3 of them in tbl_emp regardless if there is a data in tbl_remit or tbl_report.
Here is the code that I used but failed.
SELECT tbl_emp*, tbl_remit.remit, tbl_report.report from tbl_emp
left join tbl_emp.ID = tbl_remit.ID LEFT JOIN tbl_emp.ID = tbl_report.ID
the table i got was
ID | remit | report |
1 | NULL | NULL |
2 | NULL | NULL |
3 | NULL | NULL |
4 | NULL | NULL |
the table i need is
ID | remit | report |
1 |2012-01-01|2012-01-01|
2 |2013-01-01|2013-01-01|
3 |2012-05-01|2012-05-01|
4 | NULL | NULL |
You can do this by joining your other two tables with inner join. And the join the resulted table with the emp table. For that you can use subquery.
select * from tbl_emp x left join (
select a.id as id, a.remit as remit,b.report as report from
tbl_remita,tbl_report b where a.id=b.id) y on x.id = y.id
Hope it will help.

How to join tables including all ids from left table but only showing information from the right table given certain where clause

I have an attendees table with the following structure:
+--------------+---------+
| attendee_id | others1 |
+--------------+---------+
| abcd | A |
| ghij | B |
| defg | C |
+--------------+---------+
And also an eventattendees table with the following structure:
+--------------+---------+----------+
| attendee_id | others2 | event_id |
+--------------+---------+----------+
| wxyz | D | 1 |
| mlno | E | 2 |
| defg | F | 3 |
| defg | G | 2 |
| abcd | H | 1 |
+--------------+---------+----------+
What I want is to create a query that, given some event_id, returns a join of these tables (by attendee_id) that includes all attendee ids from attendee table and also returns the information from the eventattendde tables which a match for that event_id. Say, for event_id 3:
+--------------+---------+---------+----------+
| attendee_id | others1 | others2 | event_id |
+--------------+---------+--------------------+
| abcd | A | null | null |
| ghij | B | null | null |
| defg | C | F | 3 |
+--------------+---------+--------------------+
How can I do that for mysql?
You need to put your where criteria in the join instead to respect the outer join.
select a.attendee_id, a.others1, e.others2, e.event_id
from attendees a
left join eventattendees e on a.attendee_id = e.attendee_id and e.event_id = 3
If you put it in the where criteria, it will negate the outer join.
You can try this:
SELECT A.attendee_id, A.others1, ISNULL(B.others2,'empty') AS others2, ISNULL(B.event_id,'empty') AS event_id FROM TableA A LEFT JOIN TableB B ON A.attendee_id=B.attendee_id
Use left join
select a.attendee_id, a.others1, b.others2, b.event_id
from attendees as a
left join eventattendees on a.attendee_id = b.attendee_id and b.event_id= 3;

Left Join Subquery Exclude GROUP BY with null result

I am trying to create a query with a GROUP_CONCAT added as a new column in my current query, first here are my tables:
Users table
+----+----------+--------------+
| id | username | date_created |
+----+----------+--------------+
| 1 | user1 | 2000-03-16 |
| 2 | user2 | 2001-05-14 |
| 3 | user3 | 2002-01-13 |
| 4 | user4 | 2003-03-14 |
+----+----------+--------------+
Shifts table
+----+------------+--------------+
| id | shift_name | date_created |
+----+------------+--------------+
| 1 | shift1 | 2002-05-10 |
| 2 | shift2 | 2002-07-11 |
| 3 | shift3 | 2002-09-23 |
+----+------------+--------------+
Accounts table
+----+--------------+--------------+
| id | account_name | date_created |
+----+--------------+--------------+
| 1 | account1 | 2001-05-01 |
| 2 | account2 | 2001-05-02 |
| 3 | account3 | 2001-05-03 |
+----+--------------+--------------+
Shift Mapping table
+----+---------+----------+------------+
| id | user_id | shift_id | account_id |
+----+---------+----------+------------+
| 1 | 1 | 1 | 1 |
| 2 | 1 | 2 | 2 |
| 3 | 3 | 1 | 1 |
+----+---------+----------+------------+
basically, I want a query that gets all the user (to display in a table) with a custom column that shows all shift that is attach to that user(if there is no shift attach to that obviously has a null result)
Here is the query I've done so far:
SELECT users.id AS user_id, users.username, users.date_created,
GROUP_CONCAT(DISTINCT (t.shift_name)) AS shifts
FROM (`users`)
LEFT JOIN
(SELECT s.shift_name, sm.user_id FROM shift_map sm
LEFT JOIN shifts s ON sm.shift_id = s.id) t
ON users.id = t.user_id
GROUP BY user_id
ORDER BY `users`.`date_created` DESC;
Now there is no problem getting the users with a shift attach to it, my problem is that the users with no shifts attach to only returns 1 result which is caused by the GROUP BY user_id how can I exclude the users with no shift in the GROUP BY or how can I return all the users with attached shifts and with no attach shifts? Thanks.
Update
Here is the example result I want to see:
+---------+----------+--------------+----------------+
| user_id | username | date_created | shifts |
+---------+----------+--------------+----------------+
| 1 | user1 | 2000-03-16 | shift1,shift2 |
| 2 | user2 | 2001-05-14 | (NULL) |
| 3 | user3 | 2002-01-13 | shift1 |
| 4 | user4 | 2003-03-14 | (NULL) |
+---------+----------+--------------+----------------+
My problem in my query is that it only shows only 1 user with null shifts.
SELECT users.id AS user_id, users.username, users.date_created,
GROUP_CONCAT(DISTINCT (t.shift_name)) AS shifts
FROM (`users`)
LEFT JOIN
(SELECT s.shift_name, sm.user_id FROM mapping sm
LEFT JOIN shifts s ON sm.shift_id = s.id) t
ON users.id = t.user_id
GROUP BY username
ORDER BY user_id
+---------+----------+--------------+---------------+
| user_id | username | date_created | shifts |
+---------+----------+--------------+---------------+
| 1 | user1 | 2000-03-16 | shift1,shift2 |
| 2 | user2 | 2001-03-16 | NULL |
| 3 | user3 | 2002-03-16 | shift1 |
| 4 | user4 | 2003-03-16 | NULL |
+---------+----------+--------------+---------------+
My Bad, I can just use the simple LEFT JOIN:
SELECT users.id AS user_id, users.username, users.date_created,
GROUP_CONCAT(DISTINCT (shift.shift_name)) AS shifts
FROM (`users`)
LEFT JOIN shifts_map ON users.id = shifts_map.user_id
LEFT JOIN shifts ON shifts_map.shift_id = shift.id
GROUP BY user_id
ORDER BY `users`.`date_created` DESC;
I am just complicating the query, Forgot that the simple LEFT JOIN can do the trick. Thanks.

Get all users even if no records in another table

I'm facing a problem here :
I have two tables :
A users table :
+----+---------------+----------+
| id | username | company |
+----±---------------±----------+
| 1 | John | 0 |
| 2 | Jack | 0 |
| 3 | Casimir | 0 |
±----±---------------±----------±
A orders table :
+----+---------------+----------+--------+
| id | date | iduser | status |
+----±---------------±----------+--------+
| 1 | 2012-05-28 | 1 | 1 |
| 2 | 2012-05-25 | 1 | 1 |
| 3 | 2012-04-28 | 2 | 1 |
| 4 | 2012-03-28 | 1 | 1 |
| 5 | 2012-02-28 | 2 | 0 |
±----±---------------±----------±--------+
What I'm trying to do is to get a result like this :
+----------+---------------+-------------+
| username | COUNT(order) | MAX(date) |
+----------±---------------±-------------+
| John | 3 | 2012-05-28 |
| Jack | 1 | 2012-04-28 |
| Casimir | 0 | NULL |
±----------±---------------±-------------±
Here's the request I have for the moment :
SELECT u.username, COUNT(o.id), MAX(o.date)
FROM users u
INNER JOIN orders ON u.id = o.iduser
WHERE o.status = 1
GROUP BY u.id
This request gives me a result like :
+----------+---------------+-------------+
| username | COUNT(order) | MAX(date) |
+----------±---------------±-------------+
| John | 3 | 2012-05-28 |
| Jack | 1 | 2012-04-28 |
±----------±---------------±-------------±
As you can see, the user Casimir is not shown as he made no order. How can I modify my request to get the result I need please ?
Thanks !
A LEFT JOIN or LEFT OUTER JOIN will include all rows of the inital table, including those where there is no match in the joined-to table
SELECT u.username, COUNT(o.id), MAX(o.date)
FROM users u
LEFT OUTER JOIN orders o ON u.id = o.iduser AND o.status = 1
GROUP BY u.id
You need to use an OUTER JOIN instead of your current INNER JOIN.
Have a look at Jeff's post here to see how they differ:
http://www.codinghorror.com/blog/2007/10/a-visual-explanation-of-sql-joins.html