【问题标题】:How to return the X greatest rows for each row in a table如何为表中的每一行返回 X 个最大的行
【发布时间】:2015-08-12 02:52:36
【问题描述】:

假设我有两张桌子(如下)。编写选择以从每个部门获得最高薪水的 2 名员工的最佳方法是什么?假设可能有很多部门。

输出:

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

【问题讨论】:

  • 您使用哪个版本的 SQL Server?
  • 我没有固定到某个版本

标签: sql sql-server


【解决方案1】:

此类选择的最佳选择是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

【讨论】:

  • 我以前没见过那个,正是我要找的!它也是一种可读性很强的语法!不错!
  • 所以看起来 CROSS APPLY 本质上是 INNER JOIN 而 OUTER APPLY(在答案中)是 OUTER JOIN 版本。
  • 所以我在 CROSS APPLY 上阅读文档的方式是它选择左侧(部门)的所有内容,然后为每一行运行选择右侧。你是这么想的吗?
  • @FuzzyJulz,我认为它像:循环遍历左表中的所有行,然后为每一行从应用 sybquery 中选择一些内容,并从左侧连接一行并从右侧获取该行的结果.
【解决方案2】:

你可以像这样使用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

【讨论】:

  • 你应该提到这种方式他不会得到所有部门,例如,如果还没有员工。
  • 是的,你是对的,但 OP 希望输出也包含员工姓名,所以我认为他的意思是让部门只包含员工。
  • sqlfiddle.com/#!6/377f7/1 - 我已经开始写同样的例子,但是既然你先发布了答案,就在这里。
  • 是的,我在考虑有员工的部门,但无论如何我都很好。
  • 好吧,实际上 Giorgi Nakeuri 是第一个。他的回答也是正确的。您可以标记任何适合您并解决您的问题的答案。 sqlfiddle.com/#!6/377f7/8
【解决方案3】:

这个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

编辑:将 maxi 更改为 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        

【讨论】:

    猜你喜欢
    • 2019-10-22
    • 1970-01-01
    • 1970-01-01
    • 2023-01-29
    • 1970-01-01
    • 2021-12-06
    • 2012-05-07
    相关资源
    最近更新 更多