🔥

orderメソッドの使い方やパフォーマンスについて

2023/05/03に公開1

orderメソッドとは?

カラムの値を昇順・降順に並び替えることができるメソッドです。(基本的な使い方は下記参照。)

https://railsguides.jp/active_record_querying.html#並び順

 
いくつかorderメソッドの応用した使い方やorderメソッドを使用した時のパフォーマンスについて紹介します。
 
orderメソッドに引数を複数渡すことで、先に渡した引数を優先的に並び替えるようにすることができます。

また、あるカラムを降順(DESC)にし、もう一方のカラムを昇順(ASC)にすることもできます。下記が使用例です。

 Book.order(created_at: :desc, price: :asc)

上記のコードは、Bookモデルのレコードを、まずcreated_atで降順に並べ替え、次にpriceで昇順に並べ替えて返すようにしています。

 
 
orderメソッドは、モデル上でもorderメソッドを使用することができます。

モデル上でアソシエーション先のカラムを並び替えるには、アソシエーション先に対してスコープを使用し、アソシエーション先のカラム順に並び替えることもできます。(下記の記事で「スコープブロック」でページ内のキーワード検索すると関連情報が出てきます。)

https://railsguides.jp/association_basics.html

使用例は下記のような感じです。

class Author < ApplicationRecord
  has_many :books, -> { order "date_confirmed DESC" }
end

上記のコードの -> { order "date_confirmed DESC" } は、 has_many メソッドに渡される引数で、取得したAuthorレコードに対応するBookレコードをdate_confirmedカラムの値で降順に並び替えています。

このように、has_manyメソッドでアソシエーションを定義することで、関連するモデルの情報を容易に取得することができ、-> { order "date_confirmed DESC" } のような指示を追加することで、取得する情報の並び順を設定することができます。

 
 
コントローラ上でアソシエーション先のカラム順に並び替えるには、上記のモデルに書いたときと同じようにorderの引数にアソシエーション先のカラムを記述することでアソシエーション先のカラムで並び替えを行うことができます。

少し使用例を紹介します。

例えば、UserモデルがPostモデルとhas_manyのアソシエーションを持っている場合、Postモデルのcreated_atカラムで降順に並び替えたい場合は、以下のように記述します。

@user.posts.order(created_at: :desc)

また、UserモデルがPostモデルとhas_manyのアソシエーションを持っており、PostモデルがCommentモデルとhas_manyのアソシエーションを持っている場合、Commentモデルのcreated_atカラムで昇順に並び替えたい場合は、以下のように記述します。

@user.posts.joins(:comments).order("comments.created_at ASC")

上記の例では、joinsメソッドを使用して、commentsテーブルとINNER JOIN (内部結合のことで、テーブル同士を結合するためのSQL。)を行っています。その後、orderメソッドでcommentsテーブルのcreated_atカラムを昇順に並び替えています。

ちなみに、orderの引数に comments を記述している理由は、joinsメソッドが結合先のテーブル(今回の場合commentsテーブルのこと。)に対してActiveRecordを用いて条件を指定するとき、結合先のテーブル名を明示しなければいけないからです。

 
joinsメソッドを用いた時の結合先の条件指定について

https://pikawaka.com/rails/joins#whereメソッドで条件を追加してみよう(https://pikawaka.com/rails/joins#whereメソッドで条件を追加してみよう)

https://railsguides.jp/active_record_querying.html#結合テーブルで条件を指定する

 
 
最後にorderを使用した時のパフォーマンスについてですが、orderの引数にはハッシュではなく、文字列を使用することでパフォーマンスを向上させることができます。

orderメソッドの引数をハッシュにしたとき

@user.posts.joins(:comments).order({comments: {created_at: :asc}})

orderメソッドの引数を文字列にしたとき(上記の「orderメソッドの引数をハッシュにしたとき」と同じ内容です。)

@user.posts.joins(:comments).order("comments.created_at ASC")

orderメソッドの引数にハッシュを使用する代わりに、文字列を使用することでパフォーマンスが向上する理由は、Rubyのハッシュを解析するオーバーヘッド(何らかの処理を実行する際にかかる時間的・空間的なコスト、負荷のこと)がなくなるためです。

ハッシュを使用する場合、RubyのハッシュオブジェクトをSQL文に変換する必要がありますが、文字列を使用する場合は、SQL文の文字列を直接渡すことができます。このため、文字列を使用する方が高速であるとされています。
ただし、文字列を使用する場合は、SQLインジェクション(SQL文を作成し、データを書き換えること)のリスクがあるため、引用符のエスケープを行う必要があります。

たとえば、以下のようなコードでは、params[:column]に不正な文字列が渡された場合、SQLインジェクション攻撃を受ける可能性があります。

@user.posts.joins(:comments).order("#{params[:column]} ASC")

このような攻撃を防ぐためには、sanitize_sqlメソッドを使用して、引用符をエスケープする必要があります。

@user.posts.joins(:comments).order(sanitize_sql(["? ASC", params[:column]]))

このように、sanitize_sqlメソッドを使用することで、安全にSQL文を構築することができます。

 
sanitize_sql メソッドについて
sanitize_sqlメソッドは、SQLクエリを安全に構築するために使用されます。
引数に渡された文字列を安全な形式に変換し、SQLクエリに挿入することSQLインジェクション攻撃を防止することができます。
また、sanitize_sqlメソッドは、RailsのActiveRecordクエリインターフェースで使用されるSQLフラグメント(ActiveRecordを使用してSQLクエリを構築する際に、SQLクエリの一部として直接文字列で渡されるSQLコードのこと)や、手動で構築されたSQLクエリのセキュリティを向上させるために役立ちます。

https://railsguides.jp/security.html#sqlインジェクション
https://api.rubyonrails.org/v7.0/classes/ActiveRecord/Sanitization/ClassMethods.html#method-i-sanitize_sql

 
※SQLフラグメント
SQLフラグメントとは、ActiveRecordを使用してSQLクエリを構築する際に、SQLクエリの一部として直接文字列で渡されるSQLコードのことを指します。例えば、以下のようなコードが該当します。

User.where("name = 'John' AND age > 18")

このコードでは、whereメソッドの引数に直接SQLコードが指定されており、nameカラムがJohnであり、ageカラムが18より大きいユーザーを取得するクエリを生成しています。ここで、"name = 'John' AND age > 18"がSQLフラグメントとなります。

Discussion

parapone1005parapone1005

追記
orderメソッドは関連先データで並び替えることができる。

class Article < ApplicationRecord
  belongs_to :category
end

class Category < ApplicationRecord
  has_many :articles
end
@articles = Article.joins(:category).order('categories.name ASC')

この例では、 joins(:category)ArticleCategory を結合し、 order('categories.name ASC') でカテゴリの名前で昇順にソートしています。