这个标题有点迟钝,所以这里有一个例子。假设我们有一个带有模型Ship
、Pirate
和的 Rails 3 应用程序Parrot
。一艘船有_many 海盗,一艘海盗有_many 鹦鹉。
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 查询爆炸所必需的)。任何想法或建议将不胜感激。