Rails 4의 LEFT OUTER JOIN
3 가지 모델이 있습니다.
class Student < ActiveRecord::Base
has_many :student_enrollments, dependent: :destroy
has_many :courses, through: :student_enrollments
end
class Course < ActiveRecord::Base
has_many :student_enrollments, dependent: :destroy
has_many :students, through: :student_enrollments
end
class StudentEnrollment < ActiveRecord::Base
belongs_to :student
belongs_to :course
end
특정 학생과 관련된 StudentEnrollments 테이블에없는 Courses 테이블의 강좌 목록을 쿼리하고 싶습니다.
아마도 Left Join이 갈 길이라는 것을 알았지 만 레일의 joins ()는 테이블을 인수로만 받아들이는 것 같습니다. 내가 원하는 것을 할 것이라고 생각하는 SQL 쿼리는 다음과 같습니다.
SELECT *
FROM Courses c LEFT JOIN StudentEnrollment se ON c.id = se.course_id
WHERE se.id IS NULL AND se.student_id = <SOME_STUDENT_ID_VALUE> and c.active = true
이 쿼리를 Rails 4 방식으로 어떻게 실행합니까?
모든 입력을 부탁드립니다.
join-sql 인 문자열도 전달할 수 있습니다. 예 :joins("LEFT JOIN StudentEnrollment se ON c.id = se.course_id")
명확성을 위해 레일 표준 테이블 명명을 사용하지만 :
joins("LEFT JOIN student_enrollments ON courses.id = student_enrollments.course_id")
Rails 5에서 왼쪽 외부 조인을 수행하는 일반적인 방법을 찾는 사람이 있다면이 #left_outer_joins
함수를 사용할 수 있습니다 .
다중 조인 예 :
루비:
Source.
select('sources.id', 'count(metrics.id)').
left_outer_joins(:metrics).
joins(:port).
where('ports.auto_delete = ?', true).
group('sources.id').
having('count(metrics.id) = 0').
all
SQL :
SELECT sources.id, count(metrics.id)
FROM "sources"
INNER JOIN "ports" ON "ports"."id" = "sources"."port_id"
LEFT OUTER JOIN "metrics" ON "metrics"."source_id" = "sources"."id"
WHERE (ports.auto_delete = 't')
GROUP BY sources.id
HAVING (count(metrics.id) = 0)
ORDER BY "sources"."id" ASC
실제로 이것을하기위한 "Rails Way"가 있습니다.
Arel을 사용할 수 있습니다 . 이는 Rails가 ActiveRecrods에 대한 쿼리를 생성하는 데 사용하는 것입니다.
나는 그것을 메서드로 감싸서 멋지게 호출하고 원하는 인수를 전달할 수 있습니다.
class Course < ActiveRecord::Base
....
def left_join_student_enrollments(some_user)
courses = Course.arel_table
student_entrollments = StudentEnrollment.arel_table
enrollments = courses.join(student_enrollments, Arel::Nodes::OuterJoin).
on(courses[:id].eq(student_enrollments[:course_id])).
join_sources
joins(enrollments).where(
student_enrollments: {student_id: some_user.id, id: nil},
active: true
)
end
....
end
또한 많은 사람들이 사용하는 빠르고 (약간 더러운) 방법이 있습니다.
Course.eager_load(:students).where(
student_enrollments: {student_id: some_user.id, id: nil},
active: true
)
eager_load 그것은 당신이 (귀하의 경우처럼)가 필요하지 않을 수도 메모리에 loding 모델의 "부작용"을 가지고, 좋은 작품
레일 액티브을 참조하십시오 : QueryMethods는 .eager_load
그것은 당신이 깔끔한 방법으로 요구하고 정확하게 수행합니다.
결합 includes
하고 where
ActiveRecord가 LEFT OUTER JOIN을 수행하는 결과를 얻습니다 (일반적인 두 쿼리 집합을 생성하는 위치없이).
따라서 다음과 같이 할 수 있습니다.
Course.includes(:student_enrollments).where(student_enrollments: { course_id: nil })
위의 대답에 추가 includes
하여을 사용 하려면 where (id가 nil 인 것과 같이) 테이블을 참조하지 않고 OUTER JOIN을 원하거나 참조가 문자열에 있습니다 references
. 다음과 같이 표시됩니다.
Course.includes(:student_enrollments).references(:student_enrollments)
또는
Course.includes(:student_enrollments).references(:student_enrollments).where('student_enrollments.id = ?', nil)
http://api.rubyonrails.org/classes/ActiveRecord/QueryMethods.html#method-i-references
쿼리를 다음과 같이 실행합니다.
Course.joins('LEFT JOIN student_enrollment on courses.id = student_enrollment.course_id')
.where(active: true, student_enrollments: { student_id: SOME_VALUE, id: nil })
나는 이것이 오래된 질문이고 오래된 스레드라는 것을 알고 있지만 Rails 5에서는 간단하게 할 수 있습니다.
Course.left_outer_joins(:student_enrollments)
Rails 4와 3을 위해 Rails 5의 메소드 를 백 포트 하는 left_joins gem을 사용할 수 있습니다 left_joins
.
Course.left_joins(:student_enrollments)
.where('student_enrollments.id' => nil)
Rails의 Active Model의 조인 쿼리입니다.
활성 모델 쿼리 형식에 대한 자세한 내용을 보려면 여기를 클릭하십시오 .
@course= Course.joins("LEFT OUTER JOIN StudentEnrollment
ON StudentEnrollment .id = Courses.user_id").
where("StudentEnrollment .id IS NULL AND StudentEnrollment .student_id =
<SOME_STUDENT_ID_VALUE> and Courses.active = true").select
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.
Use Squeel:
Person.joins{articles.inner}
Person.joins{articles.outer}
If you want OUTER JOINs without all the extra eagerly loaded ActiveRecord objects, use .pluck(:id)
after .eager_load()
to abort the eager load while preserving the OUTER JOIN. Using .pluck(:id)
thwarts eager loading because the column name aliases (items.location AS t1_r9
, for example) disappear from the generated query when used (these independently named fields are used to instantiate all the eagerly loaded ActiveRecord objects).
A disadvantage of this approach is that you then need to run a second query to pull in the desired ActiveRecord objects identified in the first query:
# first query
idents = Course
.eager_load(:students) # eager load for OUTER JOIN
.where(
student_enrollments: {student_id: some_user.id, id: nil},
active: true
)
.distinct
.pluck(:id) # abort eager loading but preserve OUTER JOIN
# second query
Course.where(id: idents)
참고URL : https://stackoverflow.com/questions/24358805/left-outer-join-in-rails-4
'development' 카테고리의 다른 글
strlcpy 및 strlcat이 안전하지 않은 것으로 간주되는 이유는 무엇입니까? (0) | 2020.10.18 |
---|---|
iOS에서 여러 스토리 보드 사용 (0) | 2020.10.18 |
Swift 1.2의 @noescape 속성 (0) | 2020.10.18 |
Angular2에서 구독을 취소하는 방법 (0) | 2020.10.18 |
#pragma가 한 번 자동으로 가정되지 않는 이유는 무엇입니까? (0) | 2020.10.18 |