#29577: Generated SQL in PostGres for exists filter does Exists(...)=true.  
Having
just Exists(...) without "true" is faster by nearly 50% in many of my
queries
-------------------------------------+-------------------------------------
     Reporter:  Michael Chiciak      |                    Owner:  nobody
         Type:                       |                   Status:  new
  Cleanup/optimization               |
    Component:  Database layer       |                  Version:  2.0
  (models, ORM)                      |
     Severity:  Normal               |               Resolution:
     Keywords:  slow query           |             Triage Stage:
                                     |  Unreviewed
    Has patch:  0                    |      Needs documentation:  0
  Needs tests:  0                    |  Patch needs improvement:  0
Easy pickings:  0                    |                    UI/UX:  0
-------------------------------------+-------------------------------------
Description changed by Michael Chiciak:

Old description:

> I am trying to optimize my exists filters, since on large record sets I
> noticed some slowness.  I have narrowed down part of it to the "= true"
> portion of the "Exists(...)" subquery.  Removing the "= true" gives a
> noticeable performance boost
>
> Also, it would be really nice to be able to just filter and not have a
> field inside the select portion of the query.  When I use the Paginator
> class for pagination, it turns the count query into one big subquery and
> adds all my Exists filters as select fields and thus performs a Group BY
> on each of them, drastically slowing down my count query.  Only having
> the Exists in the where clause would be very nice.  Is there a way to do
> this already?
>

> Sample django code
>
> {{{
> fits_vehicle = ProductFitment.objects.filter_vehicle(2008, 2013,
> 'Chevrolet', 'Corvette').filter(product=OuterRef('pk')).only("pk")
> queryset =
> queryset.annotate(fits_vehicle=Exists(fits_vehicle)).filter(fits_vehicle=True)
>
> # With exists = true 500ms
> SELECT COUNT(*)
> FROM "aces_pies_data_product"
> WHERE EXISTS(SELECT 1 FROM "aces_pies_data_productfitment" U0 INNER JOIN
> "django_vehiclefitment_vehicle" U1 ON (U0."vehicle_id" = U1."id") INNER
> JOIN "django_vehiclefitment_vehiclemake" U2 ON (U1."make_id" = U2."id")
> INNER JOIN "django_vehiclefitment_vehiclemodel" U3 ON (U1."model_id" =
> U3."id") WHERE (U2."name" = 'Chevrolet' AND U3."name" = 'Corvette' AND
> (2008 BETWEEN U0."start_year" AND U0."end_year" OR 2009 BETWEEN
> U0."start_year" AND U0."end_year" OR 2010 BETWEEN U0."start_year" AND
> U0."end_year" OR 2011 BETWEEN U0."start_year" AND U0."end_year" OR 2012
> BETWEEN U0."start_year" AND U0."end_year" OR 2013 BETWEEN U0."start_year"
> AND U0."end_year") AND U0."product_id" =
> ("aces_pies_data_product"."id"))) = true
>
> #With no "true" 350MS
> SELECT COUNT(*)
> FROM "aces_pies_data_product"
> WHERE EXISTS(SELECT 1 FROM "aces_pies_data_productfitment" U0 INNER JOIN
> "django_vehiclefitment_vehicle" U1 ON (U0."vehicle_id" = U1."id") INNER
> JOIN "django_vehiclefitment_vehiclemake" U2 ON (U1."make_id" = U2."id")
> INNER JOIN "django_vehiclefitment_vehiclemodel" U3 ON (U1."model_id" =
> U3."id") WHERE (U2."name" = 'Chevrolet' AND U3."name" = 'Corvette' AND
> (2008 BETWEEN U0."start_year" AND U0."end_year" OR 2009 BETWEEN
> U0."start_year" AND U0."end_year" OR 2010 BETWEEN U0."start_year" AND
> U0."end_year" OR 2011 BETWEEN U0."start_year" AND U0."end_year" OR 2012
> BETWEEN U0."start_year" AND U0."end_year" OR 2013 BETWEEN U0."start_year"
> AND U0."end_year") AND U0."product_id" =
> ("aces_pies_data_product"."id")))
>
> }}}

