ActiveRecord嵌套SELECT - 我可以不用手动SQL吗?

时间:2009-12-04 00:17:49

标签: sql ruby arrays activerecord greatest-n-per-group

我有一张桌子(其中包括)一个名字和一个等级。我想返回所有唯一名称的集合,但对于返回的每个名称,我想选择排名最高的行。使用两个嵌套的SELECT语句很简单:

SELECT * FROM (SELECT * FROM foo ORDER BY rank DESC) AS ordered GROUP BY name

MySQL为每个名称带来第一个“命中”,这(因为早期的ORDER BY)将始终是排名最高的名称。

现在,如果我想用ActiveRecord连接到这个表,我有点不知所措。我可以把上面的内容扔进find_by_sql,但这感觉很脏。我试过像

这样的东西
result = foo.all
result.delete_if do |item|
  isOutranked = false
  result.each do |row|
    if (row.name == item.name) and (row.rank > item.rank) then isOutranked = true
  end
  isOutranked
end

我认为有效,但仍然觉得应该有更好的方法。通过ActiveRecord技巧或更优雅的数组操作来解决它将是受欢迎的!

1 个答案:

答案 0 :(得分:2)

  

MySQL为每个名称带来第一个“命中”,这(因为早期的ORDER BY)将始终是排名最高的名称。

您用于返回组中顶行的查询保证。这只是实施的巧合,而且可能会发生变化。 不要依赖于此。

您正在尝试解决我经常在StackOverflow上发布的“greatest-n-per-group”问题。这是一个更可靠地得到答案的查询:

SELECT t1.*
FROM foo AS t1
LEFT OUTER JOIN foo AS t2
 ON (t1.name = t2.name AND t1.rank < t2.rank)
WHERE t2.name IS NULL;

做同样事情的替代方案:

SELECT *
FROM foo AS t1
WHERE NOT EXISTS 
    (SELECT * FROM foo AS t2
     WHERE t1.name = t2.name AND t1.rank < t2.rank);
  

我可以把上面的内容扔进find_by_sql,但这感觉很脏。

ActiveRecord对于某些类型的查询非常方便,但您无法使用ActiveRecord解决每个数据库查询。越早解决这个问题,你就能越早完成工作并取得成功。 SQL不会咬你。