10

我有一个评估模型。评价有很多分数。每当创建新的评估时,都会为需要评估的每个用户创建一个分数记录(请参阅下面我正在使用的当前方法。)因此,例如,可能一次创建 40 个分数记录。然后,评估所有者使用用户的分数更新每个分数记录。

我希望使用原始 SQL,因为每个插入都是它自己的事务并且速度很慢。

我想使用原始 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(", ")}"

我很乐意展示我的一些无效尝试中的代码......

再次感谢!

好吧,我拼凑了一些类似于上面代码的东西,但是在 ('evaluation_id' 部分周围出现 SQL 语句无效错误。有什么想法吗?

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 代码中的什么导致错误的任何想法?

4

3 回答 3

20

好吧,经过多次试验和错误,这是最终的答案。很酷的是所有记录都是通过一个语句插入的。当然,会跳过验证(因此,如果您需要在创建时进行模型验证,这将不合适)但在我的情况下,这不是必需的,因为我所做的只是为每个员工的评估设置分数记录。当然,当工作负责人更新员工的评估分数时,验证会按预期工作。

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
于 2012-10-03T23:41:27.283 回答
7

我推荐使用activerecord-import gem ,而不是直接构建 SQL(并让自己面临 SQL 注入和其他问题) 。它可以发出多行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
于 2012-10-04T02:50:40.817 回答
5

我认为您正在寻找的是:

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 事务的更多信息。

更新

对不起,我误会了。为后代保留上述答案。试试这个:

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
于 2012-10-03T21:01:22.120 回答