Confused about using UNION or JOIN - mysql

I have three MySQL tables: 'people', 'people2id', 'places'. The first one stores information about people, where the 'places' table stores information about their addresses. The middle one, 'people2id', interconnects this two tables (could be that one person has two or more addresses).
Now I want to go to some person's profile and see his profile, but also his associated addresses. I created this query for that:
SELECT * FROM people p
JOIN people2id e ON p.peopleId = e.peopleId
JOIN places a ON a.peopleId = e.peopleId
WHERE p.peopleId = number
This works when the person has associated address(es), otherwise, it will fail. I do not understand if I should use any kind of JOIN or use UNION for this matter.

Change the JOIN to LEFT JOIN, i.e.
SELECT * FROM people p
LEFT JOIN people2id e ON p.peopleId = e.peopleId
LEFT JOIN places a ON a.peopleId = e.peopleId
WHERE p.peopleId = number
Using LEFT JOIN will include all the records from people, whether they contain associated records or not.

UNION is used for get same kind of informations from two different queries and return them as a single result set
JOIN is used for add columns and data to rows (not as simple but you can see it like this)

You should use LEFT JOIN.
SELECT * FROM people p
JOIN people2id e ON p.peopleId = e.peopleId
LEFT JOIN places a ON a.peopleId = e.peopleId
WHERE p.peopleId = number

Related

Is it possible to join many to many and one to many in one go?

My tables have the following schema:
wp_careers
wp_locations
wp_careers_locations
wp_educations
A career applicant can apply to many locations, and have many educational records.
The desired result is to get ALL records from wp_careers and group the applied locations as a locations fields, and put all educational records (wp_educations) as an array attached to the applicant.
Right now I know how to join many to many relations and group the locations:
SELECT c.*, GROUP_CONCAT(l.name) as locations
FROM wp_careers c
JOIN wp_careers_locations cl ON c.id = cl.career_id
JOIN wp_locations l ON cl.location_id = l.id
GROUP BY c.id
But I don't know how to extend this query to include the educational records.
One way would be to just join again:
SELECT c.*, GROUP_CONCAT(DISTINCT l.name) as locations,
GROUP_CONCAT(DISTINCT e.institute) AS edu_institutes
FROM wp_careers c
LEFT JOIN wp_careers_locations cl ON c.id = cl.career_id
LEFT JOIN wp_locations l ON cl.location_id = l.id
LEFT JOIN wp_educations e ON c.id = e.career_id
GROUP BY c.id
But this is likely to create a Cartesian product, as it will inadvertently join every location to every education. So if you have three locations and two educations for a given career, it will generate 3x2 = 6 rows when you didn't expect it to. I tried to compensate for this with DISTINCT so the list of names in each GROUP_CONCAT() will eliminate duplicates.
But honestly, I would prefer to run two queries. One for locations, and a second query for educations. That would avoid the Cartesian product. MySQL is not so weak that it can't handle an extra query, and it might actually be less expensive than doing the DISTINCT operations.
Re your comment:
You want to restrict careers in the education query only to those that have at least one location?
You can do this with a semi-join:
SELECT c.*, GROUP_CONCAT(e.institute) AS edu_institutes
FROM wp_careers c
JOIN wp_educations e ON c.id = e.career_id
WHERE c.id IN (SELECT career_id FROM wp_career_locations)
GROUP BY c.id
Even though there may be multiple rows in wp_career_locations matching each respective c.id, it doesn't cause a Cartesian product.

MySQL getting data from 3 tables which are connected by mapping tables

I have the following database example:
The example is pretty much self-explanatory: There are lessons held by teachers at defined time periods (time_start, time_end) each time period -> lesson connection has its own max_students number.
I know want to list all lessons with all information of the 3 tables (and the max_students). I would do it like that (I heard, that joining table like that is the fastest way):
SELECT * FROM lesson, teacher, time, teacher_has_lesson, time_has_lesson
WHERE lesson.lesson_id = teacher_has_lesson.lesson_lesson_id
AND teacher.teacher_id = teacher_has_lesson.teacher_teacher_id
AND lesson.lesson_id = time_has_lesson.lesson_lesson_id
AND time.time_id = time_has_lesson.time_time_id
1.) Is this a good solution if you just want to join 3 tables or are there better ones?
2.) This SQL call will get me only lessons, that have a teacher and a time. I also want to display lessons, that are in the database, but dont have a teacher or time yet. How can I do that?
There's an alternative way of writing this using join syntax. What you have is equivalent to an inner join, where you only see rows where there are matches:
select
*
from
lesson l
inner join
teacher_has_lesson tl
on l.lession_id = tl.lesson_lesson_id
inner join
teacher t
on tl.teacher_teacher_id = t.teacher_d
inner join
time_has_lesson tml
on l.lesson_id = tml.lesson_lesson_id
inner join
time tm
on tml.time_time_id = tm.time_ud
There's another type of join called outer join, where all the rows from one table are shown, and null values supplied if there are no matching values in the other table. It comes in two or three variants. left outer join shows all rows from the first table. right outer join shows all rows from the second table. full outer join shows all rows from both tables. So, for your second query you could use:
select
*
from
lesson l
left outer join
teacher_has_lesson tl
on l.lession_id = tl.lesson_lesson_id
left outer join
teacher t
on tl.teacher_teacher_id = t.teacher_d
left outer join
time_has_lesson tml
on l.lesson_id = tml.lesson_lesson_id
left outer join
time tm
on tml.time_time_id = tm.time_ud

Three rows join mysql two success third no

