LoginSignup
31
21

More than 5 years have passed since last update.

railsで大量のデータをinsertもしくはupdateする。

Last updated at Posted at 2018-10-06

railsで大量のデータをすでにあれば更新なければ作成する。

update + insert = upsert というらしいですね。

この操作、生のactive recordでやろうと思うとかなりの数のsql文ができて、かなり効率悪くなります。そこでactiverecord-importの出番です。こちらのgithubのwikiによるとon_duplicate_key_updateというものが対応しているみたいです。

MySQL, PostgreSQL (9.5+), and SQLite (3.24.0+) support on duplicate key update (also known as “upsert”) which allows you to specify fields whose values should be updated if a primary or unique key constraint is violated.

ふむふむ、どうやらプライマリーキー制約やユニークキー制約に引っかかったデータに対して、特定のカラムの更新をしてくれる見たいです。つまり、例えばprimary_keyuser_idUserテーブルがあったとして、

User.create(user_id: 1, name: "hogehoge", sex: 1)
User.create(user_id: 3, name: "fugafuga", sex: 0)
User.create(user_id: 5, name: "piyopiyo", sex: 0)

hashes = [
        {user_id: 1, name: "Harry", sex:0},
        {user_id: 2, name: "Alice", sex:1},
        {user_id: 3, name: "Fred", sex:0},
        {user_id: 4, name: "Bob", sex:0},
        {user_id: 5, name: "Patricia", sex:1}
        ]

users = []
hashes.each do |item|
users << User.new(user_id: item[:user_id], name: item[:name], sex: item[:sex])
end

# MySQLバージョン
User.import users, on_duplicate_key_update: [:name]

# Postgreバージョン
User.import users, on_duplicate_key_update: {conflict_target: [:user_id], columns:  [:name]}

とすると、次のように更新or新規作成される。

# 最初3つのcreateの時点
  User Load (0.7ms)  SELECT `users`.* FROM `users`
+---------+----------+-----+-------------------------+-------------------------+
| user_id | name     | sex | created_at              | updated_at              |
+---------+----------+-----+-------------------------+-------------------------+
| 1       | hogehoge | 1   | 2018-10-06 15:39:51 UTC | 2018-10-06 15:39:51 UTC |
| 3       | fugafuga | 0   | 2018-10-06 15:39:51 UTC | 2018-10-06 15:39:51 UTC |
| 5       | piyopiyo | 0   | 2018-10-06 15:39:51 UTC | 2018-10-06 15:39:51 UTC |
+---------+----------+-----+-------------------------+-------------------------+


# importが終わり更新処理が終わった時点
  User Load (2.6ms)  SELECT `users`.* FROM `users`
+---------+----------+-----+-------------------------+-------------------------+
| user_id | name     | sex | created_at              | updated_at              |
+---------+----------+-----+-------------------------+-------------------------+
| 1       | Harry    | 1   | 2018-10-06 15:39:51 UTC | 2018-10-06 15:40:50 UTC |
| 2       | Alice    | 1   | 2018-10-06 15:40:50 UTC | 2018-10-06 15:40:50 UTC |
| 3       | Fred     | 0   | 2018-10-06 15:39:51 UTC | 2018-10-06 15:40:50 UTC |
| 4       | Bob      | 0   | 2018-10-06 15:40:50 UTC | 2018-10-06 15:40:50 UTC |
| 5       | Patricia | 0   | 2018-10-06 15:39:51 UTC | 2018-10-06 15:40:50 UTC |
+---------+----------+-----+-------------------------+-------------------------+

ちゃんと名前だけが更新されているのがわかる。

これはかなり便利なのでは。

31
21
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
31
21