Dump, query and the EXPLAINs are below.
Here is the simplified reproduction script:
DROP TABLE IF EXISTS a,b CASCADE;
CREATE TEMP TABLE a (x numeric, y numeric);
CREATE TEMP TABLE b (x numeric);
INSERT INTO a (x,y) SELECT random(), random() FROM generate_series(1,1E6);
INSERT INTO b (x) SELECT random() FROM generate_series(1,1E6);
CREATE INDEX b_idx ON b (x);
VACUUM ANALYZE a,b;
-- Current state of the problem
EXPLAIN (ANALYZE, COSTS OFF, TIMING OFF, BUFFERS OFF)
SELECT * FROM a LEFT JOIN b ON (a.x = b.x AND a.y < 0.1)
ORDER BY a.x LIMIT 10;
-- An emulation of potentially more effective 'Row Goal' feature
-- (not semantically correct, just to touch the sky)
EXPLAIN (ANALYZE, COSTS OFF, TIMING OFF, BUFFERS OFF)
SELECT * FROM (SELECT x,y FROM a ORDER BY x LIMIT 10) AS q(x,y) LEFT JOIN b ON (q.x = b.x AND q.y < 0.1)
ORDER BY q.x LIMIT 10;
What do we see:
Limit (actual rows=10 loops=1)
-> Sort (actual rows=10 loops=1)
Sort Key: a.x
Sort Method: top-N heapsort Memory: 26kB
-> Hash Left Join (actual rows=1000000 loops=1)
Hash Cond: (a.x = b.x)
Join Filter: (a.y < 0.1)
-> Seq Scan on a (actual rows=1000000 loops=1)
-> Hash (actual rows=1000000 loops=1)
Buckets: 262144 Batches: 8 Memory Usage: 7297kB
-> Seq Scan on b (actual rows=1000000 loops=1)
Planning Time: 0.221 ms
Execution Time: 998.139 ms
(13 rows)
Limit (actual rows=10 loops=1)
-> Nested Loop Left Join (actual rows=10 loops=1)
Join Filter: (a.y < 0.1)
-> Limit (actual rows=10 loops=1)
-> Sort (actual rows=10 loops=1)
Sort Key: a.x
Sort Method: top-N heapsort Memory: 26kB
-> Seq Scan on a (actual rows=1000000 loops=1)
-> Index Only Scan using b_idx on b (actual rows=0 loops=10)
Index Cond: (x = a.x)
Heap Fetches: 0
Planning Time: 0.236 ms
Execution Time: 157.943 ms
(13 rows)
Who chooses ‘SORT_TYPE_TOP_N_HEAPSORT’? state->boundUsed, sort_bounded_heap, make_bounded_heap, ExecSetTupleBound
0001-Skip-inner-scan-in-NestLoop-Left-Join-when-join-clau.patch