Thread: BUG #13827: planner chooses more expensive plan than it should

BUG #13827: planner chooses more expensive plan than it should

From
michal.schwarz@gmail.com
Date:
The following bug has been logged on the website:

Bug reference:      13827
Logged by:          Michal Schwarz
Email address:      michal.schwarz@gmail.com
PostgreSQL version: 9.2.14
Operating system:   CentOS release 6.5 (Final), x86_64
Description:

Correct behaviour:

=> explain select 1 FROM nakupy_prodeje n, smlouvy s WHERE
n.smlouva_id=s.smlouva_id AND s.osoba_id='900316';
                                                   QUERY PLAN
-----------------------------------------------------------------------------------------------------------------
 Nested Loop  (cost=0.00..18415.76 rows=88 width=0)
   ->  Index Scan using smlouvy_osoba_id on smlouvy s  (cost=0.00..678.38
rows=170 width=4)
         Index Cond: (osoba_id = 900316)
   ->  Index Only Scan using nakupy_prodeje_smlouva_id on nakupy_prodeje n
(cost=0.00..100.99 rows=335 width=4)
         Index Cond: (smlouva_id = s.smlouva_id)
(5 rows)

Total expected cost is 18415 and 88 rows. OK.

Problematic behaviour is when I just add "AND n.datum_realizace is null" to
original WHERE condition. This query should be at least as fast as previous
query, because everything is absolutely the same, and only an ADDITIONAL
condition "AND n.datum_realizace is null" was used.

But PostgreSQL chooses much expensive query plan this time, even when it
could just use plan similar to previous with only additional filtering
applied to original 88 expected rows:

=> explain select 1 FROM nakupy_prodeje n, smlouvy s WHERE
n.smlouva_id=s.smlouva_id AND s.osoba_id='900316' AND n.datum_realizace is
null;
                                                      QUERY PLAN
----------------------------------------------------------------------------------------------------------------------
 Hash Join  (cost=680.51..42187.98 rows=3 width=0)
   Hash Cond: (n.smlouva_id = s.smlouva_id)
   ->  Index Scan using nakupy_prodeje_datum_realizace on nakupy_prodeje n
(cost=0.00..40380.37 rows=300551 width=4)
         Index Cond: (datum_realizace IS NULL)
   ->  Hash  (cost=678.38..678.38 rows=170 width=4)
         ->  Index Scan using smlouvy_osoba_id on smlouvy s
(cost=0.00..678.38 rows=170 width=4)
               Index Cond: (osoba_id = 900316)
(7 rows)

Why did PG choose a plan with cost=41287, when it could use much simpler
plan based on previous example?

Or, more specifically, why is PG using Index Cond for "datum_realizace IS
NULL" with 300000 resulting expected rows, when it could apparently use
simpler plan.

For me, it is like a difference between optimal:
  "how to count all people in room?" => "let's look at them ... yes, there
are 5 people here"
and extremely sub-optimal:
  "how to count all people WITH BEARD in room?" => "let's count ALL THE
PEOPLE IN THE TOWN (there will be approx. 300000 of them), then find out all
WITH BEARD, and finally look whether any of them is in this room".
:-)

Table structures are:

=> \d nakupy_prodeje
                                                    Table
"public.nakupy_prodeje"
          Column           |            Type             |
              Modifiers

---------------------------+-----------------------------+----------------------------------------------------------------------------
 prikaz_id                 | integer                     | not null default
nextval(('nakupy_prodeje_prikaz_id_seq'::text)::regclass)
 smlouva_id                | integer                     | not null
 datum_realizace           | timestamp without time zone |
Indexes:
    "nakupy_prodeje_pkey" PRIMARY KEY, btree (prikaz_id)
    "nakupy_prodeje_datum_realizace" btree (datum_realizace)
    "nakupy_prodeje_smlouva_id" btree (smlouva_id)

=> \d smlouvy
                                                           Table
"public.smlouvy"
                Column                 |            Type             |
                       Modifiers

---------------------------------------+-----------------------------+----------------------------------------------------------------------
 smlouva_id                            | integer                     | not
null default nextval(('smlouvy_smlouva_id_seq'::text)::regclass)
 osoba_id                              | integer                     | not
null
Indexes:
    "smlouvy_pkey" PRIMARY KEY, btree (smlouva_id)
    "smlouvy_osoba_id" btree (osoba_id)


And ANALYZE was run on these tables.

Re: BUG #13827: planner chooses more expensive plan than it should

From
Pavel Stehule
Date:
Hi

2015-12-18 15:33 GMT+01:00 <michal.schwarz@gmail.com>:

