Getting around not being able to nest aggregators, sql server - sql

I'm grouping the tuples in a relation on InstructorNo and CourseNo, where a teacher is in a tuple with a course if they can teach it. I want to select instructors that can teach the same number of courses as the instructor in the list that can teach the most courses.
For example, if Mr.Smith can teach the most courses out of all the teachers in the relation I want to select Mr.Smith and anybody else that can teach the same number of courses he can.
What I'd really like to do is something like this:
select InstructorNo, COUNT(InstCourses.CourseNo) as NoTeachableCourses
from
...
group by InstructorNo
having COUNT(InstCourses.CourseNo) = MAX(COUNT(InstCourses.CourseNo))
But that's illegal because of the nested aggregations MAX(COUNT.... How can I get this functionality without being able to nest aggregators? I'm not sure how many teachers I'll need to select in a given query, so I'm not sure if TOP or TOP WITH TIES will work.

Use TOP or TOP WITH TIES:
select top(1) with ties InstructorNo, COUNT(InstCourses.CourseNo) as NoTeachableCourses
from
...
group by InstructorNo
order by NoTeachableCourses desc;

Related

If I have multiple values in a column, how can I count it in SQL?

Let me illustrate this:
Student_ID
Course
StudentID1
CourseA
StudentID2
CourseB
StudentID3
CourseA CourseB
There is an existing table that has data that looks like the one above (Not exactly Student & Course, this is more for illustration purpose) and my job is to do a count of how many students for each course. The table is very huge and I do not know how many and what are the courses are out there (Easily in thousands), so wonder if there is a way I can get a list of all these courses and their counts through SQL?
The current method that my team did is SELECT DISTINCT COURSE, COUNT(STUDENT_ID) FROM TABLE GROUP BY COURSE, but this does not work because it treats "CourseA CourseB" as its own.
There are some other columns in this table that I might need to do a SUM as well.
Appreciate any advice on this, thanks!
you could use below to find number of students for each course:
select course, count(*) as no_of_students
from table
group by course;

SQL Query Involving Finding Most Frequent Tuple Value in Column

I have the following relations:
teaches(ID,course_id,sec_id,semester,year)
instructor(ID,name,dept_name,salary)
I am trying to express the following as an SQL query:
Find the ID and name of the instructor who has taught the most courses(i.e has the most tuples in teaches).
My Query
select ID, name
from teaches
natural join instructor
group by ID
order by count(*) desc
I know this isn't correct, but I feel like I'm on the right track. In order to answer the question, you need to work with both relations, hence the natural join operation is required. Since the question asks for the instructor that has taught the most courses, that tells me that we are trying to count the number of times each instructor ID appears in the teaches relation. From what I understand, we are looking to count distinct instructor IDs, hence the group by command is needed.
Don't use natural joins: all they do is rely on column names to decide which columns relate across tables (they don't check for foreign keys constraints or the-like, as you would thought). This is unreliable by nature.
You can use a regular inner join:
select i.id, i.name
from teaches t
inner join instructor i on i.id = t.sec_id
group by i.id, i.name
order by count(*) desc
limit 1
Notes:
this assumes that column teaches.sec_id relates to instructor.id (I cannot see which other column could be used)
I added a limit clause to the query since you stated that you want the top instructor - the syntax may vary across databases
always prefix the column names with the table they belong to, to make the query unambiguous and easier to understand
it is a good practice (and a requirement in many databases) that in an aggregate query all non-aggregared columns listed in the select clause should appear in the group by clause; I added the instructur name to your group by clause

Selecting a grouping that matches a certain criteria, SQL

