#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 django-updates+unsubscr...@googlegroups.com.
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