select_related和prefetch_related

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了select_related和prefetch_related相关的知识,希望对你有一定的参考价值。

 select_related

1)通过filter或values进行跨表查询

book_queryset=models.Book.objects.filter(title=‘西游记‘).values(‘publish__pk‘,‘publish__name‘,‘pk‘,‘title‘,‘price‘)
print(book_queryset)
<QuerySet [{‘publish__pk‘: 2, ‘publish__name‘: ‘人名出版社‘, ‘pk‘: 5, ‘title‘: ‘西游记‘, ‘price‘: Decimal(‘345.67‘)}]>
SELECT app01_book.publish_id, app01_publish.name, app01_book.nid, app01_book.title, app01_book.price
FROM "app01_book"
    INNER JOIN "app01_publish" ON app01_book.publish_id = app01_publish.nid
WHERE app01_book.title = 西游记
LIMIT 21;

2)分步查询,访问数据库两次

book_queryset=models.Book.objects.filter(title="西游记")
print(book_queryset)
author_queryset=book_queryset.values(‘publish__pk‘,‘publish__name‘,‘pk‘,‘title‘,‘price‘)
print(author_queryset)

<QuerySet [<Book: Book object>]>
<QuerySet [{‘publish__pk‘: 2, ‘publish__name‘: ‘人名出版社‘, ‘pk‘: 5, ‘title‘: ‘西游记‘, ‘price‘: Decimal(‘345.67‘)}]>

SELECT "app01_book"."nid", "app01_book"."title", "app01_book"."publishDate", "app01_book"."price", "app01_book"."keepNum"
  , "app01_book"."commentNum", "app01_book"."publish_id"
FROM "app01_book" WHERE app01_book.title = 西游记 LIMIT 21; SELECT app01_book.publish_id, app01_publish.name, app01_book.nid, app01_book.title, app01_book.price FROM "app01_book" INNER JOIN "app01_publish" ON app01_book.publish_id = app01_publish.nid WHERE app01_book.title = 西游记 LIMIT 21;

 3)使用select_related,跨表查询,查询一次

book_queryset=models.Book.objects.select_related().filter(title="西游记")
print(book_queryset)
publish_queryset=book_queryset.values(publish__pk,publish__name,pk,title,price)
print(publish_queryset)
<QuerySet [<Book: Book object>]>
<QuerySet [{publish__pk: 2, publish__name: 人名出版社, pk: 5, title: 西游记, price: Decimal(345.67)}]>
SELECT app01_book.nid, app01_book.title, app01_book.publishDate, app01_book.price, app01_book.keepNum
    , app01_book.commentNum, app01_book.publish_id, app01_publish.nid, app01_publish.name, app01_publish.city
    , app01_publish.email
FROM "app01_book"
    INNER JOIN "app01_publish" ON app01_book.publish_id = app01_publish.nid
WHERE app01_book.title = 西游记
LIMIT 21;

SELECT app01_book.publish_id, app01_publish.name, app01_book.nid, app01_book.title, app01_book.price
FROM "app01_book"
    INNER JOIN "app01_publish" ON app01_book.publish_id = app01_publish.nid
WHERE app01_book.title = 西游记
LIMIT 21;

技术分享

# Hits the database.
e = Entry.objects.get(id=5)

# Hits the database again to get the related Blog object.
b = e.blog

技术分享

# Hits the database.
e = Entry.objects.select_related(blog).get(id=5)

# Doesn‘t hit the database, because e.blog has been prepopulated
# in the previous query.
b = e.blog

技术分享

from django.utils import timezone

# Find all the blogs with entries scheduled to be published in the future.
blogs = set()

for e in Entry.objects.filter(pub_date__gt=timezone.now()).select_related(blog):
    # Without select_related(), this would make a database query for each
    # loop iteration in order to fetch the related blog for each entry.
    blogs.add(e.blog)

 技术分享