I have three tables People, Items and Locations. People can have only 1 Items. Locations has no relation to any of 2 tables. I want to get I record join all 3. I did 2 so far people + items but 3rd I keep getting MySQL errors. There's no JOIN ON for location. Any help?
SELECT * FROM ITEMS i
RIGHT JOIN PEOPLE p
ON (p.ITEM_ID =i.ID) where
p.ID=3
RIGHT JOIN
SELECT * FROM LOCATIONS lo where lo.ID=7
If there are no join keys in common, then you might want to do a cross join. This produces a Cartesian product, that is, every location for each row selected from People/items:
SELECT *
FROM ITEMS i RIGHT JOIN
PEOPLE p
ON (p.ITEM_ID =i.ID) cross join
location l
WHERE p.ID=3
By the way, MySQL has a very flexible (and non-standard) join syntax. You can actually leave the on clause off of a join and it will behave the same as a cross join. That is a bad habit, of course. If you want a cross join, then use cross join explicitly.
I'm assuming you want the location with the ID of 7 to appear on every row...
SELECT *
,(SELECT loc.name FROM Location loc WHERE loc.ID = 7) AS Location
FROM ITEMS i
RIGHT JOIN PEOPLE p
ON (p.ITEM_ID =i.ID)
WHERE p.ID=3
OR
SELECT *
FROM ITEMS i
RIGHT JOIN PEOPLE p
ON (p.ITEM_ID =i.ID)
CROSS JOIN (SELECT * FROM LOCATIONS lo where lo.ID=7) l
WHERE p.ID=3
OR
[several other ways to go about it]
Try something like this:
SELECT peo.id, it.id, loc.id
FROM People as peo
INNER JOIN Items as it on it.id = peo.id
INNER JOIN Locations as loc on loc.id = peo.id
WHERE peo.ID=3
Edit:
Your question was edited while I was typing this so my example doesn't match like it used to. Use ITEM_ID and ID as needed.
Although not recommended, you can also use
SELECT *
FROM People as peo
INNER JOIN Items as it on it.id = peo.id
INNER JOIN Locations as loc on loc.id = peo.id
WHERE peo.ID=3

Selecting data from multiple tables, where a specific table does not contain any identifiable columns?

I have a dilemma.
Let's assume(for simplicity's sake) I have four tables, with different numbers of columns and rows, they are: users, mail, events and service.
When I receive a request, I have an ID that links on three of those tables, but with different columns it matches to.
Let's say, users matches on user_id, mail matches on user_ref and events matches on user_ref as well.
That would've been a fine query for me to write up, even with single, multiple or even all IDs.
The problem arrives on the next step I have to take, and that's the *service table.
The service table doesn't conform to the same standards of the others, thus it does not have an user_id, or user_ref that can be pulled.
What it has instead, is a *mail_ref* column, and it has the potential to contain duplicates.
My current method is trying to use an IN() method, but it only works for selecting a single user/row.
Here's my current query:
SELECT
u.Name as Name,
COUNT(m.user_ref) AS Mail_total,
e.mail_id,
COUNT(e.user_ref) AS Event_total,
COUNT(s.mail_ref) AS service_total
FROM
users u
LEFT JOIN
mail m ON m.user_ref = u.user_id
LEFT JOIN
service s ON s.mail_ref IN(e.mail_id)
LEFT JOIN
events e ON e.user_ref = u.user_id
WHERE u.user_id IN(my,list,of,ids)
GROUP BY s.mail_ref
The problem I have with it currently, is that although it's selecting the correct data, it's not selecting unique data for every id I specify.
It works marginally fine when given a single id, but as mentioned above, not when it has to retrieve multiple rows.
If anyone could help me out it would be much appreciated.
Do a subquery in the left join for service. Instead of:
LEFT JOIN
service s ON s.mail_ref IN(e.mail_id)
Try
LEFT JOIN
(select TOP 1 mail_ref from server) as S on s.mail_ref = e.mail_id
See if that works.
SELECT
u.Name as Name,
(select count(*) from mail m where m.user_ref = u.user_id) AS Mail_total,
e.mail_id,
(select count(*) from events e where e.user_ref = u.user_id) AS Event_total,
(select count(*) from
events e
inner join services s on s.mail_ref = e.mail_id
where
e.user_ref = u.user_id) as service_total
FROM
users u
WHERE u.user_id IN(my,list,of,ids)

Need to use criteria from multiple tables to acquire data from multiple tables

I'm having a bit of a hiccup regarding a particular SQL query. I need to join data from two tables, while also limiting the data (but not necessarily grabbing it) by means of a third table. The tables are as follows:
MEMBERS(member_id,first_name,last_name)
MEMBERS_GROUPS(member_id,group_id)
CHARGES(charge_id,member_id,charge_amount,status)
I need to find all charges for all members of a specific group but I also want to grab the first and last name from the MEMBERS table. The query I've come up with thus far is:
select c.*, m.first_name, m.last_name
FROM charges c
LEFT JOIN member m
ON c.member_id=m.member_id
INNER JOIN members_groups mg
ON mg.group_id=1
i've also tried:
SELECT c.*, m.first_name, m.last_name
FROM charges c, members_groups mg, member m
WHERE c.member_id=mg.member_id
AND mg.group_id = 1
AND c.status='Valid'
AND c.member_id = m.member_id
…but neither returns the data I need. I'm sure I'm overthinking this, but I can't for the life of me get the correct values. I keep getting what appears to be the Cartesian product -- regardless, it's clearly returning too many rows and bad data.
Perhaps what you need is to also restrict the INNER JOIN on members_groups to
those rows with mg.member_id = m.member_id:
SELECT c.*, m.first_name, m.last_name
FROM charges c
LEFT JOIN member m
ON c.member_id=m.member_id
INNER JOIN members_groups mg
ON mg.group_id=1
AND mg.member_id = m.member_id