2013-03-07 3 views
1

Я гнездились ассоциации, как это:Как суммировать через несколько ассоциаций has_many?

# 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) 

Вторая строка кода не работает вообще.

Любая идея?

=== UPDATE ===

После первого ответа, оказывается, что PostgreSQL более уступчив к SQL стандарта, чем MySQL поэтому нуждается в «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 ид»для основной: Object`

official doc неясно, где применить метод группового и недоставало объяснения и примеры.

Так что я удалил 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 еще не в состоянии позволить вам сделать это.

Вместо этого вам нужно полагаться на SQL с помощью метода rails find_by_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] 

here Больше объяснения на Присоединяйтесь и планирование запросов в PostgreSQL.

ответ

1

Это должно работать. Единый запрос.

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 
+0

Я удалил некоторые опечатки и скобки: proj_paquet_mesures = ProjPaquetMesures.joins ([{: proj_mesures => {: proj_projets => {: proj_sous_projets =>: proj_charges}}}]). select ("proj_charges. *, sum (proj_charges.montant) как total_montant"). find (1). Но у меня все еще есть ошибка PG: PG :: Ошибка: ERROR: столбец «proj_charges.id» должен появиться в предложении GROUP BY или использоваться в агрегированной функции LINE 1: SELECT proj_charges. *, Sum (proj_charges.montant) как total_m ... – Douglas

Смежные вопросы