Ассоциации с: uniq => true не всегда возвращают уникальные результаты - PullRequest
1 голос
/ 26 января 2010

У меня есть коллекция классов, связанных с географией: State, Msa, County, City и т. Д. Все классы происходят от базового класса Location. Классы связаны, по большей части, через денормализованную таблицу соединений, называемую географией. Итак, у меня есть ...

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

Теперь, когда я запускаю из консоли следующее:

>> msas = Msa.find(:all, :include=>"states", :conditions=>{"states_locations"=>{"id"=>"1"}})

Я получаю правильное количество результатов (13 в данном случае). Однако, запустив SQL, который производит этот вызов find, я получаю тысячи результатов (опять же, географическая таблица - это своего рода datamart, поэтому я использую опцию: uniq для ассоциации).

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' 

Я предполагаю, что это означает, что Rails загружает 1000 тысяч записей в память И ТОГДА в Ruby, сокращая результаты до отдельного набора Msas (в данном случае); кажется немного неэффективным. Кроме того, следующие последующие вызовы возвращают разные результаты:

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

Мои вопросы:

  1. Почему Rails не использует DISTINCT в запросе, который он генерирует из вызова find? Я предполагаю, что это потому, что я попросил: include =>: states. Должен ли я использовать: объединяет вместо?
  2. Почему Rails возвращает неуникальные результаты при вызове msas.first.states? Разве ассоциация, имеющая: uniq => true, не должна обеспечивать уникальность результатов?
  3. Почему мне нужно использовать псевдоним таблицы, который Rails использует для состояний "версия" таблицы местоположений, то есть: условие => {: states_locations => {: id => 1}}? Rails, похоже, не понимает: include =>: states,: condition => {: states => {: id => 1}}. Есть ли способ детерминированного прогнозирования псевдонима таблицы?

Любые идеи будут высоко оценены.

Заранее спасибо, Jason

1 Ответ

1 голос
/ 26 января 2010

У вас там много вопросов, дайте мне посмотреть, поможет ли это ...

Вы правы, что rails вызовет вызов sql для получения всех результатов, а затем active_record отфильтрует уникальные записи.

Если вы хотите избежать этого, вы можете сделать следующее:

has_many :states, :through => :geographies, :select => "DISTINCT states.*"

Этот пост содержит интересный анализ

Также с вашей линией:

msas = Msa.find(:all, :include=>"states", :conditions=>{"states_locations"=>{"id"=>"1"}})

Он не возвращает уникальных результатов, потому что вы не используете установленные вами отношения. Вы, вероятно, хотите сделать что-то вроде:

@msas = State.find(state_id).msas

Удачи

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...