LEFT OUTER JOIN in Rails 4

前端 未结 12 1695
一整个雨季
一整个雨季 2020-11-28 09:44

I have 3 models:

class Student < ActiveRecord::Base
  has_many :student_enrollments, dependent: :destroy
  has_many :courses, through: :student_enrollment         


        
相关标签:
12条回答
  • 2020-11-28 10:03

    Combining includes and where results in ActiveRecord performing a LEFT OUTER JOIN behind the scenes (without the where this would generate the normal set of two queries).

    So you could do something like:

    Course.includes(:student_enrollments).where(student_enrollments: { course_id: nil })
    

    Docs here: http://guides.rubyonrails.org/active_record_querying.html#specifying-conditions-on-eager-loaded-associations

    0 讨论(0)
  • 2020-11-28 10:03

    Use Squeel:

    Person.joins{articles.inner}
    Person.joins{articles.outer}
    
    0 讨论(0)
  • 2020-11-28 10:05

    You'd execute the query as:

    Course.joins('LEFT JOIN student_enrollment on courses.id = student_enrollment.course_id')
          .where(active: true, student_enrollments: { student_id: SOME_VALUE, id: nil })
    
    0 讨论(0)
  • 2020-11-28 10:05

    I know that this is an old question and an old thread but in Rails 5, you could simply do

    Course.left_outer_joins(:student_enrollments)
    
    0 讨论(0)
  • 2020-11-28 10:07

    I've been struggling with this kind of problem for quite some while, and decided to do something to solve it once and for all. I published a Gist that addresses this issue: https://gist.github.com/nerde/b867cd87d580e97549f2

    I created a little AR hack that uses Arel Table to dynamically build the left joins for you, without having to write raw SQL in your code:

    class ActiveRecord::Base
      # Does a left join through an association. Usage:
      #
      #     Book.left_join(:category)
      #     # SELECT "books".* FROM "books"
      #     # LEFT OUTER JOIN "categories"
      #     # ON "books"."category_id" = "categories"."id"
      #
      # It also works through association's associations, like `joins` does:
      #
      #     Book.left_join(category: :master_category)
      def self.left_join(*columns)
        _do_left_join columns.compact.flatten
      end
    
      private
    
      def self._do_left_join(column, this = self) # :nodoc:
        collection = self
        if column.is_a? Array
          column.each do |col|
            collection = collection._do_left_join(col, this)
          end
        elsif column.is_a? Hash
          column.each do |key, value|
            assoc = this.reflect_on_association(key)
            raise "#{this} has no association: #{key}." unless assoc
            collection = collection._left_join(assoc)
            collection = collection._do_left_join value, assoc.klass
          end
        else
          assoc = this.reflect_on_association(column)
          raise "#{this} has no association: #{column}." unless assoc
          collection = collection._left_join(assoc)
        end
        collection
      end
    
      def self._left_join(assoc) # :nodoc:
        source = assoc.active_record.arel_table
        pk = assoc.association_primary_key.to_sym
        joins source.join(assoc.klass.arel_table,
          Arel::Nodes::OuterJoin).on(source[assoc.foreign_key].eq(
            assoc.klass.arel_table[pk])).join_sources
      end
    end
    

    Hope it helps.

    0 讨论(0)
  • 2020-11-28 10:10

    You could use left_joins gem, which backports left_joins method from Rails 5 for Rails 4 and 3.

    Course.left_joins(:student_enrollments)
          .where('student_enrollments.id' => nil)
    
    0 讨论(0)
提交回复
热议问题