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

これをどのように行うことができるかについて何か考えはありますか?ChrisHealdのCoffeePoweredサイトのコードサンプルを適応させてみましたが、サイコロはありません。

喜んで手伝ってくれる人に感謝します!

編集1

現在のメソッドがトランザクションにラップされていることについては言及しませんでした。

したがって、基本的に、これをコードブロックに追加して、すべてが1つのステートメントに挿入されるようにします(**このコードスニペットは、トピックについて説明したChrisHealdのCoffeePoweredサイトからのものです。そこで質問しますが、投稿は>です。 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

さて、試行錯誤の末、最終的な答えがこれです。優れた点は、すべてのレコードが 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
于 2012-10-03T23:41:27.283 に答える
7

SQL を直接構築する (そして SQL インジェクションやその他の問題に身を置く) よりも、activerecord-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
于 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 に答える