J'ai une collection de classes liées à la géographie: Etat, Msa, Comté, Ville, etc. Les classes descendent toutes d'une classe de base. Les classes sont liées, pour la plupart, à travers une table de jointure dénormalisée appelée géographie. J'ai donc ...Associations avec: uniq => true ne retourne pas toujours des résultats uniques
class Geography < ActiveRecord::Base
belongs_to :state
belongs_to :county
belongs_to :city
# ... etc.
end
class Location < ActiveRecord::Base
end
class State < Location
has_many :geographies
has_many :msas, :through => :geographies, :uniq => true
# ... etc.
end
class Msa < Location
has_many :geographies
has_many :states, :through => :geographies, :uniq => true
# ... etc.
end
Maintenant, quand je lance ce qui suit de la console:
>> msas = Msa.find(:all, :include=>"states", :conditions=>{"states_locations"=>{"id"=>"1"}})
Je reviens le nombre correct de résultats (13 dans ce cas). Cependant, en exécutant le SQL que cet appel de recherche produit, je récupère des milliers de résultats (encore une fois la table de géographie est une sorte de datamart, c'est pourquoi j'utilise l'option: uniq sur l'association).
SELECT `locations`.`id` AS t0_r0,
`locations`.`parent_id` AS t0_r1,
`locations`.`type` AS t0_r2,
`locations`.`name` AS t0_r3,
`states_locations`.`id` AS t1_r0,
`states_locations`.`parent_id` AS t1_r1,
`states_locations`.`type` AS t1_r2,
`states_locations`.`name` AS t1_r3
FROM `locations`
LEFT OUTER JOIN `geography`
ON `locations`.`id` = `geography`.`msa_id`
LEFT OUTER JOIN `locations` states_locations
ON `states_locations`.`id` = `geography`.`state_id`
AND `states_locations`.`type` = 'State'
WHERE `states_locations`.`id` = '1'
AND `locations`.`type` = 'Msa'
Je suppose que cela signifie que Rails est en train de charger 1,000s des enregistrements en mémoire, puis, dans Ruby, réduisant les résultats à l'ensemble distinct de MSA (dans ce cas); semble un peu inefficace. En outre, les appels suivants après le retour des résultats variables:
>> msas.first.states.size # incorrect count
=> 192
>> msas.first.states.count # correct count
=> 1
>> msas.first.states # incorrect number of State objects
=> [#<State id: 1, ... >, ..., #<State id: 1, ... >]
>> msas.first.reload.states
=> [#<State id: 1, ... >] # correct number of State objects
Mes questions sont les suivantes:
- Pourquoi ne Rails utilise DISTINCT dans la requête, il produit de la trouver appel? Je suppose que c'est parce que je l'ai demandé à: include =>: states. Dois-je utiliser: jointures à la place?
- Pourquoi Rails renvoie-t-il des résultats non uniques lors de l'appel de msas.first.states? L'association ayant un: uniq => true ne devrait-elle pas imposer l'unicité des résultats?
- Pourquoi ai-je besoin d'utiliser l'alias de table que Rails utilise pour les états "version" de la table d'emplacements, c'est-à-dire: conditions => {: states_locations => {: id => 1}}? Rails ne semble pas comprendre: include =>: states,: conditions => {: states => {: id => 1}}. Existe-t-il un moyen de prédire de manière déterministe l'alias de la table?
Toute idée serait grandement appréciée.
Merci à l'avance, Jason
Salut Jonathan, merci pour la réponse. J'ai essayé d'utiliser l'option: select, cependant, j'ai trouvé que: select est ignoré lors de l'utilisation de: include. Aussi, pour être clair, la deuxième ligne de code que vous avez référencée * est * renvoyant des résultats uniques * dans Ruby * mais pas en SQL. Je jetterai un coup d'œil sur le post de Josh et je verrai si cela apporte un nouvel éclairage. Merci encore. – Jason
La publication de Josh confirme que Rails utilise effectivement Ruby, pas SQL, pour "unique-ify" les résultats dans une association has_many: through => xxx,: uniq => true. Cependant, cela ne permet pas de comprendre pourquoi msas.first.states renvoie des résultats non uniques. On pourrait penser que, puisque Rails est assez intelligent pour que le jeu de résultats original soit unique, quoique dans Ruby, il serait également assez intelligent pour que les associations d'objets dans le jeu de résultats soient également uniques. Peut-être que j'ai besoin de plonger dans le code des associations dans Rails. – Jason