0

Я пытаюсь создать очень сложный запрос, и у меня проблемы с ним - так что я возвращаюсь к основам, чтобы попытаться что мне не хватает. Я читал Rails Guides для Active Record Associations и Active Record Query Interface (в частности, section 12 - присоединяется), и я не понимаю, как они связаны и почему требуется объединение/включение.Борясь, чтобы понять, почему объединения/включения необходимы с Rails, когда существует ассоциация в модели

На странице «Ассоциации» говорится: «Благодаря ассоциациям с активной записью мы можем оптимизировать эти и другие операции посредством декларативно говорящих Rails о наличии связи между этими двумя моделями». В разделе 12.2 на странице «Запрос» говорится: «Active Record позволяет использовать имена ассоциаций, определенных в модели, как ярлык для указания предложений JOIN для этих ассоциаций при использовании метода объединения».

Эти два утверждения несколько расходятся друг с другом. Если я создаю ассоциацию belongs_to, почему мне нужно соединение, если я пытаюсь извлечь данные из обеих таблиц? Глядя на это с другой стороны:

class Customer < ActiveRecord::Base 
    has_many :orders 
end 

class Order < ActiveRecord::Base 
    belongs_to :customer 
end 

Если я @orders = Order.all я могу выводить имя клиента, делая @orders.first.customer.name. Однако, если я хочу выбрать все заказы с «кузнецом» в названии, я бы сделал что-то вроде @orders=Order.where('customer.name ilike "%smith%"').joins(:customer)

Как получается, что эти «отношения» работают в первом тайме, но для этого требуется объединение во второй половине?

ответ

1

Вам не нужно присоединяться, однако пока вы не позвоните в свою ассоциацию, ваши данные не будут загружены.

Это качество ActiveRecord::Base, называемое ленивой загрузкой.

Вы можете увидеть это на выходе SQL с консоли.

user = User.find(1) 
User Load (0.2ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1 

Эта особая пользовательская модель имеет over hundread association.

Почему никто не загружается?

Потому что мы еще не назвали их.

user.articles 
Article Load (0.3ms) SELECT `articles`.* FROM `articles` WHERE `articles`.`user_id` = 1 

Теперь мы видим, что запрос выполнен.

В результате это становится проблемой при работе с обычным старым Ruby.

Для примера рассмотрим следующее:

users.each do |user| 
    puts user.articles.first.title 
end 

Запуск следующий код является проблематичным, потому что каждый раз, когда Руби перебирает пользователю, он называет статьи только для этого пользователя.

Вы в конечном итоге запросов для каждого пользователя повторно, который выполняет следующий SQL:

Article Load (0.5ms) SELECT `articles`.* FROM `articles` WHERE `articles`.`user_id` = 1 LIMIT 1 
Article Load (0.5ms) SELECT `articles`.* FROM `articles` WHERE `articles`.`user_id` = 2 LIMIT 1 
Article Load (0.5ms) SELECT `articles`.* FROM `articles` WHERE `articles`.`user_id` = 3 LIMIT 1 
Article Load (0.5ms) SELECT `articles`.* FROM `articles` WHERE `articles`.`user_id` = 4 LIMIT 1 
Article Load (0.5ms) SELECT `articles`.* FROM `articles` WHERE `articles`.`user_id` = 5 LIMIT 1 
Article Load (0.5ms) SELECT `articles`.* FROM `articles` WHERE `articles`.`user_id` = 6 LIMIT 1 
etc. 

Мы можем решить эту проблему путем загрузки всех наших данных, первоначально в рамках одного запроса.

users.joins(:articles).each do |user| 
    puts user.articles.first.title 
end 

который будет выполнять следующий SQL до начала перечисления:

Article Load (0.5ms) SELECT `articles`.* FROM `articles` WHERE `articles`.`user_id` IN(1, 2, 3, 4, 5, 6, etc.) 

Это где ActiveRecord::Base методы, такие как includes и joins вступают в игру.

Вот две хорошие статьи по этому вопросу:

http://blog.arkency.com/2013/12/rails4-preloading/

https://rubyinrails.com/2014/01/08/what-is-lazy-loading-in-rails/

+0

большой описание! Вы действительно помогли устранить пробел для меня. Кроме того, эта первая статья действительно дала мне новую информацию - я никогда не понимал, как смешивать и сопоставлять все отношения. Надеюсь, это поможет мне вернуть мой запрос под контроль, и позвольте мне получить результаты, которые я ищу. :) – Ben

+0

выполняет порядок ленивой загрузки 'select.preload (articles) .last' vs' select.last.preload (articles) '? – Ben

+0

Да, предварительная загрузка может произойти только в области с областью. Так, например: 'User.joins (: articles) .each'. Вы должны загружать непосредственно из области действия, иначе она не удастся, возможно, с неопределенным. – fbelanger

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