321

이것은 "나는 이걸하는 법을 모르겠다"라는 질문보다는 "이런 식으로 일하는 것이 왜"라는 질문에 더 가깝다.

그래서 당신이 사용하려고하는 관련 기록을 가져 오는 것에 관한 복음은:include조인을하고 많은 추가 쿼리를 피할 것이기 때문에 :

Post.all(:include => :comments)

그러나 로그를 볼 때 조인은 없습니다.

Post Load (3.7ms)   SELECT * FROM "posts"
Comment Load (0.2ms)   SELECT "comments.*" FROM "comments" 
                       WHERE ("comments".post_id IN (1,2,3,4)) 
                       ORDER BY created_at asc) 

그것~이다.한 번에 모든 주석을 가져 오기 때문에 지름길을 취하고 있지만 여전히 조인은 아닙니다 (모든 문서가 말하는 것처럼 보입니다). 내가 가입 할 수있는 유일한 방법은:joins대신에:include:

Post.all(:joins => :comments)

로그에 다음과 같이 표시됩니다.

Post Load (6.0ms)  SELECT "posts".* FROM "posts" 
                   INNER JOIN "comments" ON "posts".id = "comments".post_id

내가 놓친 게 있니? 나는 6 개의 연관성을 가진 앱을 가지고 있으며, 한 화면에 모든 데이터를 표시합니다. 6 명의 개인이 아닌 하나의 조인 쿼리를 갖는 것이 더 나을 것 같습니다. 퍼포먼스 측면에서 볼 때, 개별 쿼리보다는 조인을하는 것이 항상 더 좋은 것은 아니라는 것을 알고 있습니다. (사실, 위의 두 개의 개별 쿼리가 조인보다 빠르다는 것을 알지만, 모든 문서 내가 읽은 적이있다. 나는보고 놀랐다.:include광고 된대로 작동하지 않습니다.

어쩌면 레일즈~이다.성능 문제를 인식하고 어떤 경우를 제외하고는 참여하지 않습니까?


  • 이전 버전의 레일즈를 사용했다면 태그 나 질문 본문을 통해 그 사실을 알려주십시오. 그렇지 않으면, 지금 Rails 4를 사용하고 있다면, 그것은includes(이것을 읽는 모든 사람에게) - onebree
  • 또한 이미 있습니다 : preload 및 : eager_loadblog.bigbinary.com/2013/07/01/… - CJW

8 답변


167

그것은:include기능은 Rails 2.1에서 변경되었습니다. 레일즈는 모든 경우에 조인을하는 데 사용되었지만 성능상의 이유로 인해 일부 상황에서 여러 쿼리를 사용하도록 변경되었습니다.이 블로그 게시물Fabio Akita의 변화에 대한 좋은 정보가 있습니다 ( "최적화 된 Eager Loading"섹션 참조).



81

.joins테이블을 조인하고 선택한 필드를 반환합니다. 조인 쿼리 결과에 대한 연결을 호출하면 데이터베이스 쿼리가 다시 실행됩니다

:includes포함 된 연관을 열렬히로드하여 메모리에 추가합니다.:includes포함 된 테이블 속성을 모두로드합니다. 포함 쿼리 결과에 대한 연결을 호출하면 쿼리를 실행하지 않습니다.

나의블로그 게시물차이점에 대한 자세한 설명이 나와 있습니다.


  • 링크가 죽은 것 같습니다. - Eliad

68

조인과 include의 차이점은 include 문을 사용하면 다른 테이블의 모든 특성을 메모리에로드하는 훨씬 더 큰 SQL 쿼리를 생성한다는 것입니다.

예를 들어, 주석으로 가득 찬 테이블이 있고 : joins => 사용자가 정렬 목적으로 모든 사용자 정보를 가져 오는 등의 작업을 수행하면 잘 작동하고 include보다 표시 시간이 적습니다. 그러나 표시하고 싶다고 말합니다 사용자 이름, 전자 메일 등과 함께 주석을 사용합니다. 조인을 사용하여 정보를 가져 오려면 가져 오는 각 사용자에 대해 별도의 SQL 쿼리를 작성해야하지만 사용한 경우 :이 정보를 사용할 준비가되었습니다.

좋은 예 :

http://railscasts.com/episodes/181-include-vs-joins


50

성능 고려 사항 외에도 기능적 차이점이 있습니다. 주석에 참여할 때 주석이있는 게시물, 기본적으로 내부 조인을 요청합니다. 주석을 포함 할 때 모든 게시물 (외부 조인)을 요청합니다.


47

나는 최근에 차이점에 대해 더 많이 읽었습니다.:joins:includes레일에. 여기에 내가 이해 한 것을 설명하고있다.

이 시나리오를 고려하십시오.

  • 사용자는 has_many 개의 주석과 주석을 사용자에게 소유합니다.

  • User 모델에는 Name (문자열), Age (정수) 등의 특성이 있습니다. Comment 모델에는 Content, user_id 속성이 있습니다. 주석의 경우 user_id는 null 일 수 있습니다.

조인 :

: 조인은내부 조인두 테이블 사이. 그러므로

Comment.joins(:user)

#=> <ActiveRecord::Relation [#<Comment id: 1, content: "Hi I am Aaditi.This is my first   comment!", user_id: 1, created_at: "2014-11-12 18:29:24", updated_at: "2014-11-12 18:29:24">, 
     #<Comment id: 2, content: "Hi I am Ankita.This is my first comment!", user_id: 2, created_at: "2014-11-12 18:29:29", updated_at: "2014-11-12 18:29:29">,    
     #<Comment id: 3, content: "Hi I am John.This is my first comment!", user_id: 3, created_at: "2014-11-12 18:30:25", updated_at: "2014-11-12 18:30:25">]>

가져올 것이다user_id (설명 테이블의)가 user.id (사용자 테이블)와 같은 모든 레코드.따라서 당신이한다면

Comment.joins(:user).where("comments.user_id is null")

#=> <ActiveRecord::Relation []>

그림과 같이 빈 배열이 생깁니다.

또한 조인은 조인 된 테이블을 메모리에로드하지 않습니다. 따라서 당신이한다면

comment_1 = Comment.joins(:user).first

comment_1.user.age
#=>←[1m←[36mUser Load (0.0ms)←[0m  ←[1mSELECT "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT 1←[0m  [["id", 1]]
#=> 24

보시다시피,comment_1.user.age백그라운드에서 데이터베이스 쿼리를 다시 실행하여 결과를 얻습니다.

다음을 포함합니다 :

: 포함하는 것은왼쪽 외부 조인두 테이블 사이. 그러므로

Comment.includes(:user)

#=><ActiveRecord::Relation [#<Comment id: 1, content: "Hi I am Aaditi.This is my first comment!", user_id: 1, created_at: "2014-11-12 18:29:24", updated_at: "2014-11-12 18:29:24">,
   #<Comment id: 2, content: "Hi I am Ankita.This is my first comment!", user_id: 2, created_at: "2014-11-12 18:29:29", updated_at: "2014-11-12 18:29:29">,
   #<Comment id: 3, content: "Hi I am John.This is my first comment!", user_id: 3, created_at: "2014-11-12 18:30:25", updated_at: "2014-11-12 18:30:25">,    
   #<Comment id: 4, content: "Hi This is an anonymous comment!", user_id: nil, created_at: "2014-11-12 18:31:02", updated_at: "2014-11-12 18:31:02">]>

결과는주석 테이블의 모든 레코드가있는 조인 된 테이블따라서 당신이한다면

Comment.includes(:user).where("comment.user_id is null")
#=> #<ActiveRecord::Relation [#<Comment id: 4, content: "Hi This is an anonymous comment!", user_id: nil, created_at: "2014-11-12 18:31:02", updated_at: "2014-11-12 18:31:02">]>

comments.user_id가 표시된대로 nil 인 레코드를 가져옵니다.

또한 메모리에있는 테이블을 모두로드합니다. 따라서 당신이한다면

comment_1 = Comment.includes(:user).first

comment_1.user.age
#=> 24

comment_1.user.age가 백그라운드에서 데이터베이스 쿼리를 실행하지 않고 메모리에서 결과를 단순히로드한다는 것을 알 수 있습니다.


  • Rails 4에 대한 것입니까? - onebree
  • @HunterStevens : 네, 그렇습니다. - Aaditi Jain

6

tl; dr

나는 그것들을 두 가지 방식으로 대조합니다 :

조인- 레코드의 조건부 선택.

포함하다- 결과 세트의 각 구성원에 대해 연관을 사용할 때.

더 긴 버전

조인은 데이터베이스에서 가져 오는 결과 세트를 필터링합니다. 당신은 그것을 사용하여 테이블에 대한 작업을 설정합니다. 이것을 집합 이론을 수행하는 where 절로 생각하십시오.

Post.joins(:comments)

와 같다

Post.where('id in (select post_id from comments)')

예외가 하나 이상있는 경우 조인을 사용하여 중복 게시물을 다시 받게됩니다. 그러나 모든 게시물은 의견이있는 게시물이됩니다. 다음과 같이 이것을 수정할 수 있습니다 :

Post.joins(:comments).count
=> 10
Post.joins(:comments).distinct.count
=> 2

계약시,includes메서드는 릴레이션을 참조 할 때 추가 데이터베이스 쿼리가 없는지 단순히 확인합니다 (따라서 n + 1 쿼리를 만들지 않습니다)

Post.includes(:comments).count
=> 4 # includes posts without comments so the count might be higher.

도덕적이다, 사용하다.joins조건부 집합 연산 및 사용을 원할 때includes컬렉션의 각 구성원에 대한 관계를 사용할 때.


  • distinct날 때마다 얻는다. 고맙습니다! - Beejamin

4

.joins는 데이터베이스 조인으로 작동하며 두 개 이상의 테이블을 조인하고 선택한 데이터를 백엔드 (데이터베이스)에서 가져옵니다.

.includes는 데이터베이스의 왼쪽 조인으로 작동합니다. 왼쪽면의 모든 레코드를로드했으며, 오른쪽면 모델과의 관련성이 없습니다. 메모리에 관련된 모든 객체를로드하기 때문에 열심히로드하는 데 사용됩니다. 포함 쿼리 결과에 대한 연관을 호출하면 데이터베이스에 대한 쿼리가 실행되지 않고 이미 데이터를 메모리에로드했기 때문에 메모리에서 데이터를 반환하기 만합니다.


0

'조인'은 테이블 조인에 사용되며 조인에 대한 연결을 호출하면 쿼리가 다시 실행됩니다 (많은 쿼리가 실행 됨)

lets suppose you have tow model, User and Organisation
User has_many organisations
suppose you have 10 organisation for a user 
@records= User.joins(:organisations).where("organisations.user_id = 1")
QUERY will be 
 select * from users INNER JOIN organisations ON organisations.user_id = users.id where organisations.user_id = 1

it will return all records of organisation related to user
and @records.map{|u|u.organisation.name}
it run QUERY like 
select * from organisations where organisations.id = x then time(hwo many organisation you have)

총 SQL 수는이 경우 11입니다.

하지만 함께 'includes'는 포함 된 연관을 열렬히로드하고 메모리에 추가합니다 (첫 번째로드에서 모든 연관을로드). 그리고 다시 쿼리를 실행하지 않습니다.

포함 된 레코드를 얻을 때 @ records = User.includes (: organizations) .where ( "organisations.user_id = 1") 그러면 쿼리는

select * from users INNER JOIN organisations ON organisations.user_id = users.id where organisations.user_id = 1
and 


 select * from organisations where organisations.id IN(IDS of organisation(1, to 10)) if 10 organisation
and when you run this 

@ records.map {| u | u.organisation.name}     아무 쿼리도 실행되지 않습니다.

연결된 질문


관련된 질문

최근 질문