Query planner making bad decisions - Mailing list pgsql-performance

From Cory Coager
Subject Query planner making bad decisions
Date
Msg-id 24433_1242075795_4A089293_24433_141_1_4A089293.8070806@handel.davisvision.com
Whole thread Raw
Responses Re: Query planner making bad decisions
Re: Query planner making bad decisions
List pgsql-performance
I'm running version 8.1.11 on SLES 10 SP2.  I'm trying to improve this
query and unfortunately I cannot change the application.  For some
reason the planner is making a bad decision sometimes after an analyze
of table objectcustomfieldvalues.


The query is:
SELECT DISTINCT main.* FROM Tickets main JOIN CustomFields
CustomFields_1  ON ( CustomFields_1.Name = 'QA Origin' ) JOIN
CustomFields CustomFields_3  ON (CustomFields_3.Name = 'QA Group Code' )
JOIN ObjectCustomFieldValues ObjectCustomFieldValues_4  ON
(ObjectCustomFieldValues_4.ObjectId = main.id ) AND (
ObjectCustomFieldValues_4.Disabled = '0' ) AND
(ObjectCustomFieldValues_4.ObjectType = 'RT::Ticket' ) AND (
ObjectCustomFieldValues_4.CustomField = CustomFields_3.id ) JOIN
ObjectCustomFieldValues ObjectCustomFieldValues_2  ON (
ObjectCustomFieldValues_2.Disabled = '0' ) AND
(ObjectCustomFieldValues_2.ObjectId = main.id ) AND (
ObjectCustomFieldValues_2.CustomField = CustomFields_1.id ) AND
(ObjectCustomFieldValues_2.ObjectType = 'RT::Ticket' )  WHERE
(main.Status != 'deleted') AND (main.Queue = '60' AND
ObjectCustomFieldValues_2.Content LIKE '%Patient Sat Survey%' AND
ObjectCustomFieldValues_4.Content LIKE'%MOT%') AND (main.EffectiveId =
main.id) AND (main.Type = 'ticket')  ORDER BY main.id ASC;


Here is the query run in 12379.816 ms:

 Unique  (cost=1560.06..1560.12 rows=1 width=181) (actual
time=12379.573..12379.642 rows=13 loops=1)
   ->  Sort  (cost=1560.06..1560.06 rows=1 width=181) (actual
time=12379.568..12379.586 rows=13 loops=1)
         Sort Key: main.id, main.effectiveid, main.queue, main."type",
main.issuestatement, main.resolution, main."owner", main.subject,
main.initialpriority, main.finalpriority, main.priority,
main.timeestimated, main.timeworked, main.status, main.timeleft,
main.told, main.starts, main.started, main.due, main.resolved,
main.lastupdatedby, main.lastupdated, main.creator, main.created,
main.disabled
         ->  Nested Loop  (cost=0.00..1560.05 rows=1 width=181) (actual
time=9081.782..12379.303 rows=13 loops=1)
               Join Filter: ("outer".objectid = "inner".id)
               ->  Nested Loop  (cost=0.00..849.90 rows=1 width=8)
(actual time=9059.881..12052.548 rows=13 loops=1)
                     Join Filter: ("outer".objectid = "inner".objectid)
                     ->  Nested Loop  (cost=0.00..424.19 rows=1 width=4)
(actual time=0.274..26.660 rows=1575 loops=1)
                           ->  Index Scan using customfields_pkey on
customfields customfields_1  (cost=0.00..16.41 rows=1 width=4) (actual
time=0.228..0.371 rows=1 loops=1)
                                 Filter: ((name)::text = 'QA Origin'::text)
                           ->  Index Scan using ticketcustomfieldvalues2
on objectcustomfieldvalues objectcustomfieldvalues_2  (cost=0.00..407.76
rows=1 width=8) (actual time=0.039..21.243 rows=1575 loops=1)
                                 Index Cond:
