How can I join 4 table? - mysql

I have 4 tables, three are many to many relationship:
Student(StudID,GroupId,Name,....)
Lesson(LessID,LessonName,Mark)
StudentLesson(StudID,LessID)
and the relationship between student and Group is One to Many
Student(StudID,Name,....)
Group(GroupId,GroupNumber)
What I want is how select Name, LessonName, Mark, GroupNumber
select S.Name, L.LessonName, L.Mark, G.GroupNumber from Student s
join StudentLesson SL on SL.StudId = S.StudId
join Lesson L on SL.LessID = L.LessID
Join Group G on G.GroupId = S.GroupId
I think the error in this line Join Group G on G.GroupId=S.GroupId, because when I omit it, it works between many to many but between one to many it didn't work.

group is a reserved word, so it needs to be quoted. In MySQL, you can use backticks:
select S.Name, L.LessonName, L.Mark, G.GroupNumber
from Student S
join StudentLesson SL on SL.StudId = S.StudId
join Lesson L on SL.LessID = L.LessID
Join `Group` G on G.GroupId = S.GroupId

Based on Comments: the query is fine; you lack data that matches the results you're after.
There are no students with a groupID
There are no students with a groupID matching GROUPID in the group table.
To prove this out you could simply make the last join a LEFT Join provided you have no where clause with limits on Group.
FROM:
select S.Name,L.LessonName,L.Mark,G.GroupNumber from Student s
join StudentLesson SL on SL.StudId=S.StudId
join Lesson L on SL.LessID =L.LessID
Join Group G on G.GroupId=S.GroupId
TO:
SELECT S.Name, L.LessonName, L.Mark, G.GroupNumber
FROM Student s
INNER JOIN StudentLesson SL on SL.StudId=S.StudId
INNER JOIN Lesson L on SL.LessID =L.LessID
LEFT JOIN Group G on G.GroupId=S.GroupId
This will show you all students w/ lessons and groupNumber if the groupID's match; but i'm betting they will all be NULL.
So are you after all students regardless if they have lessons or groups if so your inner joins should be left. If you're only after students that have lessons and belong to groups then they all need to be inner joins. Just depends on what you're after!
Left join will say include all records from the prior data joins, and only those that match from this join (to group in the example)

Related

Join queries are not working as expected when trying to compare a count result with a value

I'm learnin SQL from a book and i'm trying to do some exercices on join queries.The only problem that i'm facing is that all of my join queries are not working while they seem well
students(student_id,student_names,student_age)
courses_students(course_id,student_id)
courses(course_id,course_schedule,course_room,teacher_id)
teachers(teacher_id,teacher_names)
The query is "which courses have more than 5 students enrolled?"
Here is what i've done :
SELECT course_name,
count
(SELECT count(*)
FROM courses) AS COUNT
FROM students,
courses,
courses_students
WHERE students.student_id=courses_students.student_id,
courses.course_id=courses_students.course_id
AND COUNT > 5
And the other one is what are the names of students enrolled in at least 2 courses scheduled for the same hours
My query :
SELECT student_name,
schedule
FROM students,
courses,
courses_students
WHERE students.student_id=courses_students.student_id,
courses.course_id=courses_students.course_id
AND COUNT > 2
In this inner query:
(SELECT count(*)
FROM courses) AS COUNT
you need to narrow down what is included in the COUNT. As it is, it is selecting all items in the courses table. The inner query does not know about the restrictions in the outer query. Try adding a where clause in this inner query. You might need to add table aliases to uniquely refer to the correct courses table, so there is no ambiguity whether it is referring to the courses table in the inner query or the outer query.
And, as noted in other answers, this is not the best way to structure joins.
In MySQL you are required to define joins explicitly. Unlike Oracle it can't handle joins with sign of =.
SELECT course_name,
count
(SELECT count(*)
FROM courses) AS COUNT
FROM students
INNER JOIN courses on courses.course_id=courses_students.course_id
INNER JOIN courses_students on students.student_id=courses_students.student_id
WHERE COUNT(*) > 2
You need to aggregage by course and then assert the number of students:
SELECT
c.course_name,
COUNT(*) AS cnt
FROM courses c
INNER JOIN courses_students cs
ON c.course_id = cs.course_id
INNER JOIN students s
ON cs.student_id = s.student_id
GROUP BY
c.course_name
HAVING
COUNT(*) > 5;
stackoverflow is actually not a site to do homework, but as you already have given a try to solve the task, here is a solution for question number one:
SELECT cs.course_id
FROM courses_students cs
INNER JOIN students s
ON cs.course_id = s.course_id
GROUP BY cs.course_id
HAVING count(*) > 5
Read about the GROUP BY and HAVING clause - nice way to solve some problems.
Question number 2 could be solved like this:
SELECT student_names
FROM students s
INNER JOIN courses_students cs
ON cs.student_id = s.student_id
INNER JOIN (
SELECT course_id
FROM courses c
GROUP BY course_schedule
HAVING count(*) > 1
) sub
ON sub.course_id = cs.course_id
The INNER JOIN with the subquery is selecting courses which are scheduled at the same time (having the same course_schedule).
As the other tables are "connected" with INNER JOINs, we will finally just have the subset of students which are participating one of those courses.

Inner join not working. Output repeating rows

