MYSQL get amount of results - sql

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

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

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

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 :)

get number of students enrolled in the same course as a specific student

I have two sql queries
select course from enrolments where student = 101;
select count(student) as course from enrolments
GROUP by course
The first produces a list of course where student with id 101 is enrolled
The second return a total count of students enrolled in each course
How do I get the total enrolments for the courses student 101 is enrolled in?
Use bool_or() in having to ensure at least one of the students is 101 in the group:
select count(student) as course
from enrolments
group by course
having bool_or(student = 101)
For a less PostgreSQL-specific solution, you would need to use a JOIN (or EXISTS). Something like this:
select count(student) as course
from enrolments e
where exists(select 1
from enrolments s
where s.course = e.course
and s.student = 101)
group by course
I originally misunderstood the question. Here is an approach that uses two levels of aggregation:
select sum(numstudents)
from (select course, count(*) as numstudents
from enrolments
group by course
having sum( (student = 1)::int) > 0
) c;
The subquery gets the value per course.
It looks like you just have to add 'where' condition to your second query:
SELECT
count(student) as course
FROM
enrolments
WHERE
student = 101
GROUP by course;

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

Getting single records back from joined tables that may produce multiple records

I've got a student table and an enrollment table; a student could have multiple enrollment records that can be active or inactive.
I want to get a select that has a single student record and an indicator as to whether that student has active enrollments.
I thought about doing this in an inline UDF that uses the student ID in a join to the enrollment table, but I wonder if there's a better way to do it in a single select statement.
The UDF call might look something like:
Select Student_Name,Student_Email,isEnrolled(Student_ID) from Student
What might the alternative - with one SQL statement - look like?
select Student_Name,
Student_Email,
(select count(*)
from Enrollment e
where e.student_id = s.student_id
) Number_Of_Enrollments
from Student e
will get the number of enrollments, which should help.
Why not join to a secondary select? Unlike other solutions this isn't firing a subquery for every row returned, but gathers the enrollment data for everyone all at once. The syntax may not be quite correct, but you should get the idea.
SELECT
s.student_name,
s.student_email,
IsNull( e.enrollment_count, 0 )
FROM
Students s
LEFT OUTER JOIN (
SELECT
student_id,
count(*) as enrollment_count
FROM
enrollments
WHERE
active = 1
GROUP BY
student_id
) e
ON s.student_id = e.student_id
The select from enrollments could also be redone as a function which returns a table for you to join on.
CREATE FUNCTION getAllEnrollmentsGroupedByStudent()
RETURNS #enrollments TABLE
(
student_id int,
enrollment_count int
) AS BEGIN
INSERT INTO
#enrollments
(
student_id,
enrollment_count
) SELECT
student_id,
count(*) as enrollment_count
FROM
enrollments
WHERE
active = 1
GROUP BY
student_id
RETURN
END
SELECT
s.student_name,
s.student_email,
e.enrollment_count
FROM
Students s
JOIN
dbo.getAllEnrollmentsGroupedByStudent() e
ON s.student_id = e.student_id
Edit:
Renze de Waal corrected my bad SQL!
Try someting like this:
SELECT Student_Name, Student_Email, CAST((SELECT TOP 1 1 FROM Enrollments e WHERE e.student_id=s.student_id) as bit) as enrolled FROM Student s
I think you can also use the exists statement in the select but not positive
try to avoid using udfs or subqueries, they are performance killers. banjolity seems to havea good solution otherwise because it uses a derivd table instead of a UDF or subselect.
select students.name,
decode(count(1), 0, "no enrollments", "has enrollments")
from students, enrollments
where
students.id = enrollments.sutdent_id and
enrollments.is_active = 1 group by students.name
Of course, replace the decode with a function your database uses (or, a case statement).