【Rails】ActiveRecordのSQLを確認する方法

ActiveRecordの裏側でどのようなSQLが発行されるのかを確認する方法がわからなかったので、備忘録としてまとめます。

バージョン

  • Ruby 3.0.3
  • Rails 6.1.7.7

記事の信頼性

  • ぼくは独学で未経験から従業員300名以上の自社開発企業へ転職しました。
  • 実務ではVue.jsとRailsを毎日書いています。
  • 初心者や駆け出しエンジニアがつまづくポイントも身をもってよく理解しています。
目次

問題

仕事でスロークエリ改善のタスクを任されていたため、ActiveRecordの裏でどのようなSQLが発行されているかを調べたかったです。

しかしどのようにやれば良いのかが分かりませんでした、、。

もちろんログを愚直に追えば確認はできるんでしょうが、それだと効率が悪いので、「何か便利なメソッドがないかな」と探していました。

解決方法

RailsのActiveRecord::Relationクラスにはto_sqlというメソッドが存在しています。

これを用いることで、ActiveRecordの裏側で発行されるSQLを個別に確認することが可能です。

たとえば、以下の記述でどのようなSQLが発行されるか、知りたかったとします。

# task_ids はTaskモデルのIDの配列
Task.where(id: task_ids).eager_load(:category, :user)

その場合、Railsコンソール上で次のように書いて実行してみれば、発行されるSQLを確認できます。

# task_idsの位置には適当な配列(1..5)を入れています
[1] pry(main)> Task.where(id: (1..5).to_a).eager_load(:category, :user).to_sql
"SELECT `tasks`.`id` AS t0_r0, `tasks`.`category_id` AS t0_r1, `tasks`.`user_id` AS t0_r2, `tasks`.`title` AS t0_r3, `tasks`.`description` AS t0_r4, `tasks`.`deadline` AS t0_5, `tasks`.`status` AS t0_r6, `tasks`.`created_at` AS t0_r7, `tasks`.`updated_at` AS t0_r8, `task_categories`.`id` AS t1_r0, `task_categories`.`name` AS t1_r1, `task_categories`.`created_at` AS t1_r2, `task_categories`.`updated_at` AS t1_r3, `users`.`id` AS t2_r0, `users`.`company_id` AS t2_r1, `users`.`role` AS t2_r2, users`.`status` AS t2_r3, `users`.`created_at` AS t2_r4, `users`.`updated_at` AS t2_r5,  FROM `tasks` LEFT OUTER JOIN `users` ON `users`.`id` = `tasks`.`user_id` LEFT OUTER JOIN `task_categories` ON `task_categories`.`id` = `tasks`.`category_id` WHERE `tasks`.`id` IN (1, 2, 3, 4, 5)"

`tasks```が見づらく感じる場合は、gsubコマンドを併用すれば消せます。

# gsub コマンドで ` を空文字に置き換える
[2] pry(main)> Task.where(id: (1..5).to_a).eager_load(:category, :user).to_sql.gsub('`', '')
=> "SELECT tasks.id AS t0_r0, tasks.category_id AS t0_r1, tasks.user_id AS t0_r2, tasks.title AS t0_r3, tasks.description AS t0_r4, tasks.deadline AS t0_5, tasks.status AS t0_r6, tasks.created_at AS t0_r7, tasks.updated_at AS t0_r8, task_categories.id AS t1_r0, task_categories.name AS t1_r1, task_categories.created_at AS t1_r2, task_categories.updated_at AS t1_r3, users.id AS t2_r0, users.company_id AS t2_r1, users.role AS t2_r2, users.status AS t2_r3, users.created_at AS t2_r4, users.updated_at AS t2_r5,  FROM tasks LEFT OUTER JOIN users ON users.id = tasks.user_id LEFT OUTER JOIN task_categories ON task_categories.id = tasks.category_id WHERE tasks.id IN (1, 2, 3, 4, 5)"

注意点

このto_sqlメソッドはとても便利なんですが、ActiveRecordのオブジェクトなら何にでも使える訳ではありません。

例として、次の実行結果を見てください。

[1] pry(main)> User.all.to_sql
=> "SELECT `users`.* FROM `users`"
[2] pry(main)> User.last.to_sql
  User Load (4.4ms)  SELECT `users`.* FROM `users` ORDER BY `users`.`id` DESC LIMIT 1
NoMethodError: undefined method `to_sql' for #<User::IndividualUser id: 59, created_at: "2024-04-04 11:54:26.000000000 +0900", updated_at: "2024-04-04 11:54:26.000000000 +0900", role: "employee", identity_verified_flag: false, postpayment_flag: 0, registered_flag: false, nda_flag: false, chat_status: "", config: "", partnership_agreement_agreed_flag: false, company_id: nil, _note: "", user_auth_id: 59, inactivated_flag: false, resigned_at: nil, type: "User::IndividualUser", corporation_id: nil>
Did you mean?  to_s
from /usr/local/bundle/gems/activemodel-6.1.7.7/lib/active_model/attribute_methods.rb:469:in `method_missing'

User.allに対してはto_sqlメソッドの実行が成功しているのに対し、User.lastに対しては失敗しています(この場合でも発行されるSQLは見えますが)

これはUser.allが返すオブジェクトのクラスとUser.lastが返すオブジェクトのクラスが異なることが原因です。

[3] pry(main)> User.all.class.name
=> "ActiveRecord::Relation"
[4] pry(main)> User.last.class.name
  User Load (8.3ms)  SELECT `users`.* FROM `users` ORDER BY `users`.`id` DESC LIMIT 1
=> "User"

User.allActiveRecord::Relationクラスのオブジェクトを返す一方で、User.lastUserクラスのオブジェクトを返します。

to_sqlメソッドはActiveRecord::Relationクラスに定義されているため、User.lastに使っても失敗してしまうんです。

この点に注意しておきましょう。

おわりに

この記事ではActiveRecordの裏側で発行されるSQLの調べ方を解説しました。

パフォーマンス改善のタスクをしているときにto_sqlメソッドはとても役立ちます。しっかりマスターしておきましょう。

また、以下の記事ではワンランク上のRailsエンジニアになりたいと考えている方向けにおすすめの技術書を紹介しています。

こちらの記事もぜひ読んでみてください。

あわせて読みたい
【実務経験1年以上向け】Rubyで設計を学べる技術書3選 Rubyで設計を学べる技術書を知りたくないですか?この記事ではRubyエンジニア向けにサンプルコードがRubyで書かれていて設計を学べる技術書を3つ紹介しています。自分に必要なのはどの1冊なのか、ぜひ考えてみてください。
参考文献
よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

未経験でSESから従業員300名以上の自社開発企業に転職しました。業務や個人開発で直面した問題や、転職・学習の経験を発信していきます。

コメント

コメントする

目次