私はこのようなネストされた関連付けを持っています:
# note : irregular inflection (proj_paquet_mesures : proj_paquets_mesures)
class ProjPaquetMesures < ActiveRecord::Base
...
has_many :proj_mesures
end
class ProjMesure < ActiveRecord::Base
...
has_many :proj_projets
end
class Projprojet < ActiveRecord::Base
...
has_many :proj_sous_projets
end
class ProjSousProjet < ActiveRecord::Base
...
has_many :proj_charges
end
class ProjCharge < ActiveRecord::Base
# integer value
attr_accessible :montant
end
すべての 'montant' の合計を proj_paquet_mesures の 1 つにネストしたいと考えています。
機能していない次のことを実行します(「includes」を使用してもSQLクエリでは効率的ではありません):
proj_paquet_mesures = ProjPaquetMesures.includes([{:proj_mesures => {:proj_projets => {:proj_sous_projets => :proj_charges}}}]).find(1)
total_charges_of_my_paquet_mesures = proj_paquet_mesures.proj_mesures.proj_projets.proj_sous_projets.proj_charges.sum(:montant)
コードの最初の行では、1 つの結合クエリが期待される代わりに、4 つのクエリが生成されます。
ProjPaquetMesures Load (8.9ms) SELECT "proj_paquets_mesures".* FROM "proj_paquets_mesures" WHERE "proj_paquets_mesures"."id" = $1 LIMIT 1 [["id", 1]]
ProjMesure Load (1.4ms) SELECT "proj_mesures".* FROM "proj_mesures" WHERE "proj_mesures"."proj_paquet_mesures_id" IN (1)
ProjProjet Load (0.6ms) SELECT "proj_projets".* FROM "proj_projets" WHERE "proj_projets"."proj_mesure_id" IN (3)
ProjSousProjet Load (0.8ms) SELECT "proj_sous_projets".* FROM "proj_sous_projets" WHERE "proj_sous_projets"."proj_projet_id" IN (1)
ProjCharge Load (2.7ms) SELECT "proj_charges".* FROM "proj_charges" WHERE "proj_charges"."proj_sous_projet_id" IN (2)
コードの 2 行目はまったく機能しません。
何か案が ?
===更新===
最初の回答に続いて、PostgreSQL は MySQL よりも SQL 標準に準拠しているように見えるため、集計関数で表示する選択した列ごとに「GROUP BY 」句が必要です。だから私は次のコードを試しました:
proj_paquet_mesures = ProjPaquetMesures.joins([{:proj_mesures => {:proj_projets => {:proj_sous_projets => :proj_charges}}}]).select("proj_charges.id, sum(proj_charges.montant) as total_montant").group([id]).find(1)
しかし、RailsNameError: undefined local variable or method
は main:Object` の id' で応答します
公式ドキュメントでは、グループ メソッドを適用する場所が不明であり、説明と例が不足しています。
だから私は proj_charges.id を削除し、これで終わります:
proj_paquet_mesures = ProjPaquetMesures.joins([{:proj_mesures => {:proj_projets => {:proj_sous_projets => :proj_charges}}}]).select("sum(proj_charges.montant) as total_montant").find(1)
total_charges_of_my_paquet_mesures = proj_paquet_mesures.total_montant
それは機能しますが、ある日 proj_charges.id ごとに合計を計算したい場合、これは機能しません。
さらに、この 4 つのレベルでネストされた結合によって生成されたクエリ プランは、Rails が専門的な複雑なデータベースを管理できるほど成熟していないことを示しています。
「JOIN」SQLコマンドの使用を開始するとき、テーブルに参加させる順序をデータベースに伝えることができる必要があります。そうしないと、深くネストされた JOIN を使用すると、データベースの応答性の問題に直面することになります。
そのためには、JOIN 命令の間に括弧を使用して、どのテーブルをどの順序で結合する必要があるかをデータベースに指示できる必要があります。Rails ではまだそれができません。
代わりに、レールの find_by_sql メソッドを使用して SQL に依存する必要があります。
ProjSouProjet.find_by_sql ['SELECT sp.id AS "proj_sous_projet_id", SUM(c.montant) AS total FROM proj_charges c JOIN (proj_sous_projets sp JOIN (proj_projets p JOIN (proj_mesures m JOIN proj_paquets_mesures pm ON m.proj_paquet_mesures_id = pm.id AND pm.id = ?) ON p.proj_mesure_id = m.id) ON sp.proj_projet_id = p.id) ON c.proj_sous_projet_id = sp.id GROUP BY sp.id', 1]
Join と PostgreSQL のクエリ プランニングについては、こちらで詳しく説明しています。