Strange query optimization in 7.3.2 - Mailing list pgsql-general

From Alec Mitchell
Subject Strange query optimization in 7.3.2
Date
Msg-id 200304111516.44382.apm13@columbia.edu
Whole thread Raw
Responses Re: Strange query optimization in 7.3.2  (Tom Lane <tgl@sss.pgh.pa.us>)
List pgsql-general
Hello,

    I've encountered what seems to be a very strange behavior in the query
optimizer using postgresql 7.3.2.  Using a query that looks like this:

EXPLAIN ANALYZE SELECT * from (trucks tr JOIN terminals t ON (t.terminal =
tr.terminal) JOIN  manifests m ON (tr.trailer = m.trailer)) JOIN stops s ON
(m.manifest = s.manifest) WHERE ((tr.group_num = 1) AND (t.city_id = 2) AND
(s.date BETWEEN '1/1/2003' AND '1/31/2003'));

I get a somewhat slow query with a plan that is not terrbily optimal.

However when I accidentally made the query look like this:

EXPLAIN ANALYZE SELECT * from (trucks tr JOIN terminals t ON (t.terminal =
tr.terminal) JOIN  manifests m ON (tr.trailer = m.trailer)) JOIN stops s ON
(m.manifest = s.manifest) WHERE ((s.manifest = m.manifest) AND (tr.trailer =
m.trailer) AND (t.terminal = tr.terminal) AND (tr.group_num = 1) AND
(t.city_id = 2) AND (s.date BETWEEN '1/1/2003' AND '1/31/2003'));

The query plan choosen is more than three times as fast.  This is especially
strange considering it is identical to the first query apart from redundant
information for the joins.

If I change the JOIN ON to NATURAL JOIN the plan chosen is the the slow one,
unless of course the redundant information is included as well.  Using
non-explicit joins also results in the slow query plan.  The redundant
information has a drastic effect on the query plan, but makes no change to
the results.  Does anyone have any idea why this might be?

Here are the results of EXPLAIN ANALYZE for the two queries:

The Slow query:

 Hash Join  (cost=202.44..7332.06 rows=9711 width=140) (actual
time=21.20..6567.87 rows=19775 loops=1)
   Hash Cond: ("outer".manifest = "inner".manifest)
   ->  Seq Scan on stops s  (cost=0.00..6421.15 rows=117416 width=88) (actual
time=0.18..4811.78 rows=118606 loops=1)
         Filter: ((date >= '2003-01-01'::date) AND (date <=
'2003-01-31'::date))
   ->  Hash  (cost=199.64..199.64 rows=1119 width=52) (actual
time=20.54..20.54 rows=0 loops=1)
         ->  Merge Join  (cost=21.86..199.64 rows=1119 width=52) (actual
time=14.72..20.14 rows=52 loops=1)
               Merge Cond: ("outer".trailer = "inner".trailer)
               ->  Index Scan using manifests_trailer_idx on manifests m
(cost=0.00..516.82 rows=13526 width=15) (actual time=0.41..4.25 rows=174
loops=1)
               ->  Sort  (cost=21.86..22.01 rows=62 width=37) (actual
time=10.86..11.01 rows=52 loops=1)
                     Sort Key: tr.trailer
                     ->  Hash Join  (cost=1.47..20.01 rows=62 width=37)
(actual time=1.63..9.49 rows=52 loops=1)
                           Hash Cond: ("outer".terminal = "inner".terminal)
                           ->  Seq Scan on trucks tr  (cost=0.00..15.38
rows=319 width=21) (actual time=0.05..5.91 rows=319 loops=1)
                                 Filter: (group_num = 1)
                           ->  Hash  (cost=1.45..1.45 rows=7 width=16) (actual
time=0.32..0.32 rows=0 loops=1)
                                 ->  Seq Scan on terminals t  (cost=0.00..1.45
rows=7 width=16) (actual time=0.06..0.27 rows=7 loops=1)
                                       Filter: (city_id = 2)
 Total runtime: 6635.96 msec


The faster (redundant) Query:

 Nested Loop  (cost=21.86..6496.24 rows=9711 width=140) (actual
time=14.90..2045.87 rows=19775 loops=1)
   ->  Merge Join  (cost=21.86..199.64 rows=1119 width=52) (actual
time=14.67..32.01 rows=52 loops=1)
         Merge Cond: ("outer".trailer = "inner".trailer)
         ->  Index Scan using manifests_trailer_idx on manifests m
(cost=0.00..516.82 rows=13526 width=15) (actual time=0.40..4.47 rows=174
loops=1)
         ->  Sort  (cost=21.86..22.01 rows=62 width=37) (actual
time=10.80..11.02 rows=52 loops=1)
               Sort Key: tr.trailer
               ->  Hash Join  (cost=1.47..20.01 rows=62 width=37) (actual
time=1.49..9.42 rows=52 loops=1)
                     Hash Cond: ("outer".terminal = "inner".terminal)
                     ->  Seq Scan on trucks tr  (cost=0.00..15.38 rows=319
width=21) (actual time=0.04..5.88 rows=319 loops=1)
                           Filter: (group_num = 1)
                     ->  Hash  (cost=1.45..1.45 rows=7 width=16) (actual
time=0.31..0.31 rows=0 loops=1)
                           ->  Seq Scan on terminals t  (cost=0.00..1.45
rows=7 width=16) (actual time=0.06..0.26 rows=7 loops=1)
                                 Filter: (city_id = 2)
   ->  Index Scan using stops_mainfest_date_idx on stops s  (cost=0.00..5.62
rows=1 width=88) (actual time=0.13..17.13 rows=380 loops=52)
         Index Cond: (("outer".manifest = s.manifest) AND (s.manifest =
"outer".manifest) AND (s.date >= '2003-01-01'::date) AND (s.date <=
'2003-01-31'::date))
 Total runtime: 2123.36 msec

As you can see the planner estimates are all the same for the operations
common to both plans, but a different choice is made for the final (big)
join.  Thanks in advance for any enlightenment on this strange issue.  I'd
rather not be using such a kludgy query to optimize things unless I have no
other option.

Alec Mitchell


pgsql-general by date:

Previous
From: h8dk97@rocketmail.com (Jerry)
Date:
Subject: Two languages in the database: English and Russian
Next
From: h8dk97@rocketmail.com (Jerry)
Date:
Subject: Need two languages in the database: Russian and English