Students have mentors, who are also students. How do I get the name of students that are mentoring more than 4 people? - sql

The students table has 4 fields. Student ID, name, location, and mentor ID. The mentor ID points to a student ID. Students can mentor many people, however a person can only have one mentor.
The two methods I have come up with are as follows:
SELECT s.Name
FROM Students s
WHERE s.Id IN (SELECT s2.MentorId
FROM Students s2
GROUP BY s2.MentorId
HAVING COUNT(s2.MentorId) > 4)
OR
SELECT s2.Name
FROM students s
JOIN students s2 ON s2.Id = s.MentorId
GROUP BY s2.Name
HAVING COUNT(s2.Name) > 4
Are these correct? They do not run for me. I am unsure if this is an error with my SQL or my code.
Thanks!

No, they are not correct, but it's only a small, simple mistake. (same mistake in both).
You are counting the wrong column in the having clause.
Personally, I would go with the second option, only replace the COUNT(s2.Name) to COUNT(s.Id) - and if your database supports it, COUNT(DISTINCT s.Id) is even better:
SELECT s2.Name
FROM students s
JOIN students s2
ON s2.Id = s.MentorId
GROUP BY s2.Name
HAVING COUNT(DISTINCT s.Id) > 4

I would start with aggregation. The students who are mentoring more than 4 students are given by:
select s.mentorid
from students s
group by s.mentorid
having count(*) > 4;
To get the full student record, you can then use in, exists, or join:
select s.* -- or whatever columns you like
from students s join
(select s.mentorid
from students s
group by s.mentorid
having count(*) > 4
) m
on s.id = m.mentorid;

You were grouping by s2.name having a number of s2.name greater than 4. You never use the same field for grouping and the aggregate function. What you really want here is grouping by s2.name having a number of s.name (you want to count the corresponding students for each mentor mentor : GROUP BY mentor HAVING COUNT students > 4)
You should infact use s.id because otherwise it will not work if you have several students with the same name
SELECT s2.Name
FROM students s
JOIN students s2 ON s2.Id = s.MentorId
GROUP BY s2.Name
HAVING COUNT(s.id) > 4
Tested and it works :)

Related

'ALL' concept in SQL queries

Relational Schema:
Students (**sid**, name, age, major)
Courses (**cid**, name)
Enrollment (**sid**, **cid**, year, term, grade)
Write a SQL query that returns the name of the students who took all courses.I'm not sure how I capture the concept of 'ALL' in a SQL query.
EDIT:
I want to be able write it without aggregation as I want to use the same logic for writing the query in relational algebra as well.
Thanks for the help!
One way of writing such queries is to count the number of course and number of courses each student took, and compare them:
SELECT s.*
FROM students s
JOIN (SELECT sid, COUNT(DISTINCT cid) AS student_courses
FROM enrollment
GROUP BY sid) e ON s.sid = e.sid
JOIN (SELECT COUNT(*) AS cnt
FROM courses) c ON cnt = student_cursed
This gives course combinations that are possible but haven't been taken...
SELECT s.sid, c.cid FROM students CROSS JOIN courses
EXCEPT
SELECT sid, cid FROM enrollment
So, you can then do the same with the student list...
SELECT sid FROM students
EXCEPT
(
SELECT DISTINCT
sid
FROM
(
SELECT s.sid, c.cid FROM students CROSS JOIN courses
EXCEPT
SELECT sid, cid FROM enrollment
)
AS not_enrolled
)
AS slacker_students
I don't like it, but it avoids aggregation...
SELECT *
FROM Students
WHERE NOT EXISTS (
SELECT 1 FROM Courses
LEFT OUTER JOIN Enrollment ON Courses.cid = Enrollment.cid
AND Enrollment.sid = Students.sid
WHERE Enrollment.sid IS NULL
)
btw. names of tables should be in singular form, not plural

Counting Attendance Per Student

Software: PowerSchool (Oracle database) Education software
When I run the sql for one student the attendance count in correct. When I add addition students the count becomes the total from all the students, and that total is listed for each student. How do I reset the count after each student or does the subquery go in a different location? Any suggestions appreciated. ~Liz
SELECT s.DCID,
s.lastfirst,
(SELECT COUNT(distinct att.ID)
FROM Students s JOIN Attendance att ON
s.id=att.studentid
JOIN Attendance_code attc ON
attc.id=att.attendance_codeID
WHERE s.id=att.studentid
AND s.id=5538
AND att.Att_Mode_Code='ATT_ModeDaily'
AND att.yearid=27
AND attc.Presence_Status_CD = ('Absent')) AS TAbs
FROM Students s
WHERE s.enroll_tatus=0
AND s.grade_level IN (6, 7, 8)
AND s.id=5538
ORDER BY s.grade_level ASC, s.lastfirst ASC;
Your query should be something like this:
SELECT s.DCID,
s.lastfirst,
(SELECT COUNT(distinct att.ID)
FROM Students s JOIN Attendance att ON
s.id=att.studentid
JOIN Attendance_code attc ON
attc.id=att.attendance_codeID
WHERE s.id=att.studentid
AND s.id=5538
AND att.Att_Mode_Code='ATT_ModeDaily'
AND att.yearid=27
AND attc.Presence_Status_CD = ('Absent')) AS TAbs
FROM Students s
WHERE s.enroll_tatus=0
AND s.grade_level IN (6, 7, 8)
AND s.id=5538
GROUP BY s.DCID, s.lastfirst #You should add this line
ORDER BY s.grade_level ASC, s.lastfirst ASC;
3 things you should change:
Remove both student's IDs filters. You want to see all students, not just the one with ID 5538.
Use different alias for the same table! You are referencing the Students table twice, and both times with the same alias (S).
Link the outmost Student table ID with the subquery's Student table ID, so each COUNT(DISTINCT att.ID) is calculated by student, and not overall.
Here is the edited query:
SELECT
s1.DCID,
s1.lastfirst,
(
SELECT
COUNT(distinct att.ID)
FROM
Students s2 -- Use different alias!
JOIN Attendance att ON s2.id=att.studentid
JOIN Attendance_code attc ON attc.id=att.attendance_codeID
WHERE
att.Att_Mode_Code='ATT_ModeDaily' AND
att.yearid=27 AND
attc.Presence_Status_CD = ('Absent') AND
s1.id = s2.id -- Relate both Student tables
) AS TAbs
FROM
Students s1
WHERE
s1.enroll_tatus=0 AND
s1.grade_level IN (6, 7, 8)
ORDER BY
s1.grade_level ASC,
s1.lastfirst ASC;

