There is pretty good article how to filter results in a has-many relation:
How to filter SQL results in a has-many-through relation
I'm just seeking a solution for COUNT result, not show them all.
student {
id
name
}
club {
id
name
}
student_club {
student_id
club_id
}
How many students are in both CLUB1 && CLUB2?
EDIT:
It would be great to use "Martin 2" method from a link below:
SELECT s.stud_id, s.name
FROM student s
JOIN student_club sc USING (stud_id)
WHERE sc.club_id IN (30, 50)
GROUP BY 1,2
HAVING COUNT(*) > 1;
Just adding something to COUNT results.
Probably simplest, cleanest and fastest for just two students:
SELECT count(*) AS ct
FROM student_club x
JOIN student_club y USING (stud_id)
WHERE x.club_id = 30
AND y.club_id = 50;
You don't need to join to the student table for this at all - as soon as you know the list of stud_id you are interested in.
For any number of students, Martin's query is more convenient. You can simplify in a similar fashion:
SELECT count(*) AS ct
FROM (
SELECT stud_id
FROM student_club
WHERE club_id IN (30, 50)
GROUP BY 1
HAVING count(*) = 2 -- adapt to number of items in list
) x;
Requires that (stud_id, club_id) is unique of course, and that list items are also unique.
The query uses table aliases for tables student_club and club. This allows to return rows only for students who are in both clubs. Then, using COUNT allows to return the number of students:
SELECT COUNT(*) AS nb
FROM student s, student_club sc1, club c1, student_club sc2, club c2
WHERE s.id=sc1.student_id AND sc1.club_id=c1.id AND c1.name="CLUB1"
AND s.id=sc2.student_id AND sc2.club_id=c2.id AND c2.name="CLUB2"
If you really want to use the "Martin 2" query, you may count the number of records this way:
SELECT COUNT(*) AS nb
FROM (
SELECT s.stud_id, s.name
FROM student s
JOIN student_club sc USING (stud_id)
WHERE sc.club_id IN (30, 50)
GROUP BY 1,2
HAVING COUNT(*) > 1
) tmp;
Related
I am building a database for a contest in which people upload photos and the jury grades them. I have two tables: photographs (id, name, user_id and section_id) and grades (id, grade, photo_id, juror_id).
What I want to achieve is a query to return all photos ordered by the average of all grades given to each photo.
For example, if we have 2 photographs with ids 1 and 2 with photo 1 having two grades (5, 6) and photo 2 also having two grades (8, 10) the first returned row will be the photo with id 2 (the average of the grades is 9 and it is greater than 5.5, the average of photo 1).
How could I achieve this?
Here is a pseudo-example of a query
SELECT * FROM photographs ORDER BY AVERAGE(SELECT grade FROM grades)
This is a job for AVG() and GROUP BY.
To get the average grade by photo from your grades table this subquery does it.
SELECT AVG(grade) avg_grade,
photo_id
FROM grades
GROUP BY photo_id
That subquery is guaranteed to return exactly one row per photo_id value. So you can LEFT JOIN it to your photographs table like so.
SELECT avg_grade.avg_grade,
photographs.*
FROM photographs
LEFT JOIN (
SELECT AVG(grade) avg_grade,
photo_id
FROM grades
GROUP BY photo_id
) avg_grade ON photographs.id = avg_grade.photo_id
ORDER BY avg_grade.avg_grade DESC
First of all you need to join your table properly, then to agreagat result, and after that to order output:
SELECT
p.id,
AVG(g.grade) AS averageGrade
FROM photographs AS p
JOIN grade AS g ON g.photo_id = p.id
GROUP BY p.Id
ORDER BY AVG(g.grade) DESC
I have the following table called Stores:
Name | Category | Industry
ABC appliances retail
XYZ banking finance
NZE clothing retail
JKI tutoring education
I would like to output all the Names that are the only one in their Industry (e.g. XYZ and JKI are the only Names in their Industry).
I have the following query:
select s.Name, s.Industry, a.Number
from Stores s
inner join (
select Industry, count(*) as Number
from Stores group by Industry
) a
on s.Industry = a.Industry;
I get an output table which has an attribute called Number which gives the total number of times each Industry appears in the table Stores. How can I select all the tuples which have the value of 1 in the Number column after using the inner join?
use where condition
select s.Name, s.Industry, a.Number
from Stores s
inner join (
select Industry, count(*) as Number
from Stores group by Industry
) a
on s.Industry = a.Industry where a.Number=1
use corelated subquery
select s.* from stores s
where exists ( select 1 from Stores s1 where s.Industry=s1.Industry
having count(*)=1)
You can use EXISTS :
SELECT s.*
FROM Stores s
WHERE EXISTS (SELECT 1 FROM Stores s1 WHERE s1.Industry = s.Industry AND s1.Name <> s.Name);
I would just use aggregation:
select industry, max(name) as name
from stores
group by industry
having count(*) = 1;
If there is only one name, then max(name) is the one.
If names can be repeated in the table, then:
having min(name) = max(name)
I'm fairly new to MySQL, and trying to understand the many-to-many relationship since these examples can popup in interviews
There are 3 tables, and since a Student can have many courses and a Course can have many students, this is a Many-to-Many relationship right?
The tables are
Student- has student ID, name, date of birth, and department.
Courses- Has ID, Name of course
Student_Courses- Has student_id, course_id
How would I display these 2 questions-
1) Given a studentID, return all the names of the courses the student is taking
2) Return the name of students who is taking X amount of courses or more (Ex. 4 or more courses).
Im trying to write queries on these, but I'm stuck...
In the case of selecting all of the courses for a given student ID you could try the following, which will return one row for each Course a Student is associated with.
select
s.name as StudentName,
c.name as CourseName
from `Student` as s
inner join `Student_Course` as sc on (sc.student_id = s.ID)
inner join `Course` as c on (c.ID = sc.course_id)
where
(s.`ID` = 'given_Student_ID_here')
;
As for selecting a list of the names of Students taking N or more courses, for this you might use an aggregating sub-select as a WHERE clause in which we reference one of the outer tables (i.e. [Student]) so that the result of the aggregation is personalised per Student record:
select
s.name as StudentName
from `Student` as s
where
(
(
select count(*)
from `Student_Course` as sc
inner join `Course` as c on (c.ID = sc.course_id)
where (sc.student_id = s.ID)
) >= 4
)
;
You might also consider an alternative approach to this second problem by using the GROUP BY and HAVING clauses:
select
s.name as StudentName
from `Student` as s
inner join `Student_Course` as sc on (sc.student_id = s.ID)
inner join `Course` as c on (c.ID = sc.course_id)
group by
s.name
having
count(*) >= 4
;
i have gotten the task of creating a statistic from tables that look like this:
Faculty
1 FacultyName1
2 FacultyName2
3 FacultyName3
4 FacultyName4
5 FacultyName5
and this:
Student
1 StudentName1 FacultyNr2
2 StudentName2 FacultyNr3
3 StudentName3 FacultyNr5
4 StudentName4 FacultyNr2
now i have to create a statistic which Groups the Faculties into newly created fields and groups by them.
Say:
Faculty Group 1 Count: 3
Faculty Group 2 Count: 1
for this example lets say that all those of FacultyName1,FacultyName2,FacultyName3 should be listet as of "Faculty Group 1" and FacultyName4 and FacultyName5 as of "Faculty Group 2".
I started by doing the following:
Select Count(*)
FROM Student INNER JOIN Faculty on Student.FacultyID = Faculty.ID
But am stuck trying to understand how to Group, how i could create Groups in the Code, where i could just say: Group by FacultyGroups (Select Case When FacultyName = 'FacultyName1' = 'Faculty Group 1')
or something similiar, does anybody have any idea ?
Assuming that you have added a GroupID column in your Faculty table
SELECT COUNT(*), f.GroupID
FROM Student AS s
INNER JOIN Faculty AS f ON s.FacultyID = f.ID
GROUP BY f.GroupID
It gives you the number of student per group of faculties and the id of this group
There are better ways, but this should work:
SELECT
CASE
WHEN f.Name IN ('FacultyName1', 'FacultyName2', 'FacultyName3') THEN 'FacultyGroup1'
WHEN f.Name IN ('FacultyName4', 'FacultyName5') THEN 'FacultyGroup2'
END AS FacultyGroup,
COUNT(*) AS Students
FROM
Student s
INNER JOIN Faculty f ON s.FacultyID = f.ID
GROUP BY
CASE
WHEN f.Name IN ('FacultyName1', 'FacultyName2', 'FacultyName3') THEN 'FacultyGroup1'
WHEN f.Name IN ('FacultyName4', 'FacultyName5') THEN 'FacultyGroup2'
END;
If your "group" logic becomes too long then it will look messy in your query, so you might want to pre-calculate this. You could do this by using a sub-query for example, so one part of your query (the sub query) would convert faculties to groups and the other "main" part would count the students per group.
I have a table Teacher that contains a TeacherPIN as well as a table Student that contains a TeacherPIN referencing a Teacher. The idea is that a Teacher contains a certain amount of Students.
I am tasked with finding the teacher with the most students. I am currently using the query:
select t.TeacherPIN, count(s.TeacherPIN)
from Teacher t, Student s
where t.TeacherPIN = s.TeacherPIN
and ((select count(s1.TeacherPIN) from Student s1 where s1.TeacherPIN = t.TeacherPIN) >=
(select count(s2.TeacherPIN) from Student s2 where s2.TeacherPIN = (select t1.TeacherPIN from Teacher t1)));
I'm sure I'm making this way more complicated than I should be, but I've been at it for a while now and am hoping someone could push me in the right direction.
Thanks!
To find just one teacher (of possibly many) with maximum number of students:
SELECT TeacherPIN
, COUNT(*) AS NumberOfStudents
FROM Student
GROUP BY TeacherPIN
ORDER BY NumberOfStudents DESC
LIMIT 1
To find all of them:
SELECT TeacherPIN
, COUNT(*) AS NumberOfStudents
FROM Student
GROUP BY TeacherPIN
HAVING COUNT(*) =
( SELECT COUNT(*) AS NumberOfStudents
FROM Student
GROUP BY TeacherPIN
ORDER BY NumberOfStudents DESC
LIMIT 1
)