Run on a 8.2.3 server: Aggregate (C=6832..6832 R=1) (AT=1665..1665 R=1 L=1) ->Hash Join (C=5007..6832 R=50) (AT=1543..1657 R=9340 L=1) Hash Cond: ("substring"((a.order_number)::text, '^[^.]+'::text) = (a.base_order)::text) ->GroupAggregate (C=2519..4194 R=10000) (AT=101..158 R=10000 L=1) ->Sort (C=2519..2544 R=10000) (AT=101..109 R=10001 L=1) Sort Key: o.order_number, (** many other "o" columns) ->Merge Left Join (C=0..1855 R=10000) (AT=0..61 R=10001 L=1) Merge Cond: ((o.order_number)::text = (ol.order_number)::text) ->Index Scan using orders_smaller_i1 on orders_smaller o (C=0..1147 R=10000) (AT=0..12 R=10000 L=1) ->Index Scan using orderlines_smaller_i4 on orderlines_smaller ol (C=0..577 R=8440) (AT=0..10 R=8435 L=1) ->Hash (C=2487..2487 R=1) (AT=1442..1442 R=9247 L=1) ->Merge Join (cost=2487.46..2487.49 rows=1 width=36) (actual time=1376.130..1431.848 rows=9247 loops=1) Merge Cond: ("outer"."?column2?" = "inner"."?column2?") ->Sort (C=1405..1405 R=1) (AT=1186..1193 R=9247 L=1) Sort Key: (a.base_order)::text ->Subquery Scan a (cost=1405.91..1405.94 rows=1 width=18) (actual time=1112.757..1135.560 rows=9247 loops=1) ->HashAggregate (C=1405..1405 R=1) (AT=1112..1121 R=9247 L=1) ->Nested Loop Left Join (C=18..1405 R=5) (AT=0..1095 R=9248 L=1) ->Nested Loop Left Join (C=18..1404 R=5) (AT=0..1017 R=9247 L=1) Join Filter: ((o.order_number)::text ~ (('^'::text || (m.order_number)::text) || '(\\.[0-9.]+)?$'::text)) ->Seq Scan on orders_smaller m (C=0..742 R=1) (AT=0..11 R=9247 L=1) Filter: ((order_number)::text !~~ '%.%'::text) ->Bitmap Heap Scan on orders_smaller o (C=18..634 R=1111) (AT=0..0 R=1 L=9247) Recheck Cond: (((o.order_number)::text >= (m.order_number)::text) AND ((o.order_number)::text <= ((m.order_number)::text || '/'::text))) Filter: (((status)::text <> 'split'::text) AND ((status)::text <> 'canceled'::text) AND ((status)::text <> 'incomplete'::text) AND ((status)::text <> 'fraud'::text)) ->Bitmap Index Scan on orders_smaller_i1 (C=0..17 R=1111) (AT=0..0 R=1 L=9247) Index Cond: (((o.order_number)::text >= (m.order_number)::text) AND ((o.order_number)::text <= ((m.order_number)::text || '/'::text))) ->Index Scan using orderlines_smaller_i4 on orderlines_smaller ol (C=0..0 R=1) (AT=0..0 R=0 L=9247) Index Cond: ((o.order_number)::text = (ol.order_number)::text) ->Sort (C=1081..1081 R=1) (AT=189..196 R=9247 L=1) Sort Key: (b.base_order)::text ->Subquery Scan b (cost=1081.46..1081.50 rows=1 width=18) (actual time=116.554..139.130 rows=9247 loops=1) ->HashAggregate (C=1081..1081 R=1) (AT=116..125 R=9247 L=1) ->Nested Loop Left Join (C=17..1081 R=3) (AT=0..101 R=9247 L=1) Join Filter: ((p.order_number)::text ~ (('^'::text || (m.order_number)::text) || '(\\.[0-9.]+)?$'::text)) ->Seq Scan on orders_smaller m (C=0..742 R=1) (AT=0..10 R=9247 L=1) Filter: ((order_number)::text !~~ '%.%'::text) ->Bitmap Heap Scan on payments_smaller p (C=17..311 R=1111) (AT=0..0 R=0 L=9247) Recheck Cond: (((p.order_number)::text >= (m.order_number)::text) AND ((p.order_number)::text <= ((m.order_number)::text || '/'::text))) Filter: *** ((transaction_type IN ('A','B','C','D') AND (amount <> 0::numeric)) ->Bitmap Index Scan on payments_smaller_i2 (C=0..17 R=1111) (AT=0..0 R=0 L=9247) Index Cond: (((p.order_number)::text >= (m.order_number)::text) AND ((p.order_number)::text <= ((m.order_number)::text || '/'::text))) Total runtime: 1666.259 ms