Tech Racho エンジニアの「?」を「!」に。
  • Ruby / Rails関連

Railsの技: to_sqlでActive Recordが生成するクエリを調べる(翻訳)

概要

原著者の許諾を得て翻訳・公開いたします。

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の技: 特定スコープ内でuniquenessバリデーションをかける(翻訳)


CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。