我确实有这样的嵌套关联:
# 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 之一中。
我执行了以下不起作用的操作(尽管使用“包含”但对 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)
第一行代码产生 4 个查询,而不是signle 预期的一个连接查询:
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)
第二行代码根本不起作用。
任何想法 ?
=== 更新 ===
在第一个答案之后,似乎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)
但是 Rails 用NameError: undefined local variable or method
id' 回复 main:Object`
官方文档不清楚在哪里应用分组方法,并且缺乏解释和示例。
所以我删除了 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 还不能让你这样做。
相反,您必须使用 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 的查询计划的解释。