我试图找出为什么我的查询没有按照我想要的方式行事。以下声明正试图为每款福特车型提供最新车型:
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 |
答案 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)')