New description:

 I am trying to optimize my exists filters, since on large record sets I
 noticed some slowness.  I have narrowed down part of it to the "= true"
 portion of the "Exists(...)" subquery.  Removing the "= true" gives a
 noticeable performance boost

 Also, it would be really nice to be able to just filter and not have a
 field inside the select portion of the query.  When I use the Paginator
 class for pagination, it turns the count query into one big subquery and
 adds all my Exists filters as select fields and thus performs a Group BY
 on each of them, drastically slowing down my count query.  Only having the
 Exists in the where clause would be very nice.  Is there a way to do this
 already?


 Sample django code

 {{{
 fits_vehicle = ProductFitment.objects.filter_vehicle(2008, 2013,
 'Chevrolet', 'Corvette').filter(product=OuterRef('pk')).only("pk")
 queryset =
 queryset.annotate(fits_vehicle=Exists(fits_vehicle)).filter(fits_vehicle=True)

 # With exists = true 500ms
 SELECT COUNT(*)
 FROM "aces_pies_data_product"
 WHERE EXISTS(SELECT 1 FROM "aces_pies_data_productfitment" U0 INNER JOIN
 "django_vehiclefitment_vehicle" U1 ON (U0."vehicle_id" = U1."id") INNER
 JOIN "django_vehiclefitment_vehiclemake" U2 ON (U1."make_id" = U2."id")
 INNER JOIN "django_vehiclefitment_vehiclemodel" U3 ON (U1."model_id" =
 U3."id") WHERE (U2."name" = 'Chevrolet' AND U3."name" = 'Corvette' AND
 (2008 BETWEEN U0."start_year" AND U0."end_year" OR 2009 BETWEEN
 U0."start_year" AND U0."end_year" OR 2010 BETWEEN U0."start_year" AND
 U0."end_year" OR 2011 BETWEEN U0."start_year" AND U0."end_year" OR 2012
 BETWEEN U0."start_year" AND U0."end_year" OR 2013 BETWEEN U0."start_year"
 AND U0."end_year") AND U0."product_id" = ("aces_pies_data_product"."id")))
 = true

 #With no "true" 350MS
 SELECT COUNT(*)
 FROM "aces_pies_data_product"
 WHERE EXISTS(SELECT 1 FROM "aces_pies_data_productfitment" U0 INNER JOIN
 "django_vehiclefitment_vehicle" U1 ON (U0."vehicle_id" = U1."id") INNER
 JOIN "django_vehiclefitment_vehiclemake" U2 ON (U1."make_id" = U2."id")
 INNER JOIN "django_vehiclefitment_vehiclemodel" U3 ON (U1."model_id" =
 U3."id") WHERE (U2."name" = 'Chevrolet' AND U3."name" = 'Corvette' AND
 (2008 BETWEEN U0."start_year" AND U0."end_year" OR 2009 BETWEEN
 U0."start_year" AND U0."end_year" OR 2010 BETWEEN U0."start_year" AND
 U0."end_year" OR 2011 BETWEEN U0."start_year" AND U0."end_year" OR 2012
 BETWEEN U0."start_year" AND U0."end_year" OR 2013 BETWEEN U0."start_year"
 AND U0."end_year") AND U0."product_id" = ("aces_pies_data_product"."id")))

 }}}


 **Edit**
 Poked around some of the annotation source code and got a work around

 To remove the annotations from my selects and thus drastically increase
 the count(*) query speed, I just did
 products.query.annotation_select.pop('fits_vehicle').  This correctly
 filters without actually selecting, big improvement in speed!

--

-- 
Ticket URL: <https://code.djangoproject.com/ticket/29577#comment:3>
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 post to this group, send email to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/066.49c9bbaa5b49eef4320e4d4ce58b0021%40djangoproject.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to