First of all, my local system which has postgres_fdw installed is this
version:
PostgreSQL 9.6.8 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 4.9.3, 64-bit

Here are my server configurations (I have tweaked these with no improvement
to my issue):

   - fetch_size=100
   - fdw_startup_cost=1000
   - fdw_tuple_cost=100000
   - use_remote_estimate=true

I am seeing some really odd behavior and I'm not sure how I can most easily
help our users to write queries that actually hit an index scan on the
remote server.  In one example, I have:

   - a temp table with 33 rows
   - text instead of integer data type - which the remote table's data type
   is
   - it is freshly analyzed

Joining to the table, doing id IN (list), or doing EXISTS all yield a full
remote table scan:

select *
from remote.customers c
inner join abc t on t.customer_id::int=c.customer_id;

select *
from remote.customers c
where c.customer_id in (SELECT customer_id::int FROM abc);

select *
from remote.customers c
where exists (SELECT 1 FROM abc WHERE customer_id::int = c.customer_id);

                                         QUERY PLAN
---------------------------------------------------------------------------------------------
 Hash Join  (cost=1002.47..1170208178867.64 rows=5851034 width=3113)
   Hash Cond: (c.customer_id = (abc.customer_id)::integer)
   ->  Foreign Scan on customers c  (cost=1000.43..1170208089344.77
rows=11702069 width=902)
   ->  Hash  (cost=1.69..1.69 rows=28 width=7)
         ->  HashAggregate  (cost=1.41..1.69 rows=28 width=7)
               Group Key: (abc.customer_id)::integer
               ->  Seq Scan on abc  (cost=0.00..1.33 rows=33 width=7)
(7 rows)

However, I can get the index scan 2 ways:

   - converting the temp table data type to int
   - keeping the data type as is, but adding DISTINCT to the IN list

Neither of these make any sense to me because the planner knows there are
only 33 rows in the temp table.  It should always do an index scan.  And
why should converting the data type to int be any different than casting it
to int?  I understand the planner stats on that field are not for an
integer, but it's still only 33 rows.

So these 2 versions get a good plan and actually run very quickly:

select *
from remote.customers c
where c.customer_id in (SELECT DISTINCT customer_id::int FROM abc);

WITH distinctified AS (SELECT DISTINCT customer_id::int FROM abc)

select *
from remote.customers c
INNER JOIN distinctified t on t.customer_id::int=c.customer_id;

                                  QUERY PLAN
-------------------------------------------------------------------------------
 Nested Loop  (cost=1002.03..5628140.32 rows=5851034 width=3113)
   ->  HashAggregate  (cost=1.58..2.00 rows=28 width=4)
         Group Key: (abc.customer_id)::integer
         ->  Seq Scan on abc  (cost=0.00..1.50 rows=33 width=4)
   ->  Foreign Scan on customers c  (cost=1000.45..201004.91 rows=2
width=902)
(5 rows)


Likewise if I alter the type it works without any special treatment:

ALTER TABLE abc ALTER COLUMN customer_id TYPE int USING customer_id::INT;
ANALYZE abc;

EXPLAIN
select *
from remote.customers c
inner join abc t using (customer_id);

                                  QUERY PLAN
-------------------------------------------------------------------------------
 Nested Loop  (cost=1000.45..6633164.02 rows=1930841 width=3194)
   ->  Seq Scan on abc t  (cost=0.00..1.33 rows=33 width=85)
   ->  Foreign Scan on customers c  (cost=1000.45..201004.91 rows=2
width=902)
(3 rows)

Any insight appreciated!

Thanks,
Jeremy

Reply via email to