Entry.objects.filter(pub_date__gt=timezone.now()).select_related(blog)
Entry.objects.select_related(blog).filter(pub_date__gt=timezone.now())

技术分享

from django.db import models

class City(models.Model):
    # ...
    pass

class Person(models.Model):
    # ...
    hometown = models.ForeignKey(
        City,
        on_delete=models.SET_NULL,
        blank=True,
        null=True,
    )

class Book(models.Model):
    # ...
    author = models.ForeignKey(Person, on_delete=models.CASCADE)

技术分享

b = Book.objects.select_related(author__hometown).get(id=4)
p = b.author         # Doesn‘t hit the database.
c = p.hometown       # Doesn‘t hit the database.

b = Book.objects.get(id=4) # No select_related() in this example.
p = b.author         # Hits the database.
c = p.hometown       # Hits the database.


prefetch_related

from django.db import models

class Topping(models.Model):
    name = models.CharField(max_length=30)

class Pizza(models.Model):
    name = models.CharField(max_length=50)
    toppings = models.ManyToManyField(Topping)

    def __str__(self):              # __unicode__ on Python 2
        return "%s (%s)" % (
            self.name,
            ", ".join(topping.name for topping in self.toppings.all()),
        )

 

1) 不使用prefetch_related, 共进行四次查询

pizza_queryset=models.Pizza.objects.all()
print(pizza_queryset)
<QuerySet [<Pizza: alex (tomato, capsicum, eggplant, zucchini, mushrooms)>, <Pizza: egon (tomato, capsicum, eggplant, zucchini, mushrooms)>, <Pizza: yuan (tomato, capsicum, eggplant, zucchini)>]>
SELECT app01_pizza.id, app01_pizza.name
FROM "app01_pizza"
LIMIT 21;

SELECT app01_topping.id, app01_topping.name
FROM "app01_topping"
    INNER JOIN "app01_pizza_toppings" ON app01_topping.id = app01_pizza_toppings.topping_id
WHERE app01_pizza_toppings.pizza_id = 1;

SELECT app01_topping.id, app01_topping.name
FROM "app01_topping"
    INNER JOIN "app01_pizza_toppings" ON app01_topping.id = app01_pizza_toppings.topping_id
WHERE app01_pizza_toppings.pizza_id = 2;

SELECT app01_topping.id, app01_topping.name
FROM "app01_topping"
    INNER JOIN "app01_pizza_toppings" ON app01_topping.id = app01_pizza_toppings.topping_id
WHERE app01_pizza_toppings.pizza_id = 3;

2) 使用prefetch_related, 共进行两次次查询

pizza_queryset=models.Pizza.objects.all().prefetch_related(toppings)
print(pizza_queryset)
<QuerySet [<Pizza: alex (tomato, capsicum, eggplant, zucchini, mushrooms)>, <Pizza: egon (tomato, capsicum, eggplant, zucchini, mushrooms)>, <Pizza: yuan (tomato, capsicum, eggplant, zucchini)>]>
SELECT app01_pizza.id, app01_pizza.name
FROM "app01_pizza"
LIMIT 21;

SELECT app01_pizza_toppings.pizza_id AS "_prefetch_related_val_pizza_id", app01_topping.id, app01_topping.name
FROM "app01_topping"
    INNER JOIN "app01_pizza_toppings" ON app01_topping.id = app01_pizza_toppings.topping_id
WHERE app01_pizza_toppings.pizza_id IN (1, 2, 3);

技术分享

参考:https://docs.djangoproject.com/en/1.11/ref/models/querysets/#django.db.models.query.QuerySet.prefetch_related

 











以上是关于select_related和prefetch_related的主要内容,如果未能解决你的问题,请参考以下文章

Django:select_related() 的用法和执行时间性能

如何使用 select_related

如何结合 select_related() 和 value()? (2016)

Django查询优化之select_related和prefetch_related

select_related和prefetch_related

pythonのdjango select_related 和 prefetch_related()