Rails 3, пользовательский SQL-запрос

У меня есть модель оценки. Оценка имеет много баллов. Всякий раз, когда создается новая оценка, для каждого пользователя, который нуждается в оценке, создается запись о баллах (см. Ниже для текущего метода, который я использую для этого). Так, например, 40 записей за каждый счет могут быть созданы сразу. Затем владелец оценки обновляет каждую запись с помощью оценки пользователя.

Я ищу использовать исходный SQL, потому что каждая вставка - это собственная транзакция и медленная.

Я хотел бы преобразовать следующее в инструкцию массовой вставки с использованием raw SQL:

def build_evaluation_score_items
  self.job.active_employees.each do |employee|
    employee_score = self.scores.build
    employee_score.user_id = employee.id
    employee_score.save
  end
end

Любые мысли о том, как это можно сделать? Я пробовал адаптировать образец кода с сайта Chris Heald Coffee Powered, но без кубиков.

Спасибо всем, кто хочет помочь!

ИЗМЕНИТЬ 1

Я забыл упомянуть, что текущий метод заключен в транзакцию.

Итак, по сути, я пытаюсь добавить это в блок кода, поэтому все вставлено в один оператор (** Этот фрагмент кода находится на сайте Chris Heald Coffee Powered, на котором обсуждалась тема. Я задал бы вопрос, но пост > 3 года.):

inserts = []
TIMES.times do
  inserts.push "(3.0, '2009-01-23 20:21:13', 2, 1)"
end
sql = "INSERT INTO user_node_scores (`score`, `updated_at`, `node_id`, `user_id`)VALUES #{inserts.join(", ")}"

Я был бы рад показать код из некоторых моих попыток, которые не работают...

Еще раз спасибо!

Ну, я собрал что-то похожее на код выше, но я получаю неверную ошибку SQL-запроса вокруг части ('evaluation_id'. Любые мысли?

def build_evaluation_score_items
  inserts = []
  self.job.active_employees.each do |employee|
    inserts.push "(#{self.id}, #{employee.id}, #{Time.now}, #{Time.now})"
  end
  sql = "INSERT INTO scores ('evaluation_id', `user_id`, 'created_at', `updated_at`)VALUES #{inserts.join(", ")}"
  ActiveRecord::Base.connection.execute(sql) 
end

Любая идея относительно того, что в приведенном выше коде SQL вызывает ошибку?

Ответ 1

Хорошо, после долгих проб и ошибок, вот окончательный ответ. Самое приятное, что все записи вставляются через один оператор. Конечно, валидации пропущены (так что это будет нецелесообразно, если вам потребуются проверки модели при создании), но в моем случае это не обязательно, потому что все, что я делаю, это создание записи оценки для каждой оценки сотрудника. Конечно, валидации работают так, как ожидалось, когда руководитель задания обновляет оценочную оценку сотрудника.

def build_evaluation_score_items
  inserts = []
  time = Time.now.to_s(:db)
  self.job.active_employees.each do |employee|
    inserts.push "(#{self.id}, #{employee.id}, '#{time}')"
  end
  sql = "INSERT INTO scores (evaluation_id, user_id, created_at) VALUES #{inserts.join(", ")}"
  ActiveRecord::Base.connection.execute(sql) 
end

Ответ 2

Вместо того, чтобы напрямую строить SQL (и открывать себя для SQL-инъекций и других проблем), я бы рекомендовал animerecord-import gem. Он может вызывать команды нескольких строк INSERT, среди других стратегий.

Затем вы можете написать что-то вроде:

def build_evaluation_score_items
  new_scores = job.active_employees.map do |employee|
    scores.build(:user_id => employee.id)
  end
  Score.import new_scores
end

Ответ 3

Я думаю, что вы ищете:

def build_evaluation_score_items
  ActiveRecord::Base.transaction do
    self.job.active_employees.each do |employee|
      employee_score = self.scores.build
      employee_score.user_id = employee.id
      employee_score.save
    end
  end
end

Все дочерние транзакции автоматически "подталкиваются" до родительской транзакции. Это предотвратит накладные расходы на столько транзакций и должно повысить производительность.

Подробнее об транзакциях ActiveRecord можно прочитать здесь.

UPDATE

Извините, я неправильно понял. Сохранение вышеуказанного ответа для потомков. Попробуйте следующее:

def build_evaluation_score_items
  raw_sql = "INSERT INTO your_table ('user_id', 'something_else') VALUES "
  insert_values = "('%s', '%s'),"
  self.job.active_employees.each do |employee|
    raw_sql += insert_values % employee.id, "something else"
  end
  ActiveRecord::Base.connection.execute raw_sql
end