I have two relations, one is a list of the areas an instructor is able to teach (AreasOfInstructor(InstructorNo,AreaName)) and the other is the result of a subquery that returns a list of AreaNames. I want to group the AreaOfInstructor relation by InstructorNo, and then return each instructor (as represented by InstructorNo) that is able to teach all the areas returned by the subquery.
My attempt:
SELECT InstructorNo
FROM AreasofInstructor
GROUP BY InstructorNo
/**WHERE THE GROUP CONTAINS* (the list of AreaNames returned by the subquery)*/
I'm not sure what the actual SQL commands are that will implement the stuff between the stars on the last line. Thanks for the help!
Edit: Just to be clear, what I'm looking for is the set of instructors that are able to teach in the areas that are returned by the subquery.
To do this, you can join both relations, group by InstructorNo, and then validate that the distinct count of AreaNames per InstructorNo matches the distinct count of AreaNames in the AreaNames relation.
with AreaNames as (subquery)
select i.InstructorNo, count(distinct i.AreaName)
from AreasofInstructor i
join AreaNames n
on n.AreaName = i.AreaName
group by i.InstructorNo
having count(distinct i.AreaName) = (select count(distinct AreaName) from AreaNames)
It's better to use Common Table Expression are more readable than a sub-query.
Check if this is what you are looking for?
WITH Areas (AreaName)
AS
(
*sub-query goes here*
)
SELECT DISTINCT
InstructorNo
FROM
AreasOfInstructor AOI
INNER JOIN
Areas A ON AOI.AreaName = A.AreaName

SQL Group By error ORA-00979

I'm trying to give this query:
select s_name, course from Student group by course;
But I get an error (ORA-00979 Not a GROUP BY EXPRESSION).
I want to list the names of all the students that are in the same course.
Is there another method of doing this? If not, what is the proper way to implement this query? I would appreciate if someone could give me the exact code required.
One variant (Oracle 11g):
select course, listagg(s_name, ', ') within group (order by s_name)
from student
group by course;
Oracle 10g (undocumented secret function wm_concat)
select course, wm_concat(s_name)
from student
group by course;
For what you want you shouldn't use GROUP BY.
The intention of GROUP BY is to summarise information per group.
Since you want detail within each course, you should rather use ORDER BY to ensure that your output is simply sorted with students in the same course listed together.
select s_name, course
from Student
order by course
For an example of what GROUP BY is intended for, try the following:
select course, COUNT(*) as NumStudents
from Student
group by course

MySQL 5.5 Database Query Help

I am having a few issues with a DB query.
I have two tables, students (Fields: FirstName, LastName, StdSSN), and teachers (TFirstName, TLastName, TSSN) which I've stripped down for this example. I need to perform a query that will return all the students except for the students that are teachers themselves.
I have the query
SELECT student.FirstName, student.LastName
FROM `student`,`teachers`
WHERE student.StdSSN=teachers.TSSN
Which gives me a list of all the teachers who are also students it does not provide me with a list of students who are not teachers, so I tried changing to:
SELECT student.FirstName, student.LastName
FROM `student`,`teachers`
WHERE student.StdSSN!=teachers.TSSN
Which gives me a list of all the students with many duplicate values so I am a little stuck here. How can I change things to return a list of all students who are not teachers? I was thinking INNER/OUTER/SELF-JOIN and was playing with that for a few hours but things became complicated and I did not accomplish anything so I've pretty much given up.
Can anyone give me any advice? I did see the query before and it was pretty simple, but I've failed somewhere.
Using NOT IN
SELECT s.*
FROM STUDENTS s
WHERE s.stdssn NOT IN (SELECT t.tssn
FROM TEACHERS t)
Using NOT EXISTS
SELECT s.*
FROM STUDENTS s
WHERE NOT EXISTS (SELECT NULL
FROM TEACHERS t
WHERE t.tssn = s.stdssn)
Using LEFT JOIN / IS NULL
SELECT s.*
FROM STUDENTS s
LEFT JOIN TEACHERS t ON t.tssn = s.stdssn
WHERE t.column IS NULL
I used "column" for any column in TEACHERS other than what is joined on.
Comparison:
If the column(s) compared are nullable (value can be NULL), NOT EXISTS is the best choice. Otherwise, LEFT JOIN/IS NULL is the best choice (for MySQL).