What is the alternative for ALL and EVERY in SQLite? - sql

Hey im new to SQL and I'm trying to find the names of students enrolled in the maximum number of classes using SQLite on sqliteonline.com
These are the relations:
This is what I have:
SELECT S.sname
FROM Student S
WHERE S.snum IN (SELECT E.snum
FROM Enrolled E
GROUP BY E.snum
HAVING COUNT (*) >= ALL (SELECT COUNT (*)
FROM Enrolled E2
GROUP BY E2.snum ))
AND this is the error that I get:
I'm guessing the ALL keyword doesn't exist in SQLite or is used differently so I'm wondering what I should do next.
I'm also having the same problem with the EVERY keyword.
What I'm trying to solve:
For each faculty member that has taught classes only in room R128, print the faculty
member's name and the total number of classes she or he has taught.
What I have:
SELECT F.fname, COUNT(*) AS CourseCount
FROM Faculty F, Class C
WHERE F.fid = C.fid
GROUP BY F.fid, F.fname
HAVING EVERY ( C.room = "R128" )
What I get:

You can use ORDER BY and LIMIT:
SELECT S.sname
FROM Student S
WHERE S.snum IN (SELECT E.snum
FROM Enrolled E
GROUP BY E.snum
HAVING COUNT(*) = (SELECT COUNT(*)
FROM Enrolled E2
GROUP BY E2.snum
ORDER BY COUNT(*) DESC
LIMIT 1
)
);
Note: There are other ways to express the query logic. This specifically addresses the question that you asked.

One option uses window functions:
select s.name
from student s
inner join (
select snum, rank() over(order by count(*) desc) rn
from enrolled e
group by snum
) e on e.snum = s.snum
where rn = 1
If your version of SQLite does not support window functions (which were added in version 3.25), I would recommend a join and filtering with a having clause:
select s.name
from students s
inner join enrolled e on e.snum = s.snum
group by s.snum, s.name
having count(*) = (
select count(*)
from enrolled
group by snum
order by count(*) desc limit 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.

SQL MAX() grouping

Tables:
student(sid, sname, sex, age, year, gpa)
major(dname, sid)
Question:
For each department with more than 15 students majoring in the department, we want to print information about the student(s) with the highest GPA within the department. In particular, for each such student, we want to print the student id, student name and GPA, and the department name the student is major in.
So far I have:
SELECT student.sid, student.sname, student.gpa, major.dname
FROM student
RIGHT JOIN major ON student.sid = major.sid
WHERE student.gpa IN (
SELECT MAX(gpa)
FROM student JOIN major ON student.sid = major.sid
GROUP BY dname
HAVING COUNT(dname) > 15
)
But it doesn't give me the accurate query. The clause inside the IN works but when put together in this way it doesn't actually match student.gpa to dname max GPA. What am I doing wrong here?
This Query gives:
enter image description here
I need:
enter image description here
Your inner query gives you the maximum GPA for each department. Then the outer query returns all students who have a GPA equal to any of the maximum GPA's, regardless of the department. The quickest fix of your code is to use a correlated subquery, that will find the maximum GPA for the student's specific department.
SELECT s.sid, s.sname, s.gpa, sm.dname
FROM student s
RIGHT JOIN major sm ON s.sid = sm.sid
WHERE student.gpa IN (
SELECT MAX(ds.gpa)
FROM student ds JOIN major dm ON ds.sid=dm.sid
WHERE dm.dname = sm.dname
GROUP BY dm.dname
HAVING COUNT(dm.dname)>15
)
This query:
select dname, max(s.gpa) maxgpa
from major m inner join student s
on s.sid = m.sid
group by dname
having count(s.sid) > 15
returns all the departments with more than 15 students and the highest gpa in that department.
Join it to the 2 tables like this:
select s.sid, s.sname, s.gpa, t.dname
from (
select dname, max(s.gpa) maxgpa
from major m inner join student s
on s.sid = m.sid
group by dname
having count(s.sid) > 15
) t
inner join major m on m.dname = t.dname
inner join student s on s.sid = m.sid and s.gpa = t.maxgpa
Or with window functions:
select t.sid, t.sname, t.gpa, t.dname
from (
select m.dname, s.*,
rank() over (partition by m.dname order by s.gpa desc) rn,
count(s.sid) over (partition by m.dname) counter
from major m inner join student s
on s.sid = m.sid
) t
where t.counter > 15 and t.rn = 1
If you change your WHERE ... IN statement to be an INNER JOIN, you can connect more fields.
SELECT student.sid, student.sname, student.gpa, major.dname
FROM student
RIGHT JOIN major
ON student.sid = major.sid
INNER JOIN (
SELECT MAX(gpa) as max_gpa, dname
FROM student JOIN major ON student.sid=major.sid
GROUP BY dname
HAVING COUNT(dname)>15
) as dept_gpa
ON student.gpa = dept_gpa.max_gpa
AND major.dname = dept_gpa.dname

Missing expression problem in SQL using Oracle

I want to get the number of employees by department and I wrote this script using Oracle but it always says that there is a missing expression
The columns used in my tables :
department :name (the name of the department) -
depnum (the id of the department"primary key"),
employee : empnum (the id of the employee) -
depnum (the id of the department in which the employee in question is working "foreign key")
Query:
select
s.name
from
department s
inner join
employee p on s.depnum = p.depnum
group by
s.name
having
count(p.empnum) = max(select count(p.empnum)
from employee p, department s
where s.depnum = p.depnum
group by s.name) ;
If you want the number of employees by department, I would expect something like this:
select s.name, count(*) as num_employees
from department s inner join
employe p
on s.depnum = p.depnum
group by s.name ;
If you want the department names with the maximum number of names, you can use a having clause:
select s.name, count(*) as num_employees
from department s inner join
employe p
on s.depnum = p.depnum
group by s.name
having count(*) = (select max(cnt)
from (select count(*) as cnt
from employee e2
group by e2.depnum
) e2
);
The problem with your query is that you are attempting to take the max() of a subquery. That syntax is not allowed -- and not necessary.
you sql statement is not correct that's why it thrown that error. I think you tried something like below
select s.name
from department s
inner join employe p on s.depnum=p.depnum
group by s.name
having count(p.empnum)=
select max(cnt) from
(
select count(p.empnum) as cnt
from employe p join department s
on s.depnum=p.depnum
group by s.name
) t;

SQL query to find students who received an A for every course taken

I have following schema:
Students(sid, firstname, lastname, status, gpa, email)
Courses(dept_code, course#, title)
Classes(classid, dept_code, course#, sect#, year, semester, limit, class_size)
Enrollments(sid, classid, lgrade)
I need some help to find out all the students who received an A for every course taken.
I might suggest doing this with an aggregation:
select e.sid
from enrollement e
group by e.sid
having min(lgrade) = max(lgrade) and min(lgrade) = 'A';
try this
select * from students
where sid not in (select distinct sid from enrollement where coalesce (lgrade,'X') <> 'A')
It means: take all students where none of his/her grade is other than A
if you wanna to get also the name of the class and course, you have to join also both tables
Try not to overthink this:
SELECT s.*
FROM STUDENTS s
WHERE s.GPA = 4.0
That'll work for the A=4, B=3, C=2, D=1, F=0 case (standard American grading system).
For non-standard systems (such as my kids high school where A=5 for honors and advanced placement classes) we can't trust the GPA:
SELECT s.*
FROM STUDENTS s
INNER JOIN (SELECT SID, COUNT(*) AS CLASS_COUNT
FROM ENROLLMENTS
GROUP BY SID) cc
INNER JOIN (SELECT SID, COUNT(*) AS A_GRADE_COUNT
FROM ENROLLMENTS
WHERE LGRADE = 'A'
GROUP BY SID) ag
ON ag.SID = s.SID
WHERE CLASS_COUNT = A_GRADE_COUNT
Best of luck.
I think this is the clearest:
select e.sid
from enrollement e
group by e.sid
having count(case when lgrade = 'A' then 1 else 0 end) = count(*) and count(*) > 0

SQL group function nested too deeply

I'm trying to create an sql query that will return the smallest occurrence of an id appearing between two tables however I keep getting the error with the line HAVING MIN(COUNT(E.C_SE_ID)). Oracle is saying that the group by function is nested too deeply.
I cannot think of another way of returning C_SE_ID
SELECT CS.C_SE_ID, MIN(COUNT(E.C_SE_ID))
FROM COURSE_SECTION CS, ENROLLMENT E, LOCATION L
WHERE CS.C_SE_ID=E.C_SE_ID
AND CS.LOC_ID=L.LOC_ID
AND L.BLDG_CODE='DBW'
GROUP BY CS.C_SE_ID
HAVING MIN(COUNT(E.C_SE_ID));
in enrollment table s_id and c_se_id are linked, I'm trying to get all the s_id that are related to that c_se_id. with the updated query oracle doesn't like the select * (for obvious reasons) but when I change it too e.c_Se_id I get nothing.
SELECT E.S_ID
FROM COURSE_SECTION CS, ENROLLMENT E
WHERE CS.C_SE_ID=E.C_SE_ID
AND E.C_SE_ID =(
select *
from (select CS.C_SE_ID, count(*) as cnt,
max(count(*)) over (partition by cs.c_se_id) as maxcnt
from COURSE_SECTION CS join
ENROLLMENT E
on CS.C_SE_ID=E.C_SE_ID join
LOCATION L
on CS.LOC_ID=L.LOC_ID
where L.BLDG_CODE='DBW'
GROUP BY CS.C_SE_ID
order by count(*) desc
) t
where cnt = maxcnt);
One way to do this is by nesting your query and then choosing the first row in the output:
select C_SE_ID, cnt
from (select CS.C_SE_ID, count(*) as cnt
from COURSE_SECTION CS join
ENROLLMENT E
on CS.C_SE_ID=E.C_SE_ID join
LOCATION L
on CS.LOC_ID=L.LOC_ID
where L.BLDG_CODE='DBW'
GROUP BY CS.C_SE_ID
order by count(*) desc
) t
where rownum = 1
Note I updated the join syntax to the more modern version using on instead of where.
If you want all minimum values (and there are more than one), then I would use analytic functions. It is a very similar idea to your original query:
select *
from (select CS.C_SE_ID, count(*) as cnt,
max(count(*)) over (partition by cs.c_se_id) as maxcnt
from COURSE_SECTION CS join
ENROLLMENT E
on CS.C_SE_ID=E.C_SE_ID join
LOCATION L
on CS.LOC_ID=L.LOC_ID
where L.BLDG_CODE='DBW'
GROUP BY CS.C_SE_ID
order by count(*) desc
) t
where cnt = maxcnt;
Try this instead of your original query:
SELECT E.S_ID
FROM ENROLLMENT E
where E.C_SE_ID in (select C_SE_ID
from (select CS.C_SE_ID, count(*) as cnt,
max(count(*)) over (partition by cs.c_se_id) as maxcnt
from ENROLLMENT E
LOCATION L
on CS.LOC_ID=L.LOC_ID
where L.BLDG_CODE='DBW'
GROUP BY e.C_SE_ID
) t
where cnt = maxcnt)
);
In addition to fixing the joins, I also removed all references to course_section. This table doesn't seem to be used (unless for filtering results), and removing it implifies the queries.