SQL to Relation Algebra - sql

How can I white this in relation algebra? I'm always getting confused with those GROUP BY and HAVING COUNT...Can't really formulate that.
SELECT DISTINCT Name
FROM studies, course
WHERE (SELECT COUNT(course_id) FROM studies
GROUP BY course_id HAVING COUNT (course_id) > 1) >= 2
AND (SELECT course_id FROM studies
GROUP BY course_id HAVING COUNT (course_id) > 1) = course.course_id;

If I'm understanding your comment correctly, then you just need to join the tables together and use a single group by with having:
select c.course_id, c.name
from course c
join studies s on c.course_id = s.course_id
group by c.course_id, c.name
having count(s.course_id) > 1

Related

Display courses with at least 10 students

I have the following tables:
Students (id, name, surname, study_year, department_id)
Courses(id, name)
Course_Signup(id, student_id, course_id, year)
I want to display the courses to which at least 10 students have signed up for, only using subqueries (no group-by, join or set operations). This could be easily implemented using data aggregation and join:
SELECT c.name, COUNT(csn.course_id)
FROM Course_Signup csn
JOIN Courses c
ON csn.course_id = c.id
GROUP BY c.name
HAVING COUNT(csn.course_id) >= 10
But how would I do this only using subqueries? Is there any other way, other than COUNT, to get the number of courses? Thank you, in advance!
You can use a correlated sub-query to retrieve the name:
SELECT (SELECT c.name FROM Courses c WHERE csn.course_id = c.id) AS name,
COUNT(*)
FROM Course_Signup csn
GROUP BY
course_id
HAVING COUNT(*) >= 10
Note: you should also GROUP BY the primary key the uniquely identifies the course as there may be two courses with the same name.
If you also don't want to use GROUP BY then:
SELECT name
FROM Courses c
WHERE 10 <= ( SELECT COUNT(*)
FROM Course_Signup csn
WHERE csn.course_id = c.id )
or, to also get the number of sign-ups:
SELECT *
FROM (
SELECT name,
( SELECT COUNT(*)
FROM Course_Signup csn
WHERE csn.course_id = c.id ) AS num_signups
FROM Courses c
)
WHERE num_signups >= 10;
You could do:
SELECT c.name
FROM Courses c
WHERE (
SELECT COUNT(*)
FROM Course_Signup csn
WHERE csn.course_id = c.id
) >= 10
which only uses a subquery and has no group-by, join or set operations.
fiddle
If you wanted the actual count in the result set then you would need to repeat the subquery in the select list.
You might also need to do COUNT(DISTINCT cs.student_id) if there might be duplicates; particularly if the same student can sign up in multiple years - but then you might want to restrict to a single year anyway.

Correlated Query with more than one table SQL

I need to list some staff details who are registered for 2 or more courses using a sub query.
The staff_id in the staff table is linked to the course table, so s.staff_id = c.staff_id. But I'm really confused and don't know how to correct.
SELECT STAFF.STAFF_ID, STAFF.FIRST_NAME, STAFF.LAST_NAME, STAFF.TITLE, COURSE.ID
FROM STAFF, COURSE C
WHERE STAFF.STAFF_ID = COURSE.STAFF_ID
AND (SELECT COURSE.COURSE_ID FROM COURSE
GROUP BY STAFF.STAFF_ID
HAVING COUNT(COURSE.COURSE_ID) >=2);
I think that you don’t need a correlated subquery. An aggregate JOIN query with a HAVING clause should do it, like :
SELECT
S.STAFF_ID,
S.FIRST_NAME,
S.LAST_NAME,
S.TITLE
FROM
STAFF AS S
INNER JOIN COURSE AS C
ON S.STAFF_ID = C.STAFF_ID
GROUP BY
S.STAFF_ID,
S.FIRST_NAME,
S.LAST_NAME,
S.TITLE
HAVING
COUNT(*) >=2
;
This will retrieve all staff persons that have at least two courses.
If you want staff that have more than one course, a join is not necessary -- implicit or explicit. Here is a more direct approach:
select s.*
from staff s
where s.staff_id in (select c.staff_id
from course c
group by c.staff_id
having count(*) >= 2
);
You will need to link your sub query back to your main query. At the moment you are just looking for ANY staff member that has more than two courses in your sub query. Try the below (untested).
For clarity especially when you are using sub queries it is a good idea to alias all of your tables.
SELECT S.STAFF_ID, S.FIRST_NAME, S.LAST_NAME, S.TITLE
FROM STAFF S, COURSE C
WHERE S.STAFF_ID = C.STAFF_ID
AND (SELECT C1.COURSE_ID FROM COURSE c1
where s.staff_id = c1.staff_id
HAVING COUNT(C1.COURSE_ID) >=2)
The above assumes that you need to get some columns out of course as well, however at the moment you have none in your first select so the query could be further simplified to the below if you do not need anything from course.
SELECT S.STAFF_ID, S.FIRST_NAME, S.LAST_NAME, S.TITLE
FROM STAFF S
where (SELECT C.COURSE_ID FROM COURSE c
where s.staff_id = c.staff_id
HAVING COUNT(C.COURSE_ID) >=2)
If you want only staff details or else can join if needed course details as well
SELECT Staff.* from Staff s where
s.staff_id in
(Select
staff_id from
(SELECT COURSE_ID,STAFF_ID
FROM COURSE
GROUP BY STAFF_ID
HAVING COUNT(COURSE.COURSE_ID) >=2)
);
or via join infact even in join you require a subquery reason being group by when grouping by s.staff_id the c.course_id values will become inconsistent and unsync with s.staff_id
Select s.* ,c.COURSE_ID from staff s
join course c
on s.staff_id IN (Select c.staff_id group by
c.staff_id
HAVING COUNT(*) >=2)

