2015-05-29 2 views
3

Предположим, у меня есть две таблицы (см. Ниже). Каков наилучший способ написать выбор, чтобы получить 2 сотрудника с самой высокой зарплатой от каждого отдела? Предположим, что существует много отделов.Как вернуть X наибольших строк для каждой строки в таблице

выход:

employee_name | salary | department_id 
John   | 65000 | 1 
Sally   | 60000 | 1 
Lucy   | 40000 | 2 
James   | 80000 | 3 
Harry   | 65000 | 3 

Таблицы:

Сотрудник

employee_name | salary | department_id 
John   | 65000 | 1 
Sally   | 60000 | 1 
Connor  | 55000 | 1 
Judy   | 55000 | 1 
Lucy   | 40000 | 2 
James   | 80000 | 3 
Harry   | 65000 | 3 
Penny   | 56000 | 3 

Отдел

department_id | name 
1    | Sales 
2    | Marketing 
3    | IT 
+1

Какую версию SQL-сервера вы используете? –

+0

Я не привязан к определенной версии – FuzzyJulz

ответ

3

Лучшим для такого типа выборов является OUTER APPLY. Он предназначен для этого вида работ:

select d.department_id, oa.employee_name, oa.salary 
from Departments d 
outer apply(select top 2 e.employee_name, e.salary 
      from Employee e 
      where d.department_id = e.department_id 
      order by e.salary desc) oa 

Если вы не хотите, чтобы отделы там, где их нет работников, то просто изменить outer apply к cross apply

+0

Я не видел этого раньше, именно то, что я искал! Это вполне читаемый синтаксис! хороший! – FuzzyJulz

+0

Итак, похоже, что CROSS APPLY - это, по сути, INNER JOIN, в то время как OUTER APPLY (в ответе) - это версия OUTER JOIN. – FuzzyJulz

+0

@FuzzyJulz, точно –

1

Вы можете использовать ROW_NUMBER() как это.

;WITH CTE as 
(
SELECT employee_name,Salary,department_id, 
    ROW_NUMBER()OVER(PARTITION BY department_id ORDER BY Salary DESC) rn 
FROM Employee 
) 
SELECT employee_name,Salary,d.department_id,d.name 
FROM CTE c 
INNER JOIN Departments d ON d.department_id = c.department_id 
WHERE rn <= 2 
+0

Вы должны навязать так, что он не получит все отделы, если, например, пока нет сотрудников. –

+0

Да, вы правы, но OP ожидает, что вывод будет содержать имя сотрудника, так что я думаю, что он хочет получить только отделы с сотрудниками. – ughai

+1

http://sqlfiddle.com/#!6/377f7/1 - Я начал писать один и тот же пример, но с тех пор, как вы сначала отправили ответ, вот оно. –

1

Этот CTE метод является достаточно эффективным
Использовать этот код:

Create TABLE #table 
(
    name varchar(10), 
    salary varchar(10), 
    depid int 

) 

insert into #table values('John','65000',1) 
insert into #table values('Sally','60000',1) 
insert into #table values('Connor','55000',1) 
insert into #table values('Judy','55000',1) 
insert into #table values('Lucy','65000',2) 
insert into #table values('Kevin','55000',2) 
insert into #table values('Ram','60000',2) 
insert into #table values('James','80000',3) 
insert into #table values('Harry','65000',3) 
insert into #table values('Penny','56000',3) 

select * from #table 


;With CTE as 
(
select name,salary,depid,ROW_NUMBER() over(partition by depid order by salary desc) as maxisal from #table 
) 
select name,salary,depid from CTE 
where maxisal<=2 

EDIT: изменил макси для maxisal, чтобы заставить его работать - Fuzzyjulz

Примечание: я добавил два данные для Depid 2

Выход:

name salary depid 
John 65000 1   
Sally 60000 1   
Lucy 65000 2   
Ram  60000 2   
James 80000 3   
Harry 65000 3   
Смежные вопросы