SQL-паттерн в Rails
июня 30, 2011 | Published in Ruby on Rails, Ruby on Rails 3, Базы данных | 1 Comment
ActiveRecord — просто замечательный инструмент для простых запросов. Тем не менее иногда во имя производительности приходится отказаться от всех преимуществ предоставляемых ActiveRecord и снизойти до использования SQL.
Рассмотрим пример. У вас имеются пользователи и проекты. Пользователи могут принадлежать многим проектам через объединенную таблицу memberships. Какой наш виигрыш? Все пользователи, которые не связаны с проектами. Схема таблицы:
create_table :users, :force => true do |t| end create_table :memberships, :force => true do |t| t.integer :user_id t.integer :project_id end create_table :project, :force => true do |t| end
Если вы не хотите использовать SQl, то вам, должно быть, будет удобно использовать что-то вроде этого (написано с головы, клянусь, это пришло ко мне через 5 секунд):
User.all(:include => :memberships).select {|u| !u.memberships.map(&:project).include?(project_id)}
ой, что я здесь натворил! В любом более-менее серьезном проекте мой маленький код создаст множество ActiveRecord объектов чем сьест большой кусок оперативной памяти. имея достаточно большое количество объектов и маленький сервер приложения, мы можем начать использовать жасткий диск в качестве виртуальной памяти!
Хорошо, хорошо, это был плохой пример. Предположим, что нет подкачки из виртуальной памяти, сборщик мусора в Ruby остается дорогим удовольствием!
Зачем создавать объекты, чтобы потом игнорировать их? Это большое расточительство времени выполнения и ресурсов! Давайта снизойдем до непосредственного взаимодействия с базой данных прри помощи SQL.
(Я не часто использую SQL в Rails, однако когда я делаю это, то использую скоупы.)
Ниже приведен пример скоупа, который передает нам множество пользователей которые не являются членами определенного проекта:
class User < ActiveRecord::Base ... scope :not_a_member_of, lambda { |project_id| sanitized_join_condition = sanitize_sql_array( ["LEFT OUTER JOIN 'memberships' ON 'memberships'.'user_id' = #{self.quoted_table_name}.'id' AND 'memberships'.'project_id' = ?", project_id] ) joins(sanitized_join_condition). where("'memberships'.'project_id' IS NULL") } ... end
Давайте разберем пример более подробно:
Помните, мы используем LEFT OUTER JOIN потому, что мы хотим получить список всех пользователей независимо от того имеют ли они запись в таблице связей или нет.
Строка с where занимается фильтрацией всех пользователей с данным project_id оставляя нам всех пользователей, которые не участвуют в проектах.
Оригинал статьи на английском:
июля 20, 2011 at 01:14 (#)
Почитайте пожалуйста про AREL.
Не учите использовать SQL когда можно обойтись без него