How do I build a query in Ruby on Rails that joins on the max of a has_many relation only and includes a select filter on that relation?

前端 未结 7 1794
走了就别回头了
走了就别回头了 2021-01-06 12:56

I\'m struggling how to have Ruby on Rails do this query right... in short: to join on a has_many relation but only via the most recent record in that r

7条回答
  •  伪装坚强ぢ
    2021-01-06 13:18

    One alternative is to use a LATERAL JOIN which is a Postgres 9.3+ specific feature which can be described as something like a SQL foreach loop.

    class Employee < ApplicationRecord
      has_many :employments
      def self.in_active_employment
        lat_query = Employment.select(:status)
                          .where('employee_id = employees.id') # lateral reference
                          .order(created_at: :desc)
                          .limit(1)
        joins("JOIN LATERAL(#{lat_query.to_sql}) ce ON true")
          .where(ce: { status: 'active' })
      end
    end
    

    This fetches the latest row from employments and then uses this in the WHERE clause to filter the rows from employees.

    SELECT "employees".* FROM "employees" 
    JOIN LATERAL(
      SELECT "employments"."status" 
      FROM "employments" 
      WHERE (employee_id = employees.id) 
      ORDER BY "employments"."created_at" DESC 
      LIMIT 1
    ) ce  ON true 
    WHERE "ce"."status" = $1 LIMIT $2 
    

    This is going to be extremely fast in comparison to a WHERE id IN subquery if the data set is large. Of course the cost is limited portability.

提交回复
热议问题