はじめに
Ruby on RailsでDBの該当するデータを全て更新したい。初心者なりにやり方をまとめてみます。
今回サンプルで用意したDBはこちら。
User
id| name |age|hobby |created_at |updated_at
1|Mary |22 |cooking |2019-06-18 00:31:31.224467|2019-06-18 00:31:31.224467
2|John |25 |soccer |2019-06-18 00:35:03.354101|2019-06-21 09:20:15.644253
3|Alice |25 |swimming|2019-06-18 00:38:13.875116|2019-06-18 00:38:13.875116
4|Tom |28 |baseball|2019-06-21 08:39:12.363736|2019-06-21 08:39:12.363736
5|Nina |20 |soccer |2019-06-21 08:39:24.677422|2019-06-21 08:39:24.677422
6|Mark |21 |travel |2019-06-21 08:39:39.254744|2019-06-21 08:39:39.254744
最終的に25歳のUserの趣味を'game'に更新しようと思います。
まずは対象のデータを取得する -find_byかwhereか-
ここでまず気になったのが、find_by
とwhere
、どちらを使ったら良いのだろうか。。。
試しにfind_by
を。
User.find_by(age:25)
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."age" = ? LIMIT ? [["age", 25], ["LIMIT", 1]]
=> #<User id: 2, name: "John", age: 25, hobby: "soccer", created_at: "2019-06-18 00:35:03", updated_at: "2019-06-21 09:47:12">
ん〜、Johnのデータしか取得されていません。。。
Railsドキュメントによると、find_byは検索条件を指定して、最初の1件を取得する
とのこと。(http://railsdoc.com/references/find_by)
なるほど。
では、where
で試してみます。
User.where(age:25)
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."age" = ? LIMIT ? [["age", 25], ["LIMIT", 11]]
=> #<ActiveRecord::Relation [#<User id: 2, name: "John", age: 25, hobby: "soccer", created_at: "2019-06-18 00:35:03", updated_at: "2019-06-21 09:47:12">,
#<User id: 3, name: "Alice", age: 25, hobby: "swimming", created_at: "2019-06-18 00:38:13", updated_at: "2019-06-21 09:47:48">]>
無事にJohnとAliceのデータを取得できました。where
を使うと該当データを全て取得できるようです!
find_by
とは異なり、ActiveRecord::Relation
の文字が。こちらは追って詳しく調べたいと思います。
データの更新
こちらはupdate
を使えば上手くいきそうな予感がします。
User.where(age:25).update(hobby:'game')
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."age" = ? [["age", 25]]
(0.1ms) begin transaction
User Update (0.9ms) UPDATE "users" SET "hobby" = ?, "updated_at" = ? WHERE "users"."id" = ? [["hobby", "game"], ["updated_at", "2019-06-21 10:10:54.641391"], ["id", 2]]
(0.8ms) commit transaction
(0.0ms) begin transaction
User Update (0.3ms) UPDATE "users" SET "hobby" = ?, "updated_at" = ? WHERE "users"."id" = ? [["hobby", "game"], ["updated_at", "2019-06-21 10:10:54.644219"], ["id", 3]]
(0.5ms) commit transaction
=> [#<User id: 2, name: "John", age: 25, hobby: "game", created_at: "2019-06-18 00:35:03", updated_at: "2019-06-21 10:10:54">,
#<User id: 3, name: "Alice", age: 25, hobby: "game", created_at: "2019-06-18 00:38:13", updated_at: "2019-06-21 10:10:54">]
更新できました! しかし、これだと一つずつデータを見ていっているので、データ数が多くなると処理が重たくなりそうです…。
もっとガバっと処理できる方法は無いのでしょうか……
Railsドキュメントで調べてみると、update_all
というのを発見!
こちらは条件に一致するレコードをすべて更新
するそうです。(http://railsdoc.com/references/update)
早速試してみました(DBは元の状態に戻します)。
User.where(age:25).update_all(hobby:'game')
User Update All (2.7ms) UPDATE "users" SET "hobby" = 'game' WHERE "users"."age" = ? [["age", 25]]
=> 2
更新した件数だけ表示されているようです。
DBを確認してみます。
User
id| name |age|hobby |created_at |updated_at
1 |Mary |22 |cooking |2019-06-18 00:31:31.224467|2019-06-18 00:31:31.224467
2 |John |25 |game |2019-06-18 00:35:03.354101|2019-06-21 10:14:38.844045
3 |Alice |25 |game |2019-06-18 00:38:13.875116|2019-06-21 10:15:00.038129
4 |Tom |28 |baseball|2019-06-21 08:39:12.363736|2019-06-21 08:39:12.363736
5 |Nina |20 |soccer |2019-06-21 08:39:24.677422|2019-06-21 08:39:24.677422
6 |Mark |21 |travel |2019-06-21 08:39:39.254744|2019-06-21 08:39:39.254744
where
とupdate_all
の組み合わせで、無事に25歳のUserの趣味を'game'に更新できました!
まとめ
・該当するデータを全て取得したい場合は、where
を使う
・データを一気に更新したい場合は、update_all
を使う
※update_all
はバリデーションが行われないので、バリデーションによる検証が必要であればupdate
を使ったほうが良さそうです。(http://railsdoc.com/references/update)
初心者ですので、間違っている点等あればご指摘いただけますと幸いです。