简体   繁体   English

使用涉及多态关联的多个联接的Rails ActiveRecord查询

[英]Rails ActiveRecord query using multiple joins involving polymorphic association

I'm trying to figure out how I can replicate the following SQL query using AR given the model definitions below. 考虑到以下模型定义,我试图弄清楚如何使用AR复制以下SQL查询。 The cast is necessary to perform the average. 强制转换是执行平均的必要条件。 The result set should group foo by bar (which comes from the polymorphic association). 结果集应按栏将foo分组(来自多态关联)。 Any help is appreciated. 任何帮助表示赞赏。

SQL: SQL:

SELECT AVG(CAST(r.foo AS decimal)) "Average", s.bar
FROM rotation r INNER JOIN cogs c ON r.cog_id = c.id 
           INNER JOIN sprockets s ON s.id = c.crankable_id
           INNER JOIN machinists m ON r.machinist_id = m.id
WHERE c.crankable_type = 'Sprocket' AND 
      r.machine_id = 123 AND 
      m.shop_id = 1
GROUP BY s.bar

ActiveRecord Models: ActiveRecord模型:

class Rotation < ActiveRecord::Base
    belongs_to :cog
    belongs_to :machinist
    belongs_to :machine
end

class Cog < ActiveRecord::Base
    belongs_to :crankable, :polymorphic => true
    has_many :rotation
end

class Sprocket < ActiveRecord::Base
    has_many :cogs, :as => :crankable
end

class Machinist < ActiveRecord::Base
    belongs_to :shop
end

UPDATE 更新

I've figured out a way to make it work, but it feels like cheating. 我想出了一种使它起作用的方法,但是感觉就像在作弊。 Is there are a better way than this? 有没有比这更好的方法了?

Sprocket.joins('INNER JOIN cogs c ON c.crankable_id = sprockets.id', 
               'INNER JOIN rotations r ON r.cog_id = c.id',
               'INNER JOIN machinists m ON r.machinist_id = m.id')
    .select('sprockets.bar', 'r.foo')
    .where(:r => {:machine_id => 123}, :m => {:shop_id => 1})
    .group('sprockets.bar')
    .average('CAST(r.foo AS decimal)')

SOLUTION

Albin's answer didn't work as-is, but did lead me to a working solution. Albin的回答并非按原样工作,但确实使我找到了可行的解决方案。 First, I had a typo in Cog and had to change the relation from: 首先,我在Cog中有一个错字,必须将关系更改为:

has_many :rotation

to the plural form: 复数形式:

has_many :rotations

With that in place, I am able to use the following query 有了这个,我可以使用以下查询

Sprocket.joins(cogs: {rotations: :machinist})
    .where({ machinists: { shop_id: 1 }, rotations: { machine_id: 123}})
    .group(:bar)
    .average('CAST(rotations.foo AS decimal)')

The only real difference is that I had to separate the where clause since a machine does not belong to a machinist. 唯一真正的区别是,由于机器不属于机械师,因此我不得不分隔where子句。 Thanks Albin! 谢谢阿尔宾!

I think this code is a little simpler and taking more help from AR 我认为这段代码更简单,而且可以从AR中获得更多帮助

Sprocket
.joins(cogs: {rotations: :machinist})
.where({ machinists: { machine_id: 123, shop_id: 1 } } )
.group(:bar)
.average('CAST(rotations.foo AS decimal)')

The select clause was unnecessary, you don't have to select values since you only need them internally in the query, AR helps you decide what you need afterwards. select子句是不必要的,您不必选择值,因为您只需要在查询中内部使用它们,AR可以帮助您确定以后需要的内容。

I tested this out using a similar structure in one of my own projects but it is not the exact same models so there might be a typo or something in there if it does not run straight up. 我在自己的一个项目中使用类似的结构对其进行了测试,但是它并不是完全相同的模型,因此如果它不能直接运行,可能会出现打字错误或其他错误。 I ran: 我跑了:

Activity
.joins(locations: {participants: :stuff})
.where({ stuffs: { my_field: 1 } })
.group(:title)
.average('CAST(participants.date_of_birth as decimal)')

producing this query 产生这个查询

SELECT AVG(CAST(participants.date_of_birth as decimal)) AS average_cast_participants_date_of_birth_as_decimal, title AS title 
FROM `activities` 
INNER JOIN `locations` ON `locations`.`activity_id` = `activities`.`id` 
INNER JOIN `participants` ON `participants`.`location_id` = `locations`.`id` 
INNER JOIN `stuffs` ON `stuffs`.`id` = `participants`.`stuff_id` 
WHERE `stuffs`.`my_field` = 1 
GROUP BY title

which AR makes in to a hash looking like this: AR生成的散列如下所示:

{"dummy title"=>#<BigDecimal:7fe9fe44d3c0,'0.19652273E4',18(18)>, "stats test"=>nil} 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM