技術メモ

書いておぼえるブログ

【Active Records】find_by_sqlで動的に組み立てたクエリを発行したい

状況

CASE式をつかったSQLをActive Recordsで実行したい。(たぶん)Active RecordsでCASEに対応するメソッドはないので、生クエリを実行するしかない。
また、WHERE句は複数の値をとる。だからクエリは動的に組み立てる必要がある。

解決

クエリはヒアドキュメントに書く。動的に変わる箇所は変数を展開させる。そして改行をスペースに置換してからfind_by_sqlに渡す。

コード

クエリ。

.strip_heredocはスペースのインデントを削除してくれる。

sql = <<-SQL.strip_heredoc
  SELECT
    CASE 
      WHEN price > #{standard_price}  THEN 'expensive' -- 高い
      WHEN price <= #{standard_price} THEN 'affordable' -- お手頃価格
    ELSE NULL END AS price_evaluation
  FROM
    books b
 SQL

実行可能なかたちに変換する。

readily_sql = sql.split("\n").map(&:strip).join(' ')

実行する。

Book.find_by_sql(sql)

雑感

このサンプルはシンプルだったが、現実世界の複雑な問題を解決するためのクエリは複雑になりがちである。複雑なクエリをActive Recordsに翻訳する技能がないとき、SQLでがんばろうとおもう。そんなときに、半ば無理やりというか、あれこれテクニックを動員して生クエリを組み立てて実行する術を知っておくのはよいことのはずだ。

ただ、長ったらしい生クエリよりも複雑なActive Records DSLの方がまだ読みやすいのではという気がする。できるだけActive Recordsのメソッドを使っていきたい。