Skip to content

JOIN 고도화에 사용되는 함수들. 단일 쿼리에서 계속 JOIN을 수행하면 비효율적이므로 미리 조인을 시켜놓은 결과를 객체로 반환한다.

select_related#

JOIN을 하는 타이밍이 DB상에서 이루어진다. 따라서 참조와 역참조 에서 다룬 JOIN이 이루어지는 위치에 따라 생각해보면... DB에서 JOIN을 하니까 정참조로는 ForiegnKey, OneToOne 모두 가능했지만, 역참조는 OneToOne만이 가능하다. 왜? 아직 파이썬 메모리 안에 대응수에 대한 정보가 없을 때 (N:M, 1:N) 오직 DB 쿼리만으로 역참조에 대한 정보가 어렵기 때문이다.

  • 정참조
    • ForeignKey
    • OneToOne
  • 역참조
    • OneToOne
books = Book.objects.select_related("author")
for book in books:
    print(book.author.name)

Book 안에 author를 ForeignKey로 들고 있는 경우 select_related를 사용하면 JOIN을 한번만 사용하여 books 객체와 각 author를 참조할 수 있다.

prefetch_related#

JOIN할 대상을 먼저 가져온 뒤 파이썬 상에서 JOIN이 이루어진다. 따라서 참조와 역참조에서 다룬 JOIN이 이루어지는 위치에 따라 생각해보면... 이미 테이블이 파이썬 메모리 안에 다 들어와 있기 때문에 역참조가 select_related보다 훨씬 유연한 편이다.

  • 정참조
    • ForeignKey
    • ManyToMany
  • 역참조
    • ForeignKey
    • ManyToMany
authors = Author.objects.prefetch_related("book_set")
for author in authors:
    for book in author.book_set.all():
        print(book.title)

성능비교#

connection.queries를 가지고 쿼리가 몇번이나 호출되었는지를 확인할 수 있다:

without optimization

from django.db import connection, reset_queries

# Clear queries log
reset_queries()

books = Book.objects.all()

# Access related objects (may trigger N+1 problem)
for book in books:
    print(book.author.name)

# Print executed SQL queries
for query in connection.queries:
    print(query['sql'])

optimized query

from django.db import connection, reset_queries

reset_queries()

# Optimized query
books = Book.objects.select_related('author')
for book in books:
    print(book.author.name)

# Check the queries
for query in connection.queries:
    print(query['sql'])