Более эффективный поиск или создание нескольких записей в Rails - PullRequest
4 голосов
/ 02 марта 2011

У меня есть приложение, которое должно отправлять приглашения пользователя на мероприятия.Когда пользователь приглашает друга (пользователя) на событие, создается новая запись, связывающая пользователя с событием, если она еще не существует.Мои модели состоят из user, event и events_user.

class Event
    def invite(user_id, *args)
        user_id.each do |u|
            e = EventsUser.find_or_create_by_event_id_and_user_id(self.id, u)
            e.save!
        end
    end
end

Использование

Event.first.invite([1,2,3])

Я не думаю, что вышеизложенное является наиболее эффективным способом выполнения моей задачи.Я предполагал метод, подобный

 Model.find_or_create_all_by_event_id_and_user_id

, но один не существует.

Модели без проверок

class User 
  has_many :events_users 
  has_many :events 
end 
class EventsUser 
  belongs_to :events 
  belongs_to :users 
end 
class Event 
  has_many :events_users 
  has_many :users, :through => :events_users 
end

Ответы [ 3 ]

7 голосов
/ 05 июля 2013

Может быть быстрее сначала извлечь все существующие записи, а затем создать все отсутствующие записи:

class Event
  def invite(user_ids, *args)
    existing_user_ids = event_users.where(user_id: user_ids).map(&:user_id)
    (user_ids - existing_user_ids).each do |u|
      event_users.create(user_id: u)
     end
  end
end

Таким образом, вы делаете только 1 запрос, если все event_users уже существуют. Однако, если event_users не существует, этот метод выполняет дополнительный запрос - по сравнению с количеством запросов, необходимых для каждого отдельного создания EventUser.

1 голос
/ 05 мая 2011

Если у вас нет проверок или обратных вызовов в модели соединения, самый быстрый метод - это raw sql:

<code>
self.connection.execute(%Q{insert into events_users (event_id,user_id) 
                             select distinct events.id,users.id from events,users 
                             where events.id = #{self.id} 
                             and users.id in ( #{user_ids.join(",")} )
                           }
                        )

Вы можете написать запрос для устранениясуществующие записи из user_ids перед выполнением вышеуказанного вызова.У меня был предыдущий вопрос об этом , в частности о таблицах объединения, у которых нет модели.

Поскольку у вас есть модель соединения, вы можете использовать ar-importдрагоценный камень:

books = []
10.times do |i| 
  books << Book.new(:name => "book #{i}")
end
Book.import books
1 голос
/ 02 марта 2011

Что вы подразумеваете под наиболее эффективным? Я предполагаю, что под эффективным вы подразумеваете производительный, а не элегантный, СУХОЙ, обслуживаемый код и т. Д.

С точки зрения БД, если вы хотите вставить в БД 100 записей, это будет переводить в 100 "INSERT INTO events_models VALUES (x, x)" sql запросов (и, возможно, 100 "SELECT COUNT (*) .." запросы, если у вас также есть проверка уникальности). Таким образом, даже если нужный метод будет реализован в AR, он все равно будет иметь цикл в массивах атрибутов с сохранением каждой пары event_id, user_id).

С точки зрения Ruby / Rails, если вы хотите иметь валидации / обратные вызовы / и т. Д. Для вашей модели, то вы должны создать экземпляр ActiveRecord один за другим в цикле. Теперь, если вы хотите супероптимизировать свой метод (чтобы исключить создание экземпляра класса ActiveRecord), вы можете вручную писать SQL-запросы (следовательно, экономить время и память). Однако выигрыш минимален по сравнению с рисками.

Btw, e.save! необязательно, так как:

Такой же стиль динамического поиска может быть используется для создания объекта, если он не существует Эта динамика искатель называется с find_or_create_by_ и вернет объект, если он уже существует и в противном случае создает, а затем возвращает.

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