#35442: N+1 queries from RelatedManager + only("pk")
-------------------------------------+-------------------------------------
               Reporter:  REGNIER    |          Owner:  nobody
  Guillaume                          |
                   Type:             |         Status:  new
  Uncategorized                      |
              Component:  Database   |        Version:  4.2
  layer (models, ORM)                |
               Severity:  Normal     |       Keywords:
           Triage Stage:             |      Has patch:  0
  Unreviewed                         |
    Needs documentation:  0          |    Needs tests:  0
Patch needs improvement:  0          |  Easy pickings:  0
                  UI/UX:  0          |
-------------------------------------+-------------------------------------
 When iterating over a queryset constructed from a RelatedManager and a
 {{{.only(...)}}} call that does not include the related field, a query
 occurs when instances are produced from the queryset.

 === Steps to Reproduce:
 {{{#!python
 class Company(models.Model):
     pass

 class Employee(models.Model):
     company = models.ForeignKey(Company, on_delete=models.CASCADE,
 related_name="employees")
 }}}

 {{{#!python
 company = Company.objects.create()
 Employee.objects.bulk_create(Employee(company=company) for _ in range(10))

 for employee in company.employees.only("pk"):
     # Some code that only access pk
     _ = employee.pk
 }}}

 === Expected Behavior:
 One query like
 {{{#!sql
 SELECT "employee"."id" FROM "employee" WHERE "employee"."company_id" =
 {COMPANY_ID}
 }}}

 === Actual Behavior:
 10 additional queries like:
 {{{#!sql
 SELECT "employee"."id", "employee"."company_id" FROM "employee" WHERE
 "employee"."id" = {EMPLOYEE_ID}
 }}}

 === Analysis:
 My understanding is that there is an optimization that fills the parent
 model on related instances without needing additional SQL join/query.
 However, when only a subset of fields is selected (in this case, only the
 primary key), the parent ID might not be loaded from the database,
 resulting in additional queries to perform said optimization.

 === Workaround:
 {{{#!python
 company = Company.objects.create()
 Employee.objects.bulk_create(Employee(company=company) for _ in range(10))

 for employee in Employee.objects.filter(company=company).only("pk"):
     # Some code that only access pk
     _ = employee.pk
 }}}
-- 
Ticket URL: <https://code.djangoproject.com/ticket/35442>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/0107018f6344fff5-d478091f-a4b5-47b4-b56b-6bcb542c4ceb-000000%40eu-central-1.amazonses.com.

Reply via email to