> The following bug has been logged on the website:
>
> Bug reference:      13827
> Logged by:          Michal Schwarz
> Email address:      michal.schwarz@gmail.com
> PostgreSQL version: 9.2.14
> Operating system:   CentOS release 6.5 (Final), x86_64
> Description:
>
> Correct behaviour:
>
> => explain select 1 FROM nakupy_prodeje n, smlouvy s WHERE
> n.smlouva_id=s.smlouva_id AND s.osoba_id='900316';
>                                                    QUERY PLAN
>
> -----------------------------------------------------------------------------------------------------------------
>  Nested Loop  (cost=0.00..18415.76 rows=88 width=0)
>    ->  Index Scan using smlouvy_osoba_id on smlouvy s  (cost=0.00..678.38
> rows=170 width=4)
>          Index Cond: (osoba_id = 900316)
>    ->  Index Only Scan using nakupy_prodeje_smlouva_id on nakupy_prodeje n
> (cost=0.00..100.99 rows=335 width=4)
>          Index Cond: (smlouva_id = s.smlouva_id)
> (5 rows)
>
> Total expected cost is 18415 and 88 rows. OK.
>
> Problematic behaviour is when I just add "AND n.datum_realizace is null" to
> original WHERE condition. This query should be at least as fast as previous
> query, because everything is absolutely the same, and only an ADDITIONAL
> condition "AND n.datum_realizace is null" was used.
>
> But PostgreSQL chooses much expensive query plan this time, even when it
> could just use plan similar to previous with only additional filtering
> applied to original 88 expected rows:
>
> => explain select 1 FROM nakupy_prodeje n, smlouvy s WHERE
> n.smlouva_id=s.smlouva_id AND s.osoba_id='900316' AND n.datum_realizace is
> null;
>                                                       QUERY PLAN
>
>
----------------------------------------------------------------------------------------------------------------------
>  Hash Join  (cost=680.51..42187.98 rows=3 width=0)
>    Hash Cond: (n.smlouva_id = s.smlouva_id)
>    ->  Index Scan using nakupy_prodeje_datum_realizace on nakupy_prodeje n
> (cost=0.00..40380.37 rows=300551 width=4)
>          Index Cond: (datum_realizace IS NULL)
>    ->  Hash  (cost=678.38..678.38 rows=170 width=4)
>          ->  Index Scan using smlouvy_osoba_id on smlouvy s
> (cost=0.00..678.38 rows=170 width=4)
>                Index Cond: (osoba_id = 900316)
> (7 rows)
>

please, send output of EXPLAIN ANALYZE

Regards

Pavel


>
> Why did PG choose a plan with cost=41287, when it could use much simpler
> plan based on previous example?
>
> Or, more specifically, why is PG using Index Cond for "datum_realizace IS
> NULL" with 300000 resulting expected rows, when it could apparently use
> simpler plan.
>
> For me, it is like a difference between optimal:
>   "how to count all people in room?" => "let's look at them ... yes, there
> are 5 people here"
> and extremely sub-optimal:
>   "how to count all people WITH BEARD in room?" => "let's count ALL THE
> PEOPLE IN THE TOWN (there will be approx. 300000 of them), then find out
> all
> WITH BEARD, and finally look whether any of them is in this room".
> :-)
>
> Table structures are:
>
> => \d nakupy_prodeje
>                                                     Table
> "public.nakupy_prodeje"
>           Column           |            Type             |
>               Modifiers
>
>
---------------------------+-----------------------------+----------------------------------------------------------------------------
>  prikaz_id                 | integer                     | not null default
> nextval(('nakupy_prodeje_prikaz_id_seq'::text)::regclass)
>  smlouva_id                | integer                     | not null
>  datum_realizace           | timestamp without time zone |
> Indexes:
>     "nakupy_prodeje_pkey" PRIMARY KEY, btree (prikaz_id)
>     "nakupy_prodeje_datum_realizace" btree (datum_realizace)
>     "nakupy_prodeje_smlouva_id" btree (smlouva_id)
>
> => \d smlouvy
>                                                            Table
> "public.smlouvy"
>                 Column                 |            Type             |
>                        Modifiers
>
>
---------------------------------------+-----------------------------+----------------------------------------------------------------------
>  smlouva_id                            | integer                     | not
> null default nextval(('smlouvy_smlouva_id_seq'::text)::regclass)
>  osoba_id                              | integer                     | not
> null
> Indexes:
>     "smlouvy_pkey" PRIMARY KEY, btree (smlouva_id)
>     "smlouvy_osoba_id" btree (osoba_id)
>
>
> And ANALYZE was run on these tables.
>
>
>
>
>
> --
> Sent via pgsql-bugs mailing list (pgsql-bugs@postgresql.org)
> To make changes to your subscription:
> http://www.postgresql.org/mailpref/pgsql-bugs
>

Re: BUG #13827: planner chooses more expensive plan than it should

From
Tom Lane
Date:
michal.schwarz@gmail.com writes:
> => explain select 1 FROM nakupy_prodeje n, smlouvy s WHERE
> n.smlouva_id=s.smlouva_id AND s.osoba_id='900316';
>                                                    QUERY PLAN
> -----------------------------------------------------------------------------------------------------------------
>  Nested Loop  (cost=0.00..18415.76 rows=88 width=0)
>    ->  Index Scan using smlouvy_osoba_id on smlouvy s  (cost=0.00..678.38
> rows=170 width=4)
>          Index Cond: (osoba_id = 900316)
>    ->  Index Only Scan using nakupy_prodeje_smlouva_id on nakupy_prodeje n
> (cost=0.00..100.99 rows=335 width=4)
>          Index Cond: (smlouva_id = s.smlouva_id)
> (5 rows)

> Total expected cost is 18415 and 88 rows. OK.

> Problematic behaviour is when I just add "AND n.datum_realizace is null" to
> original WHERE condition. This query should be at least as fast as previous
> query, because everything is absolutely the same, and only an ADDITIONAL
> condition "AND n.datum_realizace is null" was used.

But datum_realizace is not in the nakupy_prodeje_smlouva_id index.  So it
could not have done this "exactly the same"; it would have had to have
used a regular index scan, which is a whole lot more expensive than the
index-only scan because it involves fetching heap tuples too.  Evidently
the planner thinks the hash join is a better option than that.

If you try turning off enable_hashjoin and enable_mergejoin, you'll
probably get the nestloop/indexscan plan, and you'll be able to see
what cost the planner is assigning to it; but it will certainly be
higher than for the plan that got selected.

            regards, tom lane