3

そのタイトルは少し鈍いので、ここに例を示します。Shipモデル、Pirate、およびを含む Rails 3 アプリがあるとしParrotます。船にはたくさんの海賊がいて、海賊にはたくさんのオウムがいます。

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

これは、「ポリー」のような名前のオウムが少なくとも 1 匹いる海賊が少なくとも 1 匹いる船を返します。また、それらの船のすべての海賊とオウムを熱心にロードしたいのですが...実際には、一致するオウムを持つ海賊だけが熱心にロードされ、その中で一致するオウムだけが熱心にロードされます。生成された 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クエリの爆発を避けるために必要です)。アイデアやアドバイスをいただければ幸いです。

4

2 に答える 2

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

Rails 3+が必要

于 2014-09-02T07:06:04.643 に答える