Query without partition by or functions like rank() - sql

Suppose we have the table students (name, grade, group, year)
We want a query that ranks for each group the corresponding students.
I know that this can be done easy with rank() OVER ( partition by group order by grade DESC ). But I think that this can also be done with a self join or a subquery. Any ideas?

The equivalent to rank() is:
select s.*,
(select 1 + count(*)
from students s2
where s2.group = s.group and
s2.grade > s.grade
) as rank
from students s;

Related

Why doesn't DISTINCT work in this case? (SQL)

SELECT DISTINCT
employees.departmentname,
employees.firstname,
employees.salary,
employees.departmentid
FROM employees
JOIN (
SELECT MAX(salary) AS Highest, departmentID
FROM employees
GROUP BY departmentID
) departments ON employees.departmentid = departments.departmentid
AND employees.salary = departments.highest;
Why doesn't the DISTINCT work here?
I'm trying to have each department to show only once because the question is asking the highest salary in each department.
Use the ROW_NUMBER() function, as in:
select departmentname, firstname, salary, departmentid
from (
select e.*,
row_number() over(partition by departmentid, order by salary desc) as rn
from employees e
) x
where rn = 1
I'm trying to have each department to show only once because the question is asking the highest salary in each department.
Use window functions:
SELECT e.*
FROM (SELECT e.*,
ROW_NUMBER() OVER (PARTITION BY departmentID ORDER BY salary DESC) as seqnum
FROM employees e
) e
WHERE seqnum = 1;
This is guaranteed to return one row per department, even when there are ties. If you want all rows when there are ties, use RANK() instead.
Why doesn't the DISTINCT work here?
DISTINCT is not a function; it is a keyword that will eliminate duplicate rows when ALL the column values are duplicates. It does NOT apply to a single column.
The DISTINCT keyword has "worked" (i.e. done what it is intended to do) because there are no rows where all the column values are a duplicate of another row's values.
However, it hasn't solved your problem because DISTINCT is not the correct solution to your problem. For that, you want to "fetch the row which has the max value for a column [within each group]" (as per this question).
Gwen, Elena and Paula all have the same salary
and they are in the same department

SQL query to get instructors and students has invalid identifier

Hi I have a schema that look like this
I made two queries that had to do this:
Find the names of the top 4 instructors who have taught the most number of distinct courses. Display also the total number of courses taught.
Output columns: InstructorName, NumberOfCoursesTaught
Sort by: NumberOfCoursesTaught in descending order
Find the top 2 students who have taken the most number of courses.
Output columns: S_ID, StudentName, NumberOfCourses
Sort by: NumberOfCourses in descending order
For query 1, I wrote:
SELECT name AS InstructorName, count(course_id) AS NumberOfCourses
FROM Teaches
WHERE name IN (SELECT name FROM Instructor where Instructor.i_id = Teaches.i_id)
GROUP BU i_id
ORDER BY COUNT(course_id) DESC;
For query 2, I wrote
SELECT s_id as S_ID, name as StudentName, count(course_id) as NumberOfCourses
FROM Takes
WHERE name IN (SELECT name FROM Student WHERE Takes.s_id = Student.s_id)
GROUP BY s_id
ORDER BY COUNT(course_id) DESC;
Both say:
"NAME" Invalid identifier
I suggest that you should use another logic to build your queries. Here is a demonstration for the first query ; from there on, you should be able to create the second query (and maybe post it as an answer?).
Start with an aggregate query that computes the number of teaches per instructor id, looking at the Teaches table:
SELECT i_id, COUNT(*) cnt FROM Teaches GROUP BY i_id
Then rank each record by decreasing count, using window function ROW_NUMBER() :
SELECT i_id, cnt, ROW_NUMBER() OVER(ORDER BY cnt DESC) rn
FROM (SELECT i_id, COUNT(*) cnt FROM Teaches GROUP BY i_id) t
All that is left to do is get thte instructor name (JOIN ON Instructor) and filter in the top 4 records
SELECT i.name InstructorName, x.cnt NumberOfCoursesTaught
FROM (
SELECT i_id, cnt, ROW_NUMBER() OVER(ORDER BY cnt DESC) rn
FROM (SELECT i_id, COUNT(*) cnt FROM Teaches GROUP BY i_id) t
) x
INNER JOIN Instructor i ON i.i_id = x.i_id
WHERE x.rn <= 4
ORDER BY x.cnt desc

