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 之一中。

我执行了以下不起作用的操作(尽管使用“包含”但对 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 methodid' 回复 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 的查询计划的解释。

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 回答