SQL not group by expression, Find student who has taken at least 5 courses

select s.name, s.id
from student s join takes t on t.id = s.id
where s.name like 'D%'
group by s.name, s.id
having (
select count(distinct c.course_id)
from course c
where c.dept_name = 'History' and c.course_id = t.course_id)>4
order by s.name
I am confused about how GROUP BY works. I am trying to find the students who has taken at least 5 courses from history department and name start with D.
Not sure with the nested subqueries...
course(course id, title, dept name, credits)
student(ID, name, dept name, tot_cred)
takes(ID, course_id, sec_id, semester, year, grade)
You have to additionally JOIN with course table:
select s.name, s.id
from student s
inner join takes t on t.id = s.id
inner join course c on c.course_id = t.course_id
where s.name like 'D%' and c.dept_name = 'History'
group by s.name, s.id
having count(distinct c.course_id) >= 5
The WHERE clause returns all students whose names start with a 'D' and have taken at least one course in history department. The HAVING clause filters out any students with 4 or less distinct courses in history department.

Favourite Course in Student Course Table Query

I have three tables:
Student(StudentID, StudentName)
Course(CourseID, CourseName)
StudentCourse(StudentID, CourseID) -- junction table to assign courses to students
How would I query to get the favourite course--"the course which has highest number of students enrolled"?
SQLFiddle
Try it by using TOP...WITH TIES
SELECT TOP 1 WITH TIES c.CourseName,
COUNT(c.CourseID) totalCount
FROM student a
INNER JOIN studentcourse b
ON a.studentID = b.studentID
INNER JOIN course c
ON b.courseID = c.courseID
GROUP BY c.CourseName
ORDER BY totalCount DESC
WITH TIES show the records that have the same highest number of counts.
SQLFiddle Demo
SELECT TOP 1 WITH TIES COURSEID
FROM STUDENTCOURSE
GROUP BY COURSEID
ORDER BY Count(*) DESC

Count within a count

REVISED:
Okay, thanks to all of your input, I figured out what I was doing wrong (sorry guys). I am grabbing the courseID which counts as a section NOT an actual course. For me to grab the actual courseName, I have to go over to the Course Table.
So now I have StudentID from Enrollment and CourseNum from Course that need to be used to count. I'll try and work with what you guys have provided to see if I can come up with the results.
EDIT:
Here's my revised SQL. This provides me with the total courses for each student. I'm getting there:
SELECT Count(DISTINCT Course.courseNum), Grades.studentID
FROM Grades INNER JOIN
Course ON Grades.courseID = Course.courseID
GROUP BY Grades.studentID;
Final Code just in case those who care:
SELECT COUNT(NumCourses) FROM
(SELECT Count(DISTINCT Course.courseNum)AS NumCourses
FROM Grades INNER JOIN
Course ON Grades.courseID = Course.courseID
GROUP BY Grades.studentID
HAVING Count(DISTINCT Course.courseNum) = 1) a;
Try this:
select count(studentId), count(courseId) from enrolment group by courseId having (count(courseId) = 2);
To get the total number of students on 4+ courses, this is the SQL:
SELECT COUNT(CourseCount) AS CourseCount
FROM (
SELECT StudentID, COUNT(CourseID) AS CourseCount
FROM enrollment
GROUP BY StudentID
HAVING (COUNT(CourseID) >= 4)) AS T
It is much simpler to get the student count using a second query.
SELECT COUNT(NumCourses) AS NumStudents, NumCourses FROM (SELECT COUNT(courseID) AS NumCourses FROM enrollment GROUP BY courseID HAVING COUNT(courseID) = 4) As SomeTableAlias
select count(*)
from (select student_id from enrollment group by student_id having count(*) = 4)
The inner query gives you the IDs of the students who enrolled in exactly 4 courses then I count them.
I thought you wanted the number of students who are enrolled in exactly four courses.
If you want to count the number of students and the number of courses, do this:
SELECT * FROM (SELECT COUNT(*) AS NumStudents FROM (SELECT DISTINCT studendid FROM enrollment)), (SELECT COUNT(*) AS NumCourses FROM (SELECT DISTINCT courseid FROM enrollment))
SELECT COUNT(*) FROM
(SELECT COUNT(*) FROM enrollment
GROUP BY studentid HAVING COUNT(*) = 4)