To display the name of the department that has the least student count in SQL

Tables:
Department (dept_id,dept_name)
Students(student_id,student_name,dept_id)
I am using Oracle. I have to print the name of that department that has the minimum no. of students. Since I am new to SQL, I am stuck on this problem. So far, I have done this:
select d.department_id,d.department_name,
from Department d
join Student s on s.department_id=d.department_id
where rownum between 1 and 3
group by d.department_id,d.department_name
order by count(s.student_id) asc;
The output is incorrect. It is coming as IT,SE,CSE whereas the output should be IT,CSE,SE! Is my query right? Or is there something missing in my query?
What am I doing wrong?
One of the possibilities:
select dept_id, dept_name
from (
select dept_id, dept_name,
rank() over (order by cnt nulls first) rn
from department
left join (select dept_id, count(1) cnt
from students
group by dept_id) using (dept_id) )
where rn = 1
Group data from table students at first, join table department, rank numbers, take first row(s).
left join are used is used to guarantee that we will check departments without students.
rank() is used in case that there are two or more departments with minimal number of students.
To find the department(s) with the minimum number of students, you'll have to count per department ID and then take the ID(s) with the minimum count.
As of Oracle 12c this is simply:
select department_id
from student
group by department_id
order by count(*)
fetch first row with ties
You then select the departments with an ID in the found set.
select * from department where id in (<above query>);
In older versions you could use RANK instead to rank the departments by count:
select department_id, rank() over (order by count(*)) as rnk
from student
group by department_id
The rows with rnk = 1 would be the department IDs with the lowest count. So you could select the departments with:
select * from department where (id, 1) in (<above query>);

Top 2 Salary Grouped By Department

Below is the table I am referring to.
I want to find ou the 2 Employees in each department with highest salary.
Further to the above answer, if there are ties (multiple employees sharing the same salary), you can use the following to bring them all through instead of just picking two at random (which is what the ROW_NUMBER clause will do)
SELECT *
FROM (
SELECT *, DENSE_RANK() OVER (PARTITION BY Dept ORDER BY Salary DESC) AS rn
FROM MyTable ) t
WHERE t.rn <= 2
Use ROW_NUMBER() to get the top salaries per Department, then select the first two records from each departmental partiton:
SELECT *
FROM (
SELECT *, ROW_NUMBER() OVER (PARTITION BY Dept ORDER BY Salary DESC) AS rn
FROM MyTable ) t
WHERE t.rn <= 2

How to aggregate top 5 numbers in SQL Server 2000

Hi i I am facing a problem, it is that the students can have more than 5 subjects but i have to sum of only 5 subjects total marks of which the student secured the highest. One way to say sum of top 5 total marks obtained by any student.
How do i proceed please help me. Thanks in advance.
In SQL 2000 you will need to use a subselect to determine how many rows with the same ID have a higher mark. Then Filter for rows that have less then 5 higher marked rows above it:
select
ID, Sum(Mark)
From Table1 t
where
(Select count(*)
from Table1 it
where it.id=t.id and it.mark>t.mark) <5
group by ID
ROW_NUMBER isn't in sql-server-2000 unfortunately. You can achieve the same result with a subquery though. Hopefully this is what you're looking for:
SELECT s.studentid, SUM(s.total_marks)
FROM students s
WHERE s.sub_code IN (SELECT TOP 5 sub_code
FROM students a
WHERE a.studentid = s.studentid
ORDER BY total_marks DESC)
GROUP BY studentid
Working in fiddle
Here is a query that gives you only the 5 hightest marks per student:
SELECT studentID, total_marks,
row_number() OVER (PARTITION BY studentID, ORDER BY total_marks DESC) as rowN
FROM studentTable
WHERE rowN <= 5
So to get the total:
SELECT studentID, SUM(total_marks)
FROM
(
SELECT studentID, total_marks,
row_number() OVER (PARTITION BY studentID, ORDER BY total_marks DESC) as rowN
FROM studentTable
WHERE rowN <= 5
) T
GROUP BY studentID