blog

紫陽花

Railsで複雑なSQL文を投げたい時に使うArelTable

ActiveRecordについて

Railsには標準でActiveRecordと呼ばれる機能が付いています、ORマッパーとも呼ばれ、modelとデータベーステーブルがリンクしていて、オブジェクト指向でデータを扱うことができる機能です。
詳しい話は以下を参照してください。
railsguides.jp

この機能は非常に便利で直接アプリケーション内にSQL文を書かなくともRubyの記法で書くだけでSQL文に変換しデータを取得することができちゃいます

ActiveRecordさえあればなんでもできちゃうぜ!って思ってしまう人もいるかと思います。
しかしこれにもいいところもあれば、悪いところもあったりします。

そのひとつが、「 複雑なSQL文を投げることに適していない 」ということです。

そもそも複雑なSQL文を投げるような設計にするのがおかしいという声もあるかと思いますが、
そう簡単にいかないのがアプリケーションの難しさだったりします。そもそも難しい検索をしなければ実現できない場合もあります。

では複雑なSQL文を簡潔に書くにはどうしたらいいのでしょうか?

Railsには標準でarelというライブラリが組み込まれています、以下参照。

github.com

arelの機能

基本的な構文

Project.where("name = ? OR name = ?", "テスト1", "テスト2").to_sql

#=> "SELECT `project`.* FROM `projects`  WHERE (name = 'テスト1' OR name = 'テスト2')"

これを、Rubyで書くとこんな感じ

project_table = Project.arel_table

Project.where(project_table[:name].eq("テスト1").or(project_table[:name].eq("テスト2"))).to_sql
#=> "SELECT 'projects' .* FROM 'projects' WHERE(('projects'.'name' = 'テスト1' OR 'projects'.'name' = 'テスト2'))"

上のコードにeqというメソッドが出てきているが、これは'='(イコール)に変換されます。
rspecでもこのような書き方をよく使います。

now_time = DateTime.now
Event.where(Event.arel_table[:start].lt(now_time).and(Event.arel_table[:end].gt(now_time)))

#=> SELECT `events`.* FROM `events`  WHERE (`events`.`start` < '2015-12-14 22:01:20' AND `events`.`end` > '2015-12-14 22:01:20' )

個人的にアプリ開発を行った際にarelを使った際に書いたコード
Eventデータを取得するためにEventのスタートが今の時間(now_time)の範囲内であり、かつエンドは今の時間よりも未来の値であるイベントを取得せよというSQL文になります。


上のコードにltというメソッドが出てきているが、これは'ある値より小さいものにマッチする(<)'に変換されます。
上のコードにgtというメソッドが出てきているが、これは'ある値より大きいものにマッチする(>)'に変換されます。 


こんな感じにSQL文を書き出すことができます。


Tipsとしてまとめられていたりもします。

qiita.com

わかりやすくまとめられているのでぜひ参考にしてみてください。