ActiveRecordでwhere 複数カラム in サブクエリを実装する

簡単にできそうでなかなか苦労したのでメモしておきます。
素のSQLに近い状態で書くのはそれほど難しくなさそうだったのですが、なんとなくActiveRecordを使って実装した方が…と思ってやってみました。

やりたいこととしては、たとえばこのようなuser_eventsというテーブルがあった場合に、ユーザーごとの最初のイベントのレコードのみを取り出したいという状況です。
user_eventsテーブル

SELECT * FROM `user_events` WHERE (`user_id`, `event_date`) IN (SELECT `user_id`, min(`event_date`) AS `min_event_date` FROM `user_events` GROUP BY `user_id`);

で、こんなデータを取り出したいというイメージです。
SQL実行結果イメージ

こちらですが、このように記述しました。
元々書いていた方法(記事下部に残しています)は、やはりパフォーマンス的に問題になるのでは?というところで、お仕事仲間から教えてもらいました

sub_query = UserEvents.group(:user_id)
  .select('user_id')
  .select('min(`event_date`) AS `min_event_date`')
UserEvents.where("(`user_id` , `event_date`) IN (#{sub_query.to_sql})")

—– 以下、以前紹介していた方法 —–

sub_query = UserEvents.group(:user_id).select(:user_id, 'min(event_date) as min_event_date')
sub_query.to_a.map do |sub_record|
    UserEvents.where(user_id: sub_record['user_id']).where(event_date: sub_record['min_event_date'])
end.reduce(&:or)

これにより、where 複数カラム in サブクエリの部分が項目ごとにandとorでつなぎ合わされるので、望むような結果を得ることができます。
ただ、サブクエリで抽出されるレコード数が多い場合はちょっとどうなるのか心配ですが。。

ActiveRecordでwhere 複数カラム in サブクエリを実装するにコメントする

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です