create a table based on other tables - mysql

I have 4 different tables:
student: sid, name, gender
grades: sid, grade
address: postcode, sid, distance_to_school
health_condition: sid, health_code
Now I want to make a new table that contains all of the columns that I mentioned above. I need to join them based on the sid. I tried to add columns separately but the code is really long. So is there another way to do this task?

If you want to show the data from the fifth table that you want to create, I think with the query is better, so you don't need the fifth table. Just
"SELECT * FROM student s
INNER JOIN grades g
on s.sid = g.sid
INNER JOIN address a
on s.sid = a.sid
INNER JOIN health_condition h
on s.sid = h.sid
ORDER BY sid ASC";

Please use below query will give you the exact result set.
SELECT t1.sid, t1.name, t1.gender , t2.grade,t3.postcode, t3.distance_to_school,
t4.health_code AS
student as t1 inner join grades t2 on t1.sid=t2.sid
inner join address as t3 on t3.sid= t1.sid
inner join health_condition as t4 on t4.sid=t1.sid

Try this query
FROM student s
LEFT JOIN grades g on g.sid = s.sid
LEFT JOIN address a on a.sid = s.sid
LEFT JOIN health_condition h on h.sid = s.sid
WHERE s.sid = ;

Related

Is this the right way to join tables to fetch data?

I have a database with the tables:
Student(SID,Name,Surname,Age)
Registration(StudentID,CourseID)
Course(CID,Name,Cost)
I would like to extract only the name of the courses with students younger than 20. Will the query below do just that?
SELECT C.NAME
FROM Course C
INNER JOIN Registration
INNER JOIN Student S
WHERE CID = CourseID
AND SID = StudentID
AND Age < 20
GROUP BY C.NAME
I would also like to extract the number of students in each course having students younger than 20. Is it correct to do it as below?
SELECT count(S.NAME)
,C.NAME
FROM Student S
INNER JOIN Course C
INNER JOIN Registration
WHERE Age < 20
AND CID = CourseID
AND SID = StudentID
GROUP BY C.NAME
You are missing the ON part for the join otherwise it would just be a CROSS JOIN.
Your first query should look like this if you want just a distinct list of student names:
SELECT DISTINCT C.NAME
FROM Course C
INNER JOIN Registration R ON C.CID = R.CourseID
INNER JOIN Student S ON R.StudentID = S.SID
WHERE Age < 20
Your second query shouldn't really have the C.Name in the select if you want to get just a count unless you want a count of how many students have that name.
SELECT count(*)
FROM Student S
INNER JOIN Registration R ON s.SID = R.StudentID
INNER JOIN Course C ON c.CID = R.CourseID
WHERE Age < 20
GROUP BY C.NAME
First join these tables, then group by Course's PK(CID), Add the HAVING condition to filter the course which has students younger than 20.
Then use Course table to join the result to get the course name and count of students in the course.
SELECT
T1.Name,
T2.StudentCount
FROM
Course T1
INNER JOIN (
SELECT
c.CID,
COUNT(s.SID) AS StudentCount
FROM
Course c
LEFT JOIN Registration r ON c.CID = r.CourseID
LEFT JOIN Student s ON s.SID = r.StudentID
GROUP BY c.CID
HAVING COUNT(IF(s.Age < 20, 1, NULL)) > 0
) T2 ON T1.CID = T2.CID
More correctly, you should move the conditions of the join, to the join statements themselves by including them in the on clause instead of the where. While the results may not change in this instance, if you were to start including outer joins you would encounter difficulties.
SELECT count(S.NAME)
,C.NAME
FROM Student S
INNER JOIN Registration R
ON s.SID = R.StudentID
INNER JOIN Course C
ON c.CID = R.CourseID
WHERE Age < 20
GROUP BY C.NAME
There's a fiddle here showing it in action: http://sqlfiddle.com/#!9/c3b8f/1
Your first query will also produce the results you want, but again, you should move the join predicates to the join itself. Also, you don't need to perform the grouping just to get distinct values, mysql has an expression for that called distinct. So rewritten, the first query would look like:
SELECT DISTINCT C.NAME
FROM Student S
INNER JOIN Registration R
ON s.SID = R.StudentID
INNER JOIN Course C
ON c.CID = R.CourseID
WHERE Age < 20.
Again, the results are the same as what you have already but it is easier to 'read' and will put you in good stead when you move on to other queries. As it stands you have mixed implicit and explicit join syntax.
This fiddle demonstrates both queries: http://sqlfiddle.com/#!9/c3b8f/4
edit
I may have misinterpreted your original question - if you want the total number of students enrolled in a course with at least one student under 19, you can use a query like this:
select name, count(*)
from course c
inner join registration r
on c.cid = r.courseid
where exists (
select 1
from course cc
inner join registration r
on cc.cid = r.courseid
inner join student s
on s.sid = r.studentid
where cc.cid = c.cid
group by cc.cid
having min(s.age) < 20
)
group by name;
Again with the updated fiddle here: http://sqlfiddle.com/#!9/c3b8f/17