Writing two queries (sort students and cities according to average grades)

I have three tables:
1) Students: studentID (KEY), name, surname, address
2) Exams: examID (KEY), examName
3) Grades: studenID (KEY), examID(KEY), grade
How to write SQL query to show the best students (for example those with average grade above 9)?
How to write SQL query to rank Cities (column address) according to the average grade of their students?
I'm a system engineer, working with Unix and Linux systems and I am new in SQL, I only know about SQL basics, and I was trying to do this for past three days, with no success, so please help me. I presume it's not a complex thing for one who's experienced in SQL. Thanks a lot.
your first query to show the best students :
SELECT student.surname, students.surename, students.address
FROM
students INNER JOIN Grades ON Grades.StudentID=Students.StudentID
INNER JOIN Exams ON Grades.examID=exams.examID WHERE Grades.grade=
(SELECT MAX(grade) FROM Grades WHERE examID=exams.examID)
your second query to rank cities:
SELECT students.address
FROM
students INNER JOIN Grades ON Grades.StudentID=Students.StudentID
INNER JOIN Exams ON Grades.examID=exams.examID order by grades.grade DESC
Refer the fiddle here:
LINK 1 : http://sqlfiddle.com/#!4/ab4de6/19
LINK 2 : http://sqlfiddle.com/#!4/ab4de6/32
Below Queries should help you in Oracle:
--List of Students having Average grade >=9
SELECT S.studentID, S.NAME, S.SURNAME, S.ADDRESS, A.AVG_GRADE FROM
STUDENTS S JOIN
(
SELECT studentID, AVG(GRADE) AVG_GRADE FROM GRADES
GROUP BY studentID
) A
ON S.studentID = A.studentID
AND A.AVG_GRADE >=9
ORDER BY A.AVG_GRADE, S.studentID;
--------------------------------------------------------------------
--- Rank cities
SELECT A.ADDRESS, A.AVG_GRADE, ROWNUM RANKING FROM
(
SELECT S.ADDRESS, AVG(G.GRADE) AVG_GRADE FROM
STUDENTS S JOIN GRADES G
ON S.STUDENTID = G.STUDENTID
GROUP BY S.ADDRESS
ORDER BY 2 DESC
) A;
You need to know about the following concepts.
INNER QUERY / SUB QUERY
JOINS
AGGREGATE FUNCTIONS (Average Calculations)
GROUP BY
ORDER BY
ROWNUM

SQL issue,challenge

Say we have two entities:teacher and student.
each teacher has multiple student.
Now I want to:
query for at most 5 teachers,and for each teacher, no more than 10 of his student.
So far this can be done quite easily by:
select *,
(
select GROUP_CONCAT('<sid>',students.name,'</sid>') from students on
teachers.id=students.teacher limit 10
) as students
from teachers limit 5
But that's not the whole story yet.
AND
If anyone of the teachers has more than 10 students,should return true for that teacher,otherwise false
How to do this in SQL?
Pseudo SQL: one row of results for each of the 10 students for each of the 5 teachers
select t.teacher_id, s.student_id,
case when t2.count > 10 then 'true' else 'false' end
from
(select top 5 *
from teachers
order by teacher_id) t
join
(select top 10 *
from students s1 join teachers t1 on s1.teacher_id = t1.teacher_id
order by student_id) s
on t.teacher_id = s.teacher_id
join
(select teacher_id, count(*) as count
from teachers t join students s on t.teacher_id = s.teacher_id
group by teacher_id) t2
on t2.teacher_id = t.teacher_id
LINQ makes this super-easy
Use a subquery for every teacher selected.

MYSQL get amount of results

When you use mysql, you can do something like this:
SELECT * FROM student WHERE teacher_id = 1
...then you get the amount of results, and with that the result. But I only want the amount of results but then for every teacher.
Is that possible in mysql like sizeof or something?
Also, if there are teachers that have no students is it true that there not in the list? or does it have to say 0?
Do you mean you want the number of students for every teacher? You can use something like this:
SELECT teacher_id, count(*) AS student_count
FROM student
GROUP BY teacher_id
This will not include teachers that don't have any students. To include them in the results, you need to use a JOIN (assuming you have a teacher table):
SELECT teacher.id, sum(if(student.id IS NULL, 0, 1)) AS student_count
FROM teacher
LEFT JOIN student ON student.teacher_id=teacher.id
GROUP BY teacher.id
SELECT teacher_id, count(*) as count FROM student GROUP BY teacher_id;
Use:
SELECT t.teacher_id,
IFNULL(ns.num_students, 0)
FROM TEACHERS t
LEFT JOIN (SELECT s.teacher_id,
COUNT(s.student_id) 'num_students'
FROM STUDENTS s
GROUP BY s.teacher_id) ns ON ns.teacher_id = t.teacher_id