I am building a superhero database. I would like to join each individual table together so that the user can search for anything rather than having to do an INNER JOIN every time the user performs a query.
For instance, a user could search for the comic series that the superhero came from and all you would have to do is change the SELECT and the WHERE clause.
This returns a good result when searching for a superhero's power:
SELECT sh.heroName, pwr.name FROM superhero sh
INNER JOIN superheroPower sh_pwr ON sh.id = sh_pwr.superheroID
INNER JOIN power pwr ON sh_pwr.powerID = pwr.id
WHERE sh.heroName = 'Superman';
This returns an empty set:
SELECT sh.heroName, pwr.name FROM superhero sh
INNER JOIN superheroPower sh_pwr ON sh.id = sh_pwr.superheroID
INNER JOIN power pwr ON sh_pwr.powerID = pwr.id
INNER JOIN superheroAffiliation sh_aff ON sh.id = sh_aff.superheroID
INNER JOIN affiliation aff ON sh_aff.affiliationID = aff.id
INNER JOIN superheroComic sh_c ON sh.id = sh_c.superheroID
INNER JOIN comic c ON sh_c.comicID = c.id
INNER JOIN publisher pub ON c.publisherID = pub.id
INNER JOIN comicAuthor c_a ON c.id = c_a.comicID
INNER JOIN author a ON c_a.authorID = a.id
WHERE sh.heroName = 'Superman';
Is there a way to search the database with all of the tables already INNER JOINed together? Should I be doing a LEFT JOIN or a different type of JOIN instead?
If there were no NULLs anywhere (as in, everyone of those links in joins always has something on the other side), that would work.
However, because it is an INNER any cases where one side of the join does not have results will eliminate the rows (non matching rows do not show up in an INNER join).
So to answer your direct question, assuming the key you care about is heroName, you should start from superhero and left join everything else on. If you want to be able to use any field as the key, you should be using full outer on all the joins.
Related
Can someone help me to understand those results ? (For me all 3 should return 6455).
(Using RDS mysql-8.0.13)
SELECT COUNT(p.product_id) FROM product p LEFT JOIN product_attributes pa ON p.pdt_id = pa.pdt_id WHERE pa.code = 'season';
Results : 6332
SELECT COUNT(*) FROM product p;
Results : 6455
SELECT COUNT(p.product_id) FROM product p LEFT JOIN product_attributes pa ON p.pdt_id = pa.pdt_id AND pa.code = 'season';
Results : 6455
Your first join uses the WHERE clause, this mean sit selected all the rows, including those with a null join and then filters out those WHERE the pa.code = season, i.e. the null joins.
The last one joins on both, but because it is a left join you still get the full table of results, and nothing is filtered because you remove the WHERE clause. If you were to use an INNER JOIN in the last query you should get the same result (6332).
This link might be useful What's the difference between INNER JOIN, LEFT JOIN, RIGHT JOIN and FULL JOIN?
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
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
I currently have 5 tables in MySQL database. Some of them share foreign keys and are interdependent of each other. I am trying to create a query that will show all the results side by side(major, course, semester, etc). The query I created query it is not displaying my desired results since I have not added the other tables. I am not sure how to implement the other tables. How can I modify the mysql-query to display all the results in order?
Query
select * from course left join major on course.id = majors.id left join majors on courses_major_xref.majors_id = majors.id
Try the following
SELECT * FROM course
INNER JOIN major_courses_xref ON course.id = major_courses_xref.course_id
INNER join majors ON major_courses_xref.majors_id = majors.id
INNER JOIN courses_semester_xref ON course.id = courses_semester_xref.course_id
INNER JOIN semester ON courses_semester_xref.semester_id = semester.id;
I think there is just some order of operations problems in your query, try:
SELECT * from course
LEFT JOIN major_course_xref
ON course.id = major_course_xref.courseID
LEFT JOIN major
ON major.id = major_course_xref.major_id
LEFT JOIN course_semester_xref
ON course.id = course_semester_xref.course_id
LEFT JOIN semester
ON course_semester_xref.semester_id = semester.id
Hey guys quick question, I always use left join, but when I left join twice I always get funny results, usually duplicates. I am currently working on a query that Left Joins twice to retrieve the necessary information needed but I was wondering if it were possible to build another select statement in so then I do not need two left joins or two queries or if there were a better way. For example, if I could select the topic.creator in table.topic first AS something, then I could select that variable in users and left join table.scrusersonline. Thanks in advance for any advice.
SELECT * FROM scrusersonline
LEFT JOIN users ON users.id = scrusersonline.id
LEFT JOIN topic ON users.username = topic.creator
WHERE scrusersonline.topic_id = '$topic_id'
The whole point of this query is to check if the topic.creator is online by retrieving his name from table.topic and matching his id in table.users, then checking if he is in table.scrusersonline. It produces duplicate entries unfortunately and is thus inaccurate in my mind.
You use a LEFT JOIN when you want data back regardless. In this case, if the creator is offline, getting no rows back would be a good indication - so remove the LEFT joins and just do regular joins.
SELECT *
FROM scrusersonline AS o
JOIN users AS u ON u.id = o.id
JOIN topic AS t ON u.username = t.creator
WHERE o.topic_id = '$topic_id'
One option is to group your joins thus:
SELECT *
FROM scrusersonline
LEFT JOIN (users ON users.id = scrusersonline.id
JOIN topic ON users.username = topic.creator)
WHERE scrusersonline.topic_id = '$topic_id'
Try:
select * from topic t
left outer join (
users u
inner join scrusersonline o on u.id = o.id
) on t.creator = u.username
If o.id is null, the user is offline.
Would not it be better to match against topic_id in the topics table by moving the condition to the join. I think it will solve your problem, since duplicates come from joining with the topics table:
SELECT * FROM scrusersonline
JOIN users
ON users.id = scrusersonline.id
LEFT JOIN topic
ON scrusersonline.topic_id = '$topic_id'
AND users.username = topic.creator
By the way, LEFT JOIN with users is not required since you seem to search for the intersection between scrusersonline and users