轨道。用max()选择记录

时间:2014-11-06 22:04:07

标签: sql ruby-on-rails activerecord

我有一个这样的表,我想要返回最高薪水的前两个人(按姓名),以及相应薪水的记录。这是表

id, name, salary
1, Tom, 200
2, Tom, 300
3, Bob, 400
4, Bob, 500
5, Alice, 600
6, Alice, 700

我使用了这个命令

Employer.select("employers.*, max(employers.salary) as maxsalary").group("employers.name").order("maxsalary desc").limit(2)

期望的回报:

id, name, salary
6, Alice, 700
4, Bob, 500

我得到的似乎是这样的:

id, name, salary
5, Alice, 600
3, Bob, 400

无论如何选择响应最大值的记录?任何评论/答案都非常感谢。

1 个答案:

答案 0 :(得分:4)

这个问题实际上非常棘手! 这似乎很容易,但事实并非如此。

查询:

Employer.joins(%Q|
  LEFT JOIN employers as e 
  ON 
  e.name = employers.name 
  AND 
  employers.salary < e.salary
|).where('e.salary IS NULL').order('employers.salary DESC').limit(2)

如何运作!(我去过那里)

我们希望确保每位雇主的工资最高,然后获得最高的2位。


一些理论资料(如果您只想了解查询,请跳过此部分)

让Salary成为函数S(name,id),返回给定name和id的值 为了证明给定的工资(S(姓名,身份证))是最高的,我们必须证明这一点 我们要证明

  • ∀xS(姓名,身份证)&gt; S(姓名,x)(这个薪水高于其他所有人 这个名字的工资)

OR

  • ¬∃xS(name,id)&lt; S(姓名,x)(没有更高的薪水 那个名字)

第一种方法需要我们获取我不喜欢的那个名字的所有记录。

第二个需要一个明智的方式来说没有比这个更高的记录。


返回SQL

如果我们离开加入表格的名称和薪水小于联合表:

%Q|
      LEFT JOIN employers as e 
      ON 
      e.name = employers.name 
      AND 
      employers.salary < e.salary
    |

我们确保所有记录都有另一个具有较高薪水的记录,以便加入同一个用户:

employers.id, employers.name, employers.salary, e.id, e.name, e.salary
1           , Tom           , 200             , 2   , Tom   , 300
2           , Tom           , 300
3           , Bob           , 400             , 4   , Bob   , 500
4           , Bob           , 500
5           , Alice         , 600             , 6   , Alice   , 700
6           , Alice         , 700

这将有助于我们过滤每位雇主的最高工资,而无需分组:

where('e.salary IS NULL')

employers.id, employers.name, employers.salary, e.id, e.name, e.salary
2           , Tom           , 300
4           , Bob           , 500
6           , Alice         , 700

现在我们需要做的就是排序:

order('employers.salary DESC')

employers.id, employers.name, employers.salary, e.id, e.name, e.salary
6           , Alice         , 700
4           , Bob           , 500
2           , Tom           , 300

然后限制

limit(2)

employers.id, employers.name, employers.salary, e.id, e.name, e.salary
6           , Alice         , 700
4           , Bob           , 500

这就是我们需要的答案。


我们为什么不

1

Employer.order('salary desc').limit(2)

因为这将使我们获得与姓名无关的最高工资记录

employers.id, employers.name, employers.salary
5           , Alice         , 600
6           , Alice         , 700

2

Employer.select('DISTINCT(name)').order('salary desc').limit(2)

问题是它只会保留名称的第一个外观然后排序

employers.id, employers.name, employers.salary
1           , Tom           , 200
3           , Bob           , 400
5           , Alice         , 600