这个标题有点晦涩,所以这里有个例子。假设我们有一个Rails 3应用程序,其模型为ShipPirateParrot。一艘船有很多海盗,而一条海盗有很多鹦鹉。

Ship.includes(pirates: :parrots).where('parrots.name LIKE ?', '%polly%')

这将返回至少有一名海盗和至少一只鹦鹉,其名称类似于“ polly”的飞船。我也希望它能为那些船渴望装载所有的海盗和鹦鹉...但是实际上,只有那些具有匹配的鹦鹉的海盗才被热切地装载,而在那些之中,只有匹配的鹦鹉被急切地装载。生成的SQL是这样的:

SELECT ships.id AS t0_r0, ships.name AS t0_r1, pirates.id AS t1_r0, pirates.name AS t1_r1, parrots.id AS t2_r0, parrots.name AS t2_r1 FROM ships LEFT OUTER JOIN pirates ON pirates.ship_id = ships.id LEFT OUTER JOIN parrots ON parrots.pirate_id = pirates.id WHERE (parrots.name LIKE '%polly%')

在没有条件的情况下执行Ship.includes(pirates: :parrots)时,ActiveRecord会生成一堆查询,这些查询在某种程度上更接近我想要的查询:

SELECT ships.* FROM ships
SELECT pirates.* FROM pirates WHERE pirates.ship_id IN (ship IDs from previous query)
SELECT parrots.* FROM parrots WHERE parrots.pirate_id IN (pirate IDs from previous query)


如果我能以某种方式将第一个查询更改为使用第一个示例中的SQL,它将完全满足我的要求:

SELECT ships.* FROM ships LEFT OUTER JOIN pirates ON pirates.ship_id = ships.id LEFT OUTER JOIN parrots ON parrots.pirate_id = pirates.id WHERE (parrots.name LIKE '%polly%')
SELECT pirates.* FROM pirates WHERE pirates.ship_id IN (ship IDs from previous query)
SELECT parrots.* FROM parrots WHERE parrots.pirate_id IN (pirate IDs from previous query)


但是我不知道有什么方法可以让ActiveRecord做到这一点,或者我自己或者“手动”连接急切加载(在我的情况下,这是避免N + 1查询爆炸的必要方法)。任何想法或建议,将不胜感激。

最佳答案

Ship.joins(pirates: :parrots).where('parrots.name LIKE ?', '%polly%').preload(pirates: :parrots)


需要导轨3+

关于ruby-on-rails - 按子级属性过滤父级,但渴望加载所有子级,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/17861345/

10-10 05:12