2

私はこのようなネストされた関連付けを持っています:

# 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 のクエリ プランニングについては、こちらで詳しく説明しています。

4

1 に答える 1

2

これはうまくいくはずです。単一のクエリ。

 proj_paquet_mesures = ProjPaquetMesures.join([{:proj_mesures => {:proj_projets => {:proj_sous_projets => :proj_charges}}}]).select("proj_charges.*, sum(proj_charges.monatent) as total_monatant)").find()
 proj_paquet_mesures.total_monatent
于 2013-03-07T14:40:33.930 に答える