T-SQL offers ranking functions that allow you to calculate the rank according to a field or aggregate.
Given these tables :
declare @people table (personid int primary key,name nvarchar(20))
declare @classes table (personid int ,class nvarchar(20))
insert into @people(personid,name)
values
(1,'Bob'),
(2,'Bill'),
(3,'Barbara'),
(4,'Bonnie'),
(5,'Joe')
insert into @classes (personid,class)
values
(1,'Math'),
(1,'Science'),
(2,'Math'),
(2,'English'),
(3,'Science'),
(3,'English'),
(4,'English'),
(4,'Math'),
(4,'Science')
(5,'Science')
The following query will calculate the person rank according to the number of classes taken:
select p.name,count(*) As Classes,rank() over (order by count(*) desc) as Rank
from @classes c
inner join @people p on p.personid=c.personid
group by p.name
Which returns Bonnie with 3 classes. All other students come second with 2 classes:
name Classes Rank
------- ------- ----
Bonnie 3 1
Barbara 2 2
Bill 2 2
Bob 2 2
Joe 1 5
RANK will skip positions if there are ties. That's why Joe has a rank of 5.
You can't use a ranking function in a WHERE or HAVING clause. To return only the first student(s) you need to use a subquery or CTE, eg:
select name,classes
from (
select p.name,count(*) As Classes,rank() over (order by count(*) desc) as Rank
from @classes c
inner join @people p on p.personid=c.personid
group by p.name
) r
where rank=1
Or
;with r as (
select p.name,count(*) As Classes,rank() over (order by count(*) desc) as Rank
from @classes c
inner join @people p on p.personid=c.personid
group by p.name
)
select name,classes
from r
where rank=1
Both queries will return :
name Classes
------- -------
Bonnie 3
If you want to find the N best students, you should use DENSE_RANK and return rows where the rank is less than or equal to N. With DENSE_RANK Joe's rank would be 3.
The following query will return students in the first two places :
with r as (
select p.name,count(*) As Classes,dense_rank() over (order by count(*) desc) as Rank
from @classes c
inner join @people p on p.personid=c.personid
group by p.name
)
select name,classes
from r
where rank<=2