12
10

More than 3 years have passed since last update.

[Rails]Arelは簡単or難関?使い方大全

Posted at

はじめに

あるプロジェクトの開発中、「Arel」というSQL文的なものが出てきました。
初めて見た僕は、「なにこれSQL文?」と疑問でした。調べてみると、Railsの簡単にSQLを使えるという機能の一つということがわかりました。
そこで、今後も使う可能性がありそうだったので、記事にまとめようと思った次第です。。
備忘録的にまとめてますが、誰かの為になれたらと思います。

Arelとは

Arelとは,「⁠Relational Algebra」または「Active Relation」の略で、
その名前の通り、ActiveRecordから派生した、
「⁠関係代数」をRubyのオブジェクトで取り扱うライブラリのことです。

公式ドキュメントによると、、
1.複雑なSQLクエリを簡単に生成可能
2.色んなRDBMSに対応可能
3.フレームワーク(特にORM)のフレームワークとして開発された

つまり、Arelを使うことで、DBの互換性やSQL文字列の生成などに時間を取られることなく、
大事な設計やモデリングに注力してO/Rマッピング処理を実装することができるようになっています。

基本的な操作方法一覧

arel_table

モデル名.arel_tableとすることで指定したモデルのテーブルを検索する準備が出来る。
オプションとして、カラム名を追加したり、条件を追加して検索できる。


Staff.where( Staff.arel_table[:age] )

Arel.sql(〇〇〇〇)

Arel.sql(〇〇〇〇)とすることでSQL文を生成できる。
※〇〇にはSQL文を入れる。


Arel.sql('〇〇〇〇')

Arel::Nodes

Arel::Nodes#build_quoted

文字列などの値をラップして、Arel::Nodes::NamedFunctionなどの引数に渡せるようにします。


Arel::Nodes.build_quoted(" ")

Arel::Nodes::NamedFunction

任意のSQL関数の呼び出しを表現するために使う。
第1引数に関数名、第2引数に関数に渡す引数(配列)、第3引数にエイリアス名(任意)を渡す。


staffs_table = Staff.arel_table
date_format = Arel::Nodes.build_quoted("%Y-%m-%d")

Staff.select(
  Arel::Nodes::NamedFunction.new(
    "DATE_FORMAT", # 日時を指定のフォーマットに整形してくれる関数
    [staffs_table[:created_at], date_format], # created_atを指定したフォーマットに変換
    "registration_date"
  )
)

Arel::Nodes::SqlLiteral

生のSQL文字列を生成出来る。
SQLの構文中に必要な文字列をラップするのに使われる。

  • utf8_general_ci
    • UTF-8文字コードで半角や全角、濁点を区別し、英語の大文字小文字を区別しない指定。

Arel::Nodes::SqlLiteral.new("utf8_general_ci")

Arel::Nodes::InfixOperation

「infix operation」は二項演算という意。
2つの値・変数の計算を行うことができます。
第1引数に演算子、第2引数に演算子の左側の値、第3引数に演算子の右側の値が入る。


Staff.where(
  Arel::Nodes::InfixOperation.new(
    "COLLATE", # 検索条件で照合順序を指定
    Staff.arel_table[:name], # 演算子の左側が、staffsテーブルのnameカラム
    Arel::Nodes::SqlLiteral.new("utf8_general_ci") # 演算子の右側が、utf8_genera_ci
  )
  .matches(Arel::Nodes.build_quoted("さとう%")) # nameが「さとう」から始まるStaffを取得
)

Arel::Nodes::OuterJoin

外部結合(OUTER JOIN)する際に使用する。
JOIN句の第2引数に Arel::Nodes::OuterJoin を指定し、
最後にjoin_sources を呼び出すことで結合できる。


comments_table = Comment.arel_table
staffs_table = Staff.arel_table

Comment.joins(
  comments_table
    .join(staffs_table, Arel::Nodes::OuterJoin)
    .on(
      comments_table[:staff_id].eq(staffs_table[:id])
      .and(staffs_table[:is_active].eq(true))
    )
    .join_sources
)

※commentsテーブルのstaff_idとStaffテーブルのidが等しい、
かつstaffsテーブルの is_activeがtrueのものという条件。

Arel::Nodes::Descending, Arel::Nodes::Ascending

Arel::Nodes::Descendingは ORDER 句の DESC (降順)の部分を担当。
Arel::Nodes::Ascendingは ASC (昇順)です。引数には、ORDER BY の右側の値が入ります。


Staff.order(
  Arel::Nodes::Descending.new(
    Staff.arel_table[:staff_type]) # staff_typeの降順
  )
)

Arel::Nodes::Case

SQL の CASE 構文を表現することができます。

  • gt
    • 左項の値が右項の値より大きいときに真になる。(「>」と同意)

staffs_table = Staff.arel_table
new_cond = staffs_table[:created_at].gt(Time.zone.now - 3.days)

Staff.order(
  Arel::Nodes::Ascending.new(
    Arel::Nodes::Case.new
      .when(new_cond).then(1)
      .else(9)
  )
)

Staffが作成されて3日以内であれば 1、そうでなければ 9 という値とする。
3日以内に作成されたStaffを昇順で並び替えている。

Arel::Nodes::Grouping

括弧()で囲む。四則計算等で括弧を使いたい場合に使う。


Arel::Nodes::Grouping( object )

Arel::Nodes::As

AS による別名定義の文 〇 AS × を作る。


Staff.select( Staff.arel_table[:id].as('no') ) 

Tips集

使えるTipsをご紹介。

IS NOT NULL


Staff.where(
  Staff.arel_table[:age].not_eq(20)
)

AS


Staff.where(
  Staff.arel_table[:name].as('admin')
)

admin = Staff.arel_table.alias('admin')
Comment.joins(admin).select(admin[:name])

サブクエリ

FROM句へのサブクエリ


new_table = Arel::Table.new(nil)
sql = new_table.from(
    Staff.where(created_at: start_at...end_at).to_sql
  )
  .project(Arel.sql('*'))
  .to_sql

JOIN句へのサブクエリ


sub_query = Staff.where(created_at: start_at...end_at).to_sql
Comment.joins("INNER JOIN (#{sub_query})")

SUM


Staff.where(created_at: start_at...end_at)
  .select(Staff.arel_table[:name].sum().as('admin'))

Where


staffs = Staff.arel_table
staffs.arel_table.project(Arel.sql('*')).where(staffs[:id].eq(1)).to_sql

order by


staffs = Staff.arel_table
staffs.project(Arel.sql('*')).order(staffs[:id].asc).to_sql

集計関数とgroup by


staffs = Staff.arel_table
staffs.project(staffs[:id].count, staffs[:name]).group(staffs[:age]).to_sql

COUNTで利用するDISTINCT


Staff.select(Staff.arel_table[:id].count())
  .select(Staff.arel_table[:id].count('distinct'))

終わりに

Arelでは色んな使い方があるのだなと調べて驚きました。
使いこなすには実践あるのみ!ですね。。
しかし、使うことに難を示す人たちもいるようで、賛否両論ではありますが、
チーム開発において、誰かが使って自分が理解出来ない。。ってことが起きると開発にブレーキをかけてしまうので、一応理解しているだけ得だと思いました!

参考

Arel::Nodes を使って Arel で複雑な SQL文を作っちゃおう

Arel::Nodesを最低限読めるようになりたい

RailsのArelのTips

RailsのArelを調査してみた

Arelで色んなSQLを組み立ててみる

ActiveRecordのarel_tableから作れる条件式まとめ

RailsでArel、早見表

12
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
10