Run on an 8.2.5 server, with recent patches. Same results when run on unpatched 8.2.5, and on 8.2.4: Aggregate (C=6832..6832 R=1) (AT=144302..144302 R=1 L=1) ->Hash Join (C=5007..6832 R=50) (AT=144184..144295 R=9340 L=1) Hash Cond: ("substring"((a.order_number)::text, '^[^.]+'::text) = (a.base_order)::text) ->GroupAggregate (C=2519..4194 R=10000) (AT=103..161 R=10000 L=1) ->Sort (C=2519..2544 R=10000) (AT=103..111 R=10001 L=1) Sort Key: o.order_number, (** many other "o" columns) ->Merge Left Join (C=0..1855 R=10000) (AT=0..64 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..13 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=144080..144080 R=9247 L=1) ->Nested Loop (C=2487..2487 R=1) (AT=1255..144069 R=9247 L=1) Join Filter: ((a.base_order)::text = (b.base_order)::text) ->HashAggregate (C=1405..1405 R=1) (AT=1130..1143 R=9247 L=1) ->Nested Loop Left Join (C=18..1405 R=5) (AT=0..1111 R=9248 L=1) ->Nested Loop Left Join (C=18..1404 R=5) (AT=0..1033 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..12 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) ->HashAggregate (C=1081..1081 R=1) (AT=0..8 R=9247 L=9247) ->Nested Loop Left Join (C=17..1081 R=3) (AT=0..109 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..11 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: 144303.615 ms