Railsの技: to_sqlでActive Recordが生成するクエリを調べる(翻訳)
joins
や複雑なwhere
句を含んでいたり、複数テーブル間で特定の値をSELECTするような込み入ったActive Recordクエリを書こうとしているときに、Active RecordのDSLをすべて思い出すのは難しいでしょう。
joins(:orders)
で書くべきか、それともjoins(:order)
か?where(role: { name: 'Manager' })
と書くべきか、それともwhere(roles: { name: 'Manager' })
と書くべきか、といった具合です。
Railsコンソール上でこうしたクエリのテストを繰り返すのはよい考えですが、コードを実行するとたまに以下のような奇妙な結果が返って頭を抱えることもあります。
#<MyModel::ActiveRecord_Relation:0x23118>
この結果にアクセスしようとすると、以下のような謎エラーが発生してクエリが吹き飛んでしまうこともあります。
ActiveRecord::StatementInvalid: PG::UndefinedTable: ERROR: missing FROM-clause entry for table "permission"
LINE 1: ...."id" = "permissions_users"."permission_id" WHERE "permissio...
使い方
問題点をデバッグするために、生成されたSQLを検査したくなることがあります。
実際にはActive Recordの機能を使えばとても簡単です。クエリでto_sql
メソッドを呼び出せば、SQLを実行する代わりに完全なクエリを出力してくれます(SQLが無効な場合にも出力されます)。
User.where("email LIKE '%?'", "gmail.com").to_sql
#=> "SELECT \"users\".* FROM \"users\" WHERE (email LIKE '%'gmail.com'')"
なるほど、%?
構文で失敗していることがわかりました。
User.where("email LIKE ?", "%gmail.com").to_sql
#=> "SELECT \"users\".* FROM \"users\" WHERE (email LIKE '%gmail.com')"
この方法は、特にデータベースで複数のテーブルを扱っている場合に有用です。
User.joins(:permissions).where(permission: { key: :edit_posts }).to_sql
#=> "SELECT \"users\".* FROM \"users\" INNER JOIN \"permissions_users\" ON \"permissions_users\".\"user_id\" = \"users\".\"id\" INNER JOIN \"permissions\" ON \"permissions\".\"id\" = \"permissions_users\".\"permission_id\" WHERE \"permission\".\"key\" = 'edit_posts'"
今度はwhere
に以下のように複数形のpermissions
を書く必要があることがわかりました。
User.joins(:permissions).where(permissions: { key: :edit_posts })
この技のおかげで、複雑なクエリのデバッグに要する時間を大幅に節約できました。また、期待どおりのSQLクエリをRailsが生成しているかどうかを確認するときにも、この技を使って複雑なクエリを検証するようになりました。
参考情報
- Rails API:
ActiveRecord::Relation#to_sql
概要
原著者の許諾を得て翻訳・公開いたします。