AR - 过滤组查询

时间:2014-08-26 13:55:12

标签: mysql sql ruby-on-rails ruby activerecord

我试图找出为什么我的查询没有按照我想要的方式行事。以下声明正试图为每款福特车型提供最新车型:

Car.where("make = 'Ford'").group(:model_id).having('year = MAX(year)')

但似乎是按model_id进行分组,然后按年份过滤所有数据(不仅仅针对每个组)。这是它生成的SQL:

SELECT `cars`.* FROM `cars` WHERE `cars`.`make` = 'Ford' GROUP BY model_id HAVING year = MAX(year)

我做错了什么?我可以使用不同的查询吗?

更新

以下是数据样本:

| make | model_id | year |
| Ford | 1        | 2000 |
| Ford | 1        | 2002 |
| Ford | 1        | 2010 |
| Ford | 2        | 2012 |
| BMW  | 3        | 2012 |
| BMW  | 4        | 2014 |

它应该在查询后返回以下内容:

| Ford | 1        | 2010 |
| Ford | 2        | 2012 |

3 个答案:

答案 0 :(得分:0)

您还可以使用聚合函数作为SELECT子句的一部分,它应该产生所需的结果:

cars = Car.select('MAX(year)').group(:model_id)
cars.to_a.last.attributes
#=> {name: 'BMW', model_id: 4, ..., max: 2014}

您可以使用别名一次选择多个聚合:

cars = Car.select('MAX(year) as max_year, MAX(hp) as max_hp').group(:model_id)
cars.to_a.last.attributes
#=> {name: 'BMW', model_id: 4, ..., max_year: 2014, max_hp: 120}

在Postgresql中,you can use window functions查找组中的极值。它不是很漂亮,但结果是一个适当的ActiveRecord关系,可以与其他范围链接:

Car.from('(SELECT *, rank() OVER (PARTITION BY model_id ORDER BY year DESC) FROM cars) AS cars').where('rank = 1')

为了便于阅读,可以对其进行重构:

partition = 'PARTITION BY model_id ORDER BY year DESC'
subquery = Car.arel_table.project("*, rank() OVER (#{partition})")
Car.from("(#{subquery}) AS cars").where('rank = 1')

现在你甚至可以做一些事情,比如“为每个团队获得两辆最新车型”:

Car.from("(#{subquery}) AS cars").where('rank <= 2')

答案 1 :(得分:0)

感谢@ p11y发布了一个帮助我通过以下查询解决问题的链接。注意:它可能不是最有效的。如果您愿意,请提供更有效的查询。

subquery = "select max(year) from cars as c where c.model_id = cars.model_id"
Car.where("make = 'Ford' AND year = (#{subquery})")

更新:添加子查询以便于阅读。

答案 2 :(得分:-1)

对于初学者来说,你的预期结果是不可能的 - 你说这个查询是针对福特的,但你期待宝马的结果。我假设这是一个剪切和粘贴错误。

您面临的问题是您需要告诉having子句有关查询的更多信息 - 即您感兴趣的每组数据的最大(年)。

在mysql中提取所需结果的SQL查询如下所示:

mysql> select make, model, max(year) as 'year' from cars group by model having year=max(year);

结果:

+------+-------+------+
| make | model | year |
+------+-------+------+
| Ford |     1 | 2010 |
| Ford |     2 | 2012 |
| BMW  |     3 | 2012 |
| BMW  |     4 | 2014 |
+------+-------+------+

将其翻译为活动记录,您需要一个类似以下的查询:

Car.select('make, model, max(year) as year').group(:model).having('year = max(year)')

仅适用于福特:

Car.select('make, model, max(year) as year').where(:model=>'Ford').group(:model).having('year = max(year)')