2
1

More than 5 years have passed since last update.

ActiveRecordで行値式で検索する方法

Last updated at Posted at 2019-05-02

以下のように複数カラムをつかってINで検索することを行値式行値構成子と呼ぶそうです。

SELECT * FROM table
WHERE (column1, column2) IN (
  (value1, value2),
  (value3, value4)
);

これをRailsで使用する方法がなさそうだったので、実現する方法を考えました。
ActiveRecord.where

Hoge.where('id = ? OR id = ?', 1, 2)
=> SELECT  `hoges`.* FROM `hoges` WHERE (id = 1 OR id = 2)

のように文字列でWHERE文を使用しつつ、プレースホルダも使えるので、これを利用します。

pluckなどで取得した値をそのまま使えるようにしたかったので、
第1引数にカラム名の配列
第2引数に値の配列
を指定してwhereに渡す値を生成するメソッドを定義します。

# @param [Array] columns 検索に使用するカラム名の配列 ex. [:id, :name]
# @param [Array] values 検索に使用する値の2重配列 ex. [[1, 'hoge'], [2, 'piyo']]
# @return [Array] ActiveRecord.whereで使用できるWHERE句と検索値を持った配列
def row_constructor(columns, values)
  # 1レコード分のプレースホルダを作成
  # ex. [:id, :name] => (?,?)
  placeholder = "(#{(['?'] * columns.size).join(',')})"
  # 検索数分のプレースホルダを作成
  # ex. [[1, 'hoge'], [2, 'piyo']] => (?,?),(?,?)
  placeholders = ([placeholder] * values.size).join(',')
  # 行値式のWHERE句を作成
  # ex. (id,name) IN ((?,?),(?,?))
  sql = "(#{columns.join(',')}) IN (#{placeholders})"
  # ActiveRecord.whereには可変長引数で渡すので2重配列を平坦にして、先頭にクエリ文字列を追加
  values.flatten.unshift(sql)
end

上記のメソッドを以下のように使用します。

columns = [:id, :name]
values = [[1, 'hoge'], [2, 'piyo']]
Hoge.where(*row_constructor(columns, values))
=> SELECT  `hoges`.* FROM `hoges` WHERE ((id,name) IN ((1,'hoge'),(2,'piyo')))

そもそもActiveRecordにメソッドが用意されていたり、
gemがあったら教えてください。

2
1
3

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
2
1