I'm trying to get the the students number & name with the course code & name for students who have a grade below 40. This is what i have
SELECT S.name, S.no, C.code, C.name, T.grade
FROM student S INNER JOIN course C INNER JOIN take T
WHERE grade <40;
It is outputting the grades under 40 but it is returning 128 rows showing everyone's name and number grade repeating them.
Sorry if this is wrong but im a beginner.
You need the conditions that relate the tables to each other:
SELECT S.name, S.no, C.code, C.name, T.grade
FROM student AS s
JOIN take AS t ON t.student_no = s.no
JOIN course AS c ON t.course_code = c.code
Replace student_no and course_code with the actual foreign key columns in the take table.
Simple syntax refer it
SELECT Orders.OrderID, Customers.CustomerName, Shippers.ShipperName
FROM ((Orders
INNER JOIN Customers ON Orders.CustomerID = Customers.CustomerID)
INNER JOIN Shippers ON Orders.ShipperID = Shippers.ShipperID);

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 - join two tables, group and count

I have two tables:
reviewStatusPhases - id|name
and
userPhase - id|reviewStatusPhase_id|user_id|created_at|updated_at
The reviewStatusPhases table have records inserted (Active, Inactive, On Pause, Terminated...), and userPhase is empty.
The tables are connected via
userPhase.reviewStatusPhase_id = reviewStatusPhases.id
one to one.
Is it possible that in one query I get all reviewStatusPhases, and cound how many users are in each phase? In this case I will get something like this:
Active (0 Users)
Inactive (0 Users)
On Pause (0 Users)
Terminated (0 Users)
I'm making some assumptions here (e.g. INNER JOIN versus LEFT JOIN in the join, and DISTINCT in the count), but it sounds like you just want
SELECT reviewStatusPhases.name, COUNT(DISTINCT userPhase.user_id)
FROM userPhase INNER JOIN reviewStatusPhases
ON userPhase.reviewStatusPhase_id = reviewStatusPhases.id
GROUP BY reviewStatusPhases.name
Query will be as follows:
SELECT r.name as `name`, count(u.id) as `count` FROM reviewStatusPhases r LEFT OUTER JOIN userPhase u ON r.id = u.reviewStatusPhase_id GROUP BY r.name
left outer join with reviewStatusPhases on left to show all names.
group by names of reviewStatusPhases.
display reviewStatusPhases name and count of user id's (to neglect null values)
Use LEFT JOIN as follows:
SELECT COUNT(m.UserId) FROM Table1 m
LEFT JOIN Table2 k ON k.StatusId = m.StatusId
WHERE k.Status = 'Inactive'
You can easily use the Status column to track the users and their activities. In your case, ReviewStatus.
I hope the following will be helpful
SELECT RPS.Name, COUNT(UP.user_id)
FROM reviewStatusPhases RPS
LEFT OUTER JOIN userphases UP ON RPS.id = UP.reviewStatusPhase_id
GROUP BY RPS.Name
ORDER BY RPS.Name
SELECT
DISTINCT s.s_level AS 'Level',
COUNT(DISTINCT s.s_id) AS Schools,
COUNT(DISTINCT st.st_id) AS Teachers
FROM schools AS s
JOIN school_teachers AS st ON st.st_school_idFk = s.s_id AND st.st_status = 1
WHERE s.s_status = 1
GROUP BY s.s_level

MySql query to get count of days spent in each country for each purpose? (Get count of all record in second table present in first table)

I have three tables tl_log, tl_geo_countries,tl_purpose. I am trying to get the count of number of days spent in each country in table 'tl_log' for each purpose in table 'tl_purpose'.
I tried below mysql query
SELECT t.country_id AS countryID,t.reason_id AS reasonID,count(t.reason_id) AS
days,c.name AS country, p.purpose AS purpose
FROM `tl_log` AS t
LEFT JOIN tl_geo_countries AS c ON t.country_id=c.id
LEFT JOIN tl_purpose AS p ON t.reason_id=p.id
GROUP BY t.reason_id,t.country_id ORDER BY days DESC
But landed up with.
I am not able to get the count for purpose for each country in 'tl_log' that is not present in table 'tl_log'. Any help is greatly appreciated. Also, Please let me know if the question is difficult to understand.
Expected Output:
Below is the structure of these three tables
tl_log
tl_geo_countries
tl_purpose
If you want all possible combination of countries and purposes, even those that do not appear on the log table (these will be shown with a count of 0), you can do first a cartesian product of the two tables (a CROSS join) and then LEFT join to the log table:
SELECT
c.id AS countryID,
p.id AS reasonID,
COUNT(t.reason_id) AS days,
c.name AS country,
p.purpose AS purpose
FROM
tl_geo_countries AS c
CROSS JOIN
tl_purpose AS p
LEFT JOIN
tl_log AS t
ON t.country_id = c.id
AND t.reason_id = p.id
GROUP BY
p.id,
c.id
ORDER BY
days DESC ;
If you want the records for only the countries that are present in the log table (but still all possible reason/purposes), a slight modification is needed:
SELECT
c.id AS countryID,
p.id AS reasonID,
COUNT(t.reason_id) AS days,
c.name AS country,
p.purpose AS purpose
FROM
( SELECT DISTINCT
country_id
FROM
tl_log
) AS dc
JOIN
tl_geo_countries AS c
ON c.id = dc.country_id
CROSS JOIN
tl_purpose AS p
LEFT JOIN
tl_log AS t
ON t.country_id = c.id
AND t.reason_id = p.id
GROUP BY
p.id,
c.id
ORDER BY
days DESC ;
LEFT JOIN should be replaced by RIGHT JOIN