(objectcustomfieldvalues_2.customfield = "outer".id)
                                 Filter: ((disabled = 0) AND
((objecttype)::text = 'RT::Ticket'::text) AND ((content)::text ~~
'%Patient Sat Survey%'::text))
                     ->  Nested Loop  (cost=0.00..424.99 rows=58
width=4) (actual time=5.188..7.605 rows=18 loops=1575)
                           ->  Index Scan using customfields_pkey on
customfields customfields_3  (cost=0.00..16.41 rows=1 width=4) (actual
time=0.235..0.419 rows=1 loops=1575)
                                 Filter: ((name)::text = 'QA Group
Code'::text)
                           ->  Index Scan using ticketcustomfieldvalues2
on objectcustomfieldvalues objectcustomfieldvalues_4  (cost=0.00..407.76
rows=65 width=8) (actual time=4.947..7.130 rows=18 loops=1575)
                                 Index Cond:
(objectcustomfieldvalues_4.customfield = "outer".id)
                                 Filter: ((disabled = 0) AND
((objecttype)::text = 'RT::Ticket'::text) AND ((content)::text ~~
'%MOT%'::text))
               ->  Index Scan using tickets1 on tickets main
(cost=0.00..709.77 rows=30 width=181) (actual time=0.020..17.104
rows=5743 loops=13)
                     Index Cond: (queue = 60)
                     Filter: (((status)::text <> 'deleted'::text) AND
(effectiveid = id) AND (("type")::text = 'ticket'::text))
 Total runtime: 12379.816 ms
(23 rows)


select attname,n_distinct from pg_stats where tablename='tickets';
     attname     | n_distinct
-----------------+------------
 id              |         -1
 effectiveid     |  -0.968462
 queue           |         37
 type            |          1
 issuestatement  |          1
 resolution      |          1
 owner           |        123
 subject         |  -0.885148
 initialpriority |         12
 finalpriority   |          9
 priority        |         43
 timeestimated   |          5
 timeworked      |          5
 status          |          6
 timeleft        |          3
 told            |  -0.128088
 starts          |         60
 started         |  -0.862352
 due             |       1270
 resolved        |   -0.94381
 lastupdatedby   |        366
 lastupdated     |   -0.98511
 creator         |       1054
 created         |  -0.965858
 disabled        |          1
(25 rows)


select attname,n_distinct from pg_stats where tablename='customfields';
    attname    | n_distinct
---------------+------------
 id            |         -1
 name          |   -0.83855
 type          |          3
 description   |  -0.202636
 sortorder     |  -0.110379
 creator       |          4
 created       |  -0.739703
 lastupdatedby |          4
 lastupdated   |   -0.78089
 disabled      |          2
 lookuptype    |          1
 repeated      |          1
 pattern       |          1
 maxvalues     |          2
(14 rows)


select attname,n_distinct from pg_stats where
tablename='objectcustomfieldvalues';
     attname     | n_distinct
-----------------+------------
 id              |         -1
 objectid        |      95048
 customfield     |        543
 content         |      30209
 creator         |        115
 created         |  -0.268605
 lastupdatedby   |        115
 lastupdated     |   -0.26863
 objecttype      |          1
 largecontent    |         -1
 contenttype     |          1
 contentencoding |          2
 sortorder       |          1
 disabled        |          2
(14 rows)



If I now analyze objectcustomfieldvalues the query now takes 51747.341 ms:

 Unique  (cost=1564.95..1565.02 rows=1 width=181) (actual
time=51747.087..51747.152 rows=13 loops=1)
   ->  Sort  (cost=1564.95..1564.95 rows=1 width=181) (actual
time=51747.083..51747.097 rows=13 loops=1)
         Sort Key: main.id, main.effectiveid, main.queue, main."type",
main.issuestatement, main.resolution, main."owner", main.subject,
main.initialpriority, main.finalpriority, main.priority,
main.timeestimated, main.timeworked, main.status, main.timeleft,
main.told, main.starts, main.started, main.due, main.resolved,
main.lastupdatedby, main.lastupdated, main.creator, main.created,
main.disabled
         ->  Nested Loop  (cost=0.00..1564.94 rows=1 width=181) (actual
time=38871.343..51746.920 rows=13 loops=1)
               Join Filter: ("inner".objectid = "outer".id)
               ->  Nested Loop  (cost=0.00..1136.77 rows=1 width=185)
(actual time=7.772..39862.238 rows=1548 loops=1)
                     Join Filter: ("outer".objectid = "inner".id)
                     ->  Nested Loop  (cost=0.00..426.63 rows=1 width=4)
(actual time=0.266..27.745 rows=1575 loops=1)
                           ->  Index Scan using customfields_pkey on
customfields customfields_1  (cost=0.00..16.41 rows=1 width=4) (actual
time=0.219..0.404 rows=1 loops=1)
                                 Filter: ((name)::text = 'QA Origin'::text)
                           ->  Index Scan using ticketcustomfieldvalues2
on objectcustomfieldvalues objectcustomfieldvalues_2  (cost=0.00..410.20
rows=1 width=8) (actual time=0.040..22.006 rows=1575 loops=1)
                                 Index Cond:
(objectcustomfieldvalues_2.customfield = "outer".id)
                                 Filter: ((disabled = 0) AND
((objecttype)::text = 'RT::Ticket'::text) AND ((content)::text ~~
'%Patient Sat Survey%'::text))
                     ->  Index Scan using tickets1 on tickets main
(cost=0.00..709.77 rows=30 width=181) (actual time=0.015..17.164
rows=5743 loops=1575)
                           Index Cond: (queue = 60)
                           Filter: (((status)::text <> 'deleted'::text)
AND (effectiveid = id) AND (("type")::text = 'ticket'::text))
               ->  Nested Loop  (cost=0.00..427.44 rows=58 width=4)
(actual time=5.207..7.646 rows=18 loops=1548)
                     ->  Index Scan using customfields_pkey on
customfields customfields_3  (cost=0.00..16.41 rows=1 width=4) (actual
time=0.242..0.434 rows=1 loops=1548)
                           Filter: ((name)::text = 'QA Group Code'::text)
                     ->  Index Scan using ticketcustomfieldvalues2 on
objectcustomfieldvalues objectcustomfieldvalues_4  (cost=0.00..410.20
rows=66 width=8) (actual time=4.958..7.154 rows=18 loops=1548)
                           Index Cond:
(objectcustomfieldvalues_4.customfield = "outer".id)
                           Filter: ((disabled = 0) AND
((objecttype)::text = 'RT::Ticket'::text) AND ((content)::text ~~
'%MOT%'::text))
 Total runtime: 51747.341 ms
(23 rows)


select attname,n_distinct from pg_stats where
tablename='objectcustomfieldvalues';
     attname     | n_distinct
-----------------+------------
 id              |         -1
 objectid        |      95017
 customfield     |        539
 content         |      30287
 creator         |        114
 created         |  -0.268403
 lastupdatedby   |        114
 lastupdated     |  -0.268809
 objecttype      |          1
 largecontent    |         -1
 contenttype     |          1
 contentencoding |          2
 sortorder       |          1
 disabled        |          2
(14 rows)


Even better yet, if I turn off enable_nestloop the query runs in
3499.970 ms:

 Unique  (cost=53860.11..53860.18 rows=1 width=181) (actual
time=3499.614..3499.684 rows=13 loops=1)
   ->  Sort  (cost=53860.11..53860.11 rows=1 width=181) (actual
time=3499.608..3499.627 rows=13 loops=1)
         Sort Key: main.id, main.effectiveid, main.queue, main."type",
main.issuestatement, main.resolution, main."owner", main.subject,
main.initialpriority, main.finalpriority, main.priority,
main.timeestimated, main.timeworked, main.status, main.timeleft,
main.told, main.starts, main.started, main.due, main.resolved,
main.lastupdatedby, main.lastupdated, main.creator, main.created,
main.disabled
         ->  Hash Join  (cost=27240.41..53860.10 rows=1 width=181)
(actual time=3429.166..3499.538 rows=13 loops=1)
               Hash Cond: ("outer".objectid = "inner".id)
               ->  Merge Join  (cost=0.00..26619.39 rows=58 width=4)
(actual time=1666.503..1736.814 rows=18 loops=1)
                     Merge Cond: ("outer".id = "inner".customfield)
                     ->  Index Scan using customfields_pkey on
customfields customfields_3  (cost=0.00..16.41 rows=1 width=4) (actual
time=0.221..0.410 rows=1 loops=1)
                           Filter: ((name)::text = 'QA Group Code'::text)
                     ->  Index Scan using ticketcustomfieldvalues2 on
objectcustomfieldvalues objectcustomfieldvalues_4  (cost=0.00..26514.04
rows=35342 width=8) (actual time=98.035..1736.277 rows=44 loops=1)
                           Filter: ((disabled = 0) AND
((objecttype)::text = 'RT::Ticket'::text) AND ((content)::text ~~
'%MOT%'::text))
               ->  Hash  (cost=27240.40..27240.40 rows=1 width=185)
(actual time=1762.572..1762.572 rows=1548 loops=1)
                     ->  Hash Join  (cost=26530.47..27240.40 rows=1
width=185) (actual time=1728.887..1758.147 rows=1548 loops=1)
                           Hash Cond: ("outer".id = "inner".objectid)
                           ->  Index Scan using tickets1 on tickets
main  (cost=0.00..709.77 rows=30 width=181) (actual time=0.024..17.550
rows=5743 loops=1)
                                 Index Cond: (queue = 60)
                                 Filter: (((status)::text <>
'deleted'::text) AND (effectiveid = id) AND (("type")::text =
'ticket'::text))
                           ->  Hash  (cost=26530.47..26530.47 rows=1
width=4) (actual time=1728.787..1728.787 rows=1575 loops=1)
                                 ->  Merge Join  (cost=0.00..26530.47
rows=1 width=4) (actual time=1493.343..1726.020 rows=1575 loops=1)
                                       Merge Cond: ("outer".id =
"inner".customfield)
                                       ->  Index Scan using
customfields_pkey on customfields customfields_1  (cost=0.00..16.41
rows=1 width=4) (actual time=0.237..0.429 rows=1 loops=1)
                                             Filter: ((name)::text = 'QA
Origin'::text)
                                       ->  Index Scan using
ticketcustomfieldvalues2 on objectcustomfieldvalues
objectcustomfieldvalues_2  (cost=0.00..26514.04 rows=1 width=8) (actual
time=1493.091..1721.155 rows=1575 loops=1)
                                             Filter: ((disabled = 0) AND
((objecttype)::text = 'RT::Ticket'::text) AND ((content)::text ~~
'%Patient Sat Survey%'::text))
 Total runtime: 3499.970 ms
(25 rows)




Is there anything I can do to improve this?


~Cory Coager



------------------------------------------------------------------------
The information contained in this communication is intended
only for the use of the recipient(s) named above. It may
contain information that is privileged or confidential, and
may be protected by State and/or Federal Regulations. If
the reader of this message is not the intended recipient,
you are hereby notified that any dissemination,
distribution, or copying of this communication, or any of
its contents, is strictly prohibited. If you have received
this communication in error, please return it to the sender
immediately and delete the original message and any copy
of it from your computer system. If you have any questions
concerning this message, please contact the sender.
------------------------------------------------------------------------


pgsql-performance by date:

Previous
From: Aidan Van Dyk
Date:
Subject: Re: Any better plan for this query?..
Next
From: Dimitri
Date:
Subject: Re: Any better plan for this query?..