SQL view all courses for one student

I have 3 tables
table_student
ID, firstname, lastname
table_courses
ID, course_name
table_student_course
ID, student_ID, course_ID, date_taken
All I want to do is list all courses by course_name and date_taken by student with ID=1
Anyone please?
You need to use the JOIN on the table_courses and table_student_course tables and then apply the Order By on cource_name to sort out by course name.And for selecting the particular student apply the Where clause as filter.
SELECT
t.course_name,
tsc.date_taken
FROM
table_courses t INNER JOIN table_student_course tsc
ON t.ID = tsc.course_ID
WHERE
tsc.student_ID = 1
ORDER BY
t.course_name
If you also want to get the students detail from the query then you need to join the 3 tables like below,
SELECT s.firstname, s.lastname, c.course_name, sc.date_taken
FROM table_courses c
INNER JOIN table_student_course sc ON c.ID = sc.course_ID
INNER JOIN table_student s ON sc.student_ID = s.ID
WHERE sc.student_ID = 1
ORDER BY c.course_name
SELECT s.firstname, s.lastname, c.course_name, sc.date_taken
FROM table_courses c
INNER JOIN table_student_course sc ON c.ID = sc.course_ID
INNER JOIN table_student s ON sc.student_ID = s.ID
WHERE sc.student_ID = 1
ORDER BY c.course_name
By using Inner Join on the tables "table_courses" & "table_student_course" we selects all rows from both tables as long as there is a match between the columns in both tables making sure the ids are the same. If the condition is met( student_id=1) then the query will return what is expected.
SELECT course_name, date_taken
FROM table_courses c INNER JOIN table_student_course sc ON c.id = sc.course_id
WHERE sc.student_ID = 1
ORDER BY course_name

How to triple join tables to find and find entries that match two things?

I have a simple problem that's hard to describe (at least for me).
Consider the following schema for a database modeling courses:
COURSE (cid, did, name, num, creditHours),
STUDENT (sid, fname, lname, did)
ENROLLED_IN (eid, sid, cid)
What is a query that will find the sid of the students enrolled in course.name=Math" and "Science"?
I'm sorry i asked a similar (simpler) question thinking I could figure the rest out but I could not: https://stackoverflow.com/questions/18902489/how-to-find-entries-in-database-that-meet-multiple-matches
AS the other page suggests, you need to do two JOIN's to the same table. But since you want to use the Name and not the cid, you join to the COURSE based on the enrollment data.
SELECT DISTINCT s.sid
FROM STUDENT s
INNER JOIN ENROLLED_IN e ON e.sid = s.sid
INNER JOIN COURSE c ON c.cid = e.cid AND c.Name = 'Math'
INNER JOIN COURSE c2 ON c2.cid = e.cid AND c2.Name = 'Science'
If you need the whole student record, then...
SELECT STUDENT.*
FROM STUDENT
INNER JOIN
(SELECT DISTINCT s.sid
FROM STUDENT s
INNER JOIN ENROLLED_IN e ON e.sid = s.sid
INNER JOIN COURSE c ON c.cid = e.cid AND c.Name = 'Math'
INNER JOIN COURSE c2 ON c2.cid = e.cid AND c2.Name = 'Science'
) t0 ON t0.sid = STUDENT.sid
EDIT Instead of DISTINCT you could also use GROUP BY, ala
SELECT s.sid
FROM STUDENT s
INNER JOIN ENROLLED_IN e ON e.sid = s.sid
INNER JOIN COURSE c ON c.cid = e.cid AND c.Name = 'Math'
INNER JOIN COURSE c2 ON c2.cid = e.cid AND c2.Name = 'Science'
GROUP BY s.sid
EDIT and instead of using two joins, you can use HAVING clauses
SELECT s.sid
FROM STUDENT s
INNER JOIN ENROLLED_IN e ON e.sid = s.sid
INNER JOIN COURSE c ON c.cid = e.cid
WHERE c.Name IN ('Math', 'Science')
GROUP BY s.sid
HAVING COUNT(*) = 2

MySQL `INNER JOIN` multiples of the same table

Is it possible to INNER JOIN a MySQL query to achieve this result?
I have a table with Strategies and a table with Members. The Strategy table holds the ID of the author that corresponds to their ID in the Member table and the ID of an author that updated the existing author's work. Is it possible to grab a reference to both of these people at the same time? Something like the following, which returns no errors, but also no results...
SELECT * FROM Strategies
INNER JOIN Members AS a
INNER JOIN Members AS b
WHERE Strategies.ID='2'
AND Strategies.AuthorID = a.ID
AND Strategies.UpdateAuthorID = b.ID
Use a LEFT JOIN:
SELECT
s.*,
a.Name AS MemberName,
b.Name AS UpdatedMemberName
FROM Strategies AS s
LEFT JOIN Members AS a ON s.AuthorID = a.ID AND s.ID = 2
LEFT JOIN Members AS b ON s.UpdateAuthorID = b.ID AND s.ID = 2 ;
If you want them in one column use COALESCE:
SELECT
s.*,
COALESCE(a.Name, b.Name) AS MemberName
FROM Strategies AS s
LEFT JOIN Members AS a ON s.AuthorID = a.ID AND s.ID = 2
LEFT JOIN Members AS b ON s.UpdateAuthorID = b.ID AND s.ID = 2
SELECT toD.dom_url AS ToURL,
fromD.dom_url AS FromUrl,
rvw.*
FROM reviews AS rvw
LEFT JOIN domain AS toD
ON toD.Dom_ID = rvw.rev_dom_for
LEFT JOIN domain AS fromD
ON fromD.Dom_ID = rvw.rev_dom_from
if domain is table name

Sum of count of records in three related tables

I have three related tables:
Course
Student
Teacher
Each course is given by a teacher to many students.
I can find the number of students attending a course:
SELECT c.id, count(*) FROM course as c, student as s
WHERE c.id = s.course_id
GROUP BY c.id;
I can find all the courses given by a teacher:
SELECT t.id, c.id FROM course as c, teacher as t
WHERE t.id = c.teacher_id;
I want to find how many students were taught by each teacher. Is the following query correct one?
SELECT t.id, sum(x.count_s)
FROM
(SELECT count(*) AS count_s FROM course as c2, student as s
WHERE c2.id = s.course_id AND c2.id = c.id
GROUP BY c2.id) as x,
course as c, teacher as t
WHERE t.id = c.teacher_id;
Unfortunately, I cannot test it directly because this is actually a simplification of the real problem at hand. I need to find out which solution works for the simplified problem.
To answer your question, no. You cannot reference c.id inside the inline view aliased as x. That should throw an error.
But if you remove that, then your query has the potential to return an inflated count, due to a semi-Cartesian product, between the inline view aliased as x and c.
So that predicate needs to be relocated, and you'd need to return c2.id from x (i.e. add it to the SELECT list, you already have it referenced in the GROUP BY).
This is equivalent to your query, just rewritten to replace the comma join operators and relocate join predicates to ON clause. This statement is equivalent to yours, and is invalid):
SELECT t.id
, SUM(x.count_s)
FROM ( SELECT count(*) AS count_s
FROM course c2
JOIN student s
ON c2.id = s.course_id
AND c2.id = c.id -- INVALID here
GROUP
BY c2.id
) x
CROSS -- no join predicate
JOIN course c
JOIN teacher t
ON t.id = c.teacher_id
To fix that, add c2.id to the SELECT list in x, and relocate that predicate. Something like this:
SELECT t.id
, SUM(x.count_s)
FROM ( SELECT count(*) AS count_s
, c2.id -- added
FROM course c2
JOIN student s
ON c2.id = s.course_id
-- AND c2.id = c.id -- relocated (removed from here)
GROUP
BY c2.id
) x
JOIN course c
ON x.id = c.id -- relocated (added here)
JOIN teacher t
ON t.id = c.teacher_id
Assuming that id is UNIQUE and NOT NULL in course, that query will return a reasonable count (although counts of zero will be "missing").
To return the "zero" counts, you'd need to use an OUTER join. And as I always prefer to use LEFT JOIN, the tables/inline views in the outermost query would need to be re-ordered:
SELECT t.id
, IFNULL(SUM(x.count_s),0)
FROM teacher t
LEFT
JOIN course c
ON c.teacher_id = t.id
LEFT
JOIN ( SELECT count(*) AS count_s
, c2.id -- added
FROM course c2
JOIN student s
ON c2.id = s.course_id
-- AND c2.id = c.id -- relocated (removed from here)
GROUP
BY c2.id
) x
ON x.id = c.id -- relocated (added here)
Assuming that id is a PRIMARY KEY (or equivalent UNIQUE and NOT NULL) on each table, then that will return a "correct" count.
It's not necessary to include the course table in the inline view aliased as x. It would be sufficient to GROUP BY s.course_id.
SELECT t.id
, IFNULL(SUM(x.count_s),0)
FROM teacher t
LEFT
JOIN course c
ON c.teacher_id = t.id
LEFT
JOIN ( SELECT count(*) AS count_s
, s.course_id
FROM student s
GROUP
BY s.course_id
) x
ON x.course_id = c.id -- relocated (added here)
That query will return a valid "count".
A simpler statement would easier to understand. Here's how I would get the count:
SELECT t.id AS teacher_id
, COUNT(s.id) AS how_many_students_taught
FROM teacher t
LEFT
JOIN course c
ON c.id = t.course_id
LEFT
JOIN student s
ON s.course_id = c.id
GROUP
BY t.id
You only need to use LEFT JOIN on course against student because it is impossible for a teacher to teach on students that have no courses.
SELECT a.id as Teacher_ID,
b.id as CourseID,
COUNT(c.studentID) totalStudents
FROM teacher a
INNER JOIN course b
ON b.teacher_ID = a.id
LEFT JOIN student c
ON b.id = c.course_ID
GROUP BY a.id, b.id
Assuming you want the distinct count of students a teacher has taught, then this should work:
SELECT t.Id, COUNT(DISTINCT s.id)
FROM Teacher t
LEFT JOIN Course c ON t.id = c.teacher_id
LEFT JOIN Student s ON c.id = s.course_id
GROUP BY t.Id
If however, you'd prefer to know how many students were taught in each course by each teacher, then try this:
SELECT t.Id, c.Id, COUNT(DISTINCT s.id)
FROM Teacher t
LEFT JOIN Course c ON t.id = c.teacher_id
LEFT JOIN Student s ON c.id = s.course_id
GROUP BY t.Id, c.Id
Good luck.