こんにちは
ADA事業部の社内システムチームの高橋です。
最近xxx to earnが気になってしょうがないです。
最近Railsの開発してた時に
チームリーダーより勧められた
insert_allというメソッドについて今日はご紹介したいと思います。
結論から言うと
勧められたのですが使うのを断念しました。
とても便利かつパフォーマンスも上がる?し
素晴らしいメソッドだなと思っているのですが
使えないケースが存在するのでそちらもご紹介したいと思います。
insert_allとは
簡単に言うと一括インサート、またの名をバルクインサート!
を行なってくれるメソッドです。
公式ドキュメントは下記となります。
下記のようなUserモデルがあった時
# == Schema Information # # Table name: users # # id :integer not null, primary key # name :string not null # email :string not null # created_at :datetime not null # updated_at :datetime not null # class User < ApplicationRecord end
user_attributes = (1..100).map { { name: "name", email: "hoge@hoge.com", created_at: Time.current, updated_at: Time.current } } User.insert_all user_attributes
こんな感じで記述すると
複数のSQL文を発行せず一つのSQL文で
複数件のレコード登録が行えるようになります。
createメソッドとのSQL文の違いは下記
# createメソッド User.create(name: "name", email: "hoge@hoge.com") # SQL文は下記 DEBUG: User Create (0.7ms) INSERT INTO `users` (`id`, `name`, `email`, `created_at`, `updated_at`) VALUES (1, 'name', 'hoge@hoge.com', '2022-04-18 08:28:08.003019', '2022-04-18 08:28:08.003019') # insert_allメソッド、先ほどのコードでセットしたuser_attributesを渡す User.insert_all user_attributes # SQL文は下記 DEBUG: User Bulk Insert (3.4ms) INSERT INTO `users` (`name`,`email`,`created_at`,`updated_at`) VALUES ('name', 'hoge@hoge.com', '2022-04-18 08:36:35.875333', '2022-04-18 08:36:35.875372'), ('name', 'hoge@hoge.com', '2022-04-18 08:36:35.875385', '2022-04-18 08:36:35.875398').....
スゲー!めっちゃ楽!!
バージョンによってはタイムスタンプが自動更新されません。
私の開発しているシステムでは更新するオプションが存在しませんでしたので
こういったサンプルを紹介しています。
オプションを使用したい方は下記を参照
これなら複数件のレコード登録は
脳死でコレで決まりだね!と思ったアナタ。いや私。
抑えておくべき点が調べてたらあったんですね。。
抑えておきたい点
使用する前に、いくつかの抑えておかなきゃいけない点が二つあります。
insert_allを使うかどうかを考える上で重要な視点なので
まずは確認した方が良いです。
バリデーションを行わない
まず一つ目はですね、バリデーションが行われないです。
モデルに下記のように記述することで
アプリケーション側でレコードの各カラムなどのバリデーションをおこなっていると思うのですが
insert_allではこれらが全て行われません。
コールバックアクションも同様です。
class User < ApplicationRecord validates :name, presence: true, length: { maximum: 50 } validates :email, presence: true, length: { maximum: 255 } before_save { self.email = email.downcase } end
なのでinsert_allを使用する際は
これらバリデーションを行わなくても問題がないケースであるかどうかを
見極める必要があります。
createしたオブジェクト情報が返ってこない
もう一つはですね、登録されたオブジェクト情報が返ってこないんです。
例えば下記のようにcreateメソッドを使用した時
登録されたオブジェクト情報が返ってくると思います。
User.create(name: "name", email: "hoge@hoge.com") # => #<User id: 1, name: "name", email: "hoge@hoge.com",...>
でAPIによっては結果をこのまま返したいじゃないですか。
そんな要望がですね、叶える事ができない。。
しかもPostgreSQLしか叶える事ができない。。
辛い。。MySQLがデファクトスタンダードじゃねーの。。。
(そんな事ないですね、すいません)
ちなみに下記のように記述すれば
PostgreSQLなら返したいカラム情報も指定することが出来ます。
users = (1..100).map { { name: "name", email: "hoge@hoge.com", created_at: Time.current, updated_at: Time.current } } User.insert_all(users, returning: %i[id name]) # idとnameのみ返す
なので登録したオブジェクト情報を使用したい場合は
PostgreSQL以外では辞めた方が良いかもしれません。
まとめ
- 検証を行わない
- 登録されたオブジェクト情報を返さない(PostgreSQL以外)
上記でも問題ない場合に使ってみる事をお勧めします。