agora inbox for [email protected]help / color / mirror / Atom feed
[PATCH 3/3] Tests for 0:1, 1:1 and 1:0 partition matching 8+ messages / 5 participants [nested] [flat]
* [PATCH 3/3] Tests for 0:1, 1:1 and 1:0 partition matching @ 2019-01-22 12:28 Etsuro Fujita <[email protected]> 0 siblings, 0 replies; 8+ messages in thread From: Etsuro Fujita @ 2019-01-22 12:28 UTC (permalink / raw) Rajkumar Raghuvanshi and Ashutosh Bapat. --- src/test/regress/expected/partition_join.out | 4131 +++++++++++++++--- src/test/regress/sql/partition_join.sql | 427 +- 2 files changed, 3855 insertions(+), 703 deletions(-) diff --git a/src/test/regress/expected/partition_join.out b/src/test/regress/expected/partition_join.out index c55de5d476..f19611c853 100644 --- a/src/test/regress/expected/partition_join.out +++ b/src/test/regress/expected/partition_join.out @@ -8,59 +8,86 @@ SET enable_partitionwise_join to true; -- partitioned by a single column -- CREATE TABLE prt1 (a int, b int, c varchar) PARTITION BY RANGE(a); +CREATE TABLE prt1_p0 PARTITION OF prt1 FOR VALUES FROM (MINVALUE) TO (0); CREATE TABLE prt1_p1 PARTITION OF prt1 FOR VALUES FROM (0) TO (250); CREATE TABLE prt1_p3 PARTITION OF prt1 FOR VALUES FROM (500) TO (600); CREATE TABLE prt1_p2 PARTITION OF prt1 FOR VALUES FROM (250) TO (500); -INSERT INTO prt1 SELECT i, i % 25, to_char(i, 'FM0000') FROM generate_series(0, 599) i WHERE i % 2 = 0; +CREATE TABLE prt1_p4 PARTITION OF prt1 FOR VALUES FROM (600) TO (800); +INSERT INTO prt1 SELECT i, i % 25, to_char(i, 'FM0000') FROM generate_series(-250, 799) i WHERE i % 2 = 0; +CREATE INDEX iprt1_p0_a on prt1_p0(a); CREATE INDEX iprt1_p1_a on prt1_p1(a); CREATE INDEX iprt1_p2_a on prt1_p2(a); CREATE INDEX iprt1_p3_a on prt1_p3(a); +CREATE INDEX iprt1_p4_a on prt1_p4(a); ANALYZE prt1; +-- prt2 have missing starting MINVALUE to -250 range and +-- extra bounds from 800 to MAXVALUE CREATE TABLE prt2 (a int, b int, c varchar) PARTITION BY RANGE(b); +CREATE TABLE prt2_p0 PARTITION OF prt2 FOR VALUES FROM (-250) TO (0); CREATE TABLE prt2_p1 PARTITION OF prt2 FOR VALUES FROM (0) TO (250); CREATE TABLE prt2_p2 PARTITION OF prt2 FOR VALUES FROM (250) TO (500); CREATE TABLE prt2_p3 PARTITION OF prt2 FOR VALUES FROM (500) TO (600); -INSERT INTO prt2 SELECT i % 25, i, to_char(i, 'FM0000') FROM generate_series(0, 599) i WHERE i % 3 = 0; +CREATE TABLE prt2_p4 PARTITION OF prt2 FOR VALUES FROM (600) TO (MAXVALUE); +INSERT INTO prt2 SELECT i % 25, i, to_char(i, 'FM0000') FROM generate_series(-250, 799) i WHERE i % 3 = 0; +CREATE INDEX iprt2_p0_b on prt2_p0(b); CREATE INDEX iprt2_p1_b on prt2_p1(b); CREATE INDEX iprt2_p2_b on prt2_p2(b); CREATE INDEX iprt2_p3_b on prt2_p3(b); +CREATE INDEX iprt2_p4_b on prt2_p4(b); ANALYZE prt2; +-- Partition-wise-join is possible with some partition bounds overlap +-- with each other completely and some partialy for inner,left,right, +-- full, semi and anti joins -- inner join EXPLAIN (COSTS OFF) SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.b = 0 ORDER BY t1.a, t2.b; - QUERY PLAN --------------------------------------------------- + QUERY PLAN +--------------------------------------------------------------- Sort Sort Key: t1.a -> Append -> Hash Join Hash Cond: (t2.b = t1.a) - -> Seq Scan on prt2_p1 t2 + -> Seq Scan on prt2_p0 t2 -> Hash - -> Seq Scan on prt1_p1 t1 + -> Seq Scan on prt1_p0 t1 Filter: (b = 0) -> Hash Join Hash Cond: (t2_1.b = t1_1.a) - -> Seq Scan on prt2_p2 t2_1 + -> Seq Scan on prt2_p1 t2_1 -> Hash - -> Seq Scan on prt1_p2 t1_1 + -> Seq Scan on prt1_p1 t1_1 Filter: (b = 0) -> Hash Join Hash Cond: (t2_2.b = t1_2.a) - -> Seq Scan on prt2_p3 t2_2 + -> Seq Scan on prt2_p2 t2_2 -> Hash - -> Seq Scan on prt1_p3 t1_2 + -> Seq Scan on prt1_p2 t1_2 Filter: (b = 0) -(21 rows) + -> Nested Loop + -> Seq Scan on prt1_p3 t1_3 + Filter: (b = 0) + -> Index Scan using iprt2_p3_b on prt2_p3 t2_3 + Index Cond: (b = t1_3.a) + -> Hash Join + Hash Cond: (t2_4.b = t1_4.a) + -> Seq Scan on prt2_p4 t2_4 + -> Hash + -> Seq Scan on prt1_p4 t1_4 + Filter: (b = 0) +(32 rows) SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.b = 0 ORDER BY t1.a, t2.b; - a | c | b | c ------+------+-----+------ - 0 | 0000 | 0 | 0000 - 150 | 0150 | 150 | 0150 - 300 | 0300 | 300 | 0300 - 450 | 0450 | 450 | 0450 -(4 rows) + a | c | b | c +------+-------+------+------- + -150 | -0150 | -150 | -0150 + 0 | 0000 | 0 | 0000 + 150 | 0150 | 150 | 0150 + 300 | 0300 | 300 | 0300 + 450 | 0450 | 450 | 0450 + 600 | 0600 | 600 | 0600 + 750 | 0750 | 750 | 0750 +(7 rows) -- left outer join, with whole-row reference; partitionwise join does not apply EXPLAIN (COSTS OFF) @@ -72,35 +99,50 @@ SELECT t1, t2 FROM prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER -> Hash Right Join Hash Cond: (t2.b = t1.a) -> Append - -> Seq Scan on prt2_p1 t2 - -> Seq Scan on prt2_p2 t2_1 - -> Seq Scan on prt2_p3 t2_2 + -> Seq Scan on prt2_p0 t2 + -> Seq Scan on prt2_p1 t2_1 + -> Seq Scan on prt2_p2 t2_2 + -> Seq Scan on prt2_p3 t2_3 + -> Seq Scan on prt2_p4 t2_4 -> Hash -> Append - -> Seq Scan on prt1_p1 t1 + -> Seq Scan on prt1_p0 t1 Filter: (b = 0) - -> Seq Scan on prt1_p2 t1_1 + -> Seq Scan on prt1_p1 t1_1 Filter: (b = 0) - -> Seq Scan on prt1_p3 t1_2 + -> Seq Scan on prt1_p2 t1_2 Filter: (b = 0) -(16 rows) + -> Seq Scan on prt1_p3 t1_3 + Filter: (b = 0) + -> Seq Scan on prt1_p4 t1_4 + Filter: (b = 0) +(22 rows) SELECT t1, t2 FROM prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a, t2.b; - t1 | t2 ---------------+-------------- - (0,0,0000) | (0,0,0000) - (50,0,0050) | - (100,0,0100) | - (150,0,0150) | (0,150,0150) - (200,0,0200) | - (250,0,0250) | - (300,0,0300) | (0,300,0300) - (350,0,0350) | - (400,0,0400) | - (450,0,0450) | (0,450,0450) - (500,0,0500) | - (550,0,0550) | -(12 rows) + t1 | t2 +----------------+---------------- + (-250,0,-0250) | + (-200,0,-0200) | + (-150,0,-0150) | (0,-150,-0150) + (-100,0,-0100) | + (-50,0,-0050) | + (0,0,0000) | (0,0,0000) + (50,0,0050) | + (100,0,0100) | + (150,0,0150) | (0,150,0150) + (200,0,0200) | + (250,0,0250) | + (300,0,0300) | (0,300,0300) + (350,0,0350) | + (400,0,0400) | + (450,0,0450) | (0,450,0450) + (500,0,0500) | + (550,0,0550) | + (600,0,0600) | (0,600,0600) + (650,0,0650) | + (700,0,0700) | + (750,0,0750) | (0,750,0750) +(21 rows) -- right outer join EXPLAIN (COSTS OFF) @@ -112,35 +154,53 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b WHE -> Append -> Hash Right Join Hash Cond: (t1.a = t2.b) - -> Seq Scan on prt1_p1 t1 + -> Seq Scan on prt1_p0 t1 -> Hash - -> Seq Scan on prt2_p1 t2 + -> Seq Scan on prt2_p0 t2 Filter: (a = 0) -> Hash Right Join Hash Cond: (t1_1.a = t2_1.b) - -> Seq Scan on prt1_p2 t1_1 + -> Seq Scan on prt1_p1 t1_1 + -> Hash + -> Seq Scan on prt2_p1 t2_1 + Filter: (a = 0) + -> Hash Right Join + Hash Cond: (t1_2.a = t2_2.b) + -> Seq Scan on prt1_p2 t1_2 -> Hash - -> Seq Scan on prt2_p2 t2_1 + -> Seq Scan on prt2_p2 t2_2 Filter: (a = 0) -> Nested Loop Left Join - -> Seq Scan on prt2_p3 t2_2 + -> Seq Scan on prt2_p3 t2_3 Filter: (a = 0) - -> Index Scan using iprt1_p3_a on prt1_p3 t1_2 - Index Cond: (a = t2_2.b) -(20 rows) + -> Index Scan using iprt1_p3_a on prt1_p3 t1_3 + Index Cond: (a = t2_3.b) + -> Hash Right Join + Hash Cond: (t1_4.a = t2_4.b) + -> Seq Scan on prt1_p4 t1_4 + -> Hash + -> Seq Scan on prt2_p4 t2_4 + Filter: (a = 0) +(32 rows) SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b WHERE t2.a = 0 ORDER BY t1.a, t2.b; - a | c | b | c ------+------+-----+------ - 0 | 0000 | 0 | 0000 - 150 | 0150 | 150 | 0150 - 300 | 0300 | 300 | 0300 - 450 | 0450 | 450 | 0450 - | | 75 | 0075 - | | 225 | 0225 - | | 375 | 0375 - | | 525 | 0525 -(8 rows) + a | c | b | c +------+-------+------+------- + -150 | -0150 | -150 | -0150 + 0 | 0000 | 0 | 0000 + 150 | 0150 | 150 | 0150 + 300 | 0300 | 300 | 0300 + 450 | 0450 | 450 | 0450 + 600 | 0600 | 600 | 0600 + 750 | 0750 | 750 | 0750 + | | -225 | -0225 + | | -75 | -0075 + | | 75 | 0075 + | | 225 | 0225 + | | 375 | 0375 + | | 525 | 0525 + | | 675 | 0675 +(14 rows) -- full outer join, with placeholder vars EXPLAIN (COSTS OFF) @@ -148,8 +208,16 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT 50 phv, * FROM prt1 WHERE prt1.b = 0) QUERY PLAN ------------------------------------------------------------------ Sort - Sort Key: prt1_p1.a, prt2_p1.b + Sort Key: prt1_p0.a, prt2_p0.b -> Append + -> Hash Full Join + Hash Cond: (prt1_p0.a = prt2_p0.b) + Filter: (((50) = prt1_p0.a) OR ((75) = prt2_p0.b)) + -> Seq Scan on prt1_p0 + Filter: (b = 0) + -> Hash + -> Seq Scan on prt2_p0 + Filter: (a = 0) -> Hash Full Join Hash Cond: (prt1_p1.a = prt2_p1.b) Filter: (((50) = prt1_p1.a) OR ((75) = prt2_p1.b)) @@ -174,7 +242,15 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT 50 phv, * FROM prt1 WHERE prt1.b = 0) -> Hash -> Seq Scan on prt2_p3 Filter: (a = 0) -(27 rows) + -> Hash Full Join + Hash Cond: (prt1_p4.a = prt2_p4.b) + Filter: (((50) = prt1_p4.a) OR ((75) = prt2_p4.b)) + -> Seq Scan on prt1_p4 + Filter: (b = 0) + -> Hash + -> Seq Scan on prt2_p4 + Filter: (a = 0) +(43 rows) SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT 50 phv, * FROM prt1 WHERE prt1.b = 0) t1 FULL JOIN (SELECT 75 phv, * FROM prt2 WHERE prt2.a = 0) t2 ON (t1.a = t2.b) WHERE t1.phv = t1.a OR t2.phv = t2.b ORDER BY t1.a, t2.b; a | c | b | c @@ -211,8 +287,15 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a < 450) t1 LEFT JO QUERY PLAN ----------------------------------------------------------- Sort - Sort Key: prt1_p1.a, b + Sort Key: prt1_p0.a, b -> Append + -> Hash Left Join + Hash Cond: (prt1_p0.a = b) + -> Seq Scan on prt1_p0 + Filter: ((a < 450) AND (b = 0)) + -> Hash + -> Result + One-Time Filter: false -> Hash Left Join Hash Cond: (prt1_p1.a = b) -> Seq Scan on prt1_p1 @@ -227,29 +310,42 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a < 450) t1 LEFT JO -> Hash -> Seq Scan on prt1_p2 Filter: ((a < 450) AND (b = 0)) -(17 rows) +(24 rows) SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a < 450) t1 LEFT JOIN (SELECT * FROM prt2 WHERE b > 250) t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a, t2.b; - a | c | b | c ------+------+-----+------ - 0 | 0000 | | - 50 | 0050 | | - 100 | 0100 | | - 150 | 0150 | | - 200 | 0200 | | - 250 | 0250 | | - 300 | 0300 | 300 | 0300 - 350 | 0350 | | - 400 | 0400 | | -(9 rows) + a | c | b | c +------+-------+-----+------ + -250 | -0250 | | + -200 | -0200 | | + -150 | -0150 | | + -100 | -0100 | | + -50 | -0050 | | + 0 | 0000 | | + 50 | 0050 | | + 100 | 0100 | | + 150 | 0150 | | + 200 | 0200 | | + 250 | 0250 | | + 300 | 0300 | 300 | 0300 + 350 | 0350 | | + 400 | 0400 | | +(14 rows) EXPLAIN (COSTS OFF) SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a < 450) t1 FULL JOIN (SELECT * FROM prt2 WHERE b > 250) t2 ON t1.a = t2.b WHERE t1.b = 0 OR t2.a = 0 ORDER BY t1.a, t2.b; QUERY PLAN ------------------------------------------------------------ Sort - Sort Key: prt1_p1.a, b + Sort Key: prt1_p0.a, b -> Append + -> Hash Full Join + Hash Cond: (prt1_p0.a = b) + Filter: ((prt1_p0.b = 0) OR (a = 0)) + -> Seq Scan on prt1_p0 + Filter: (a < 450) + -> Hash + -> Result + One-Time Filter: false -> Hash Full Join Hash Cond: (prt1_p1.a = b) Filter: ((prt1_p1.b = 0) OR (a = 0)) @@ -274,64 +370,153 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a < 450) t1 FULL JO -> Hash -> Result One-Time Filter: false -(27 rows) + -> Hash Full Join + Hash Cond: (prt2_p4.b = a) + Filter: ((b = 0) OR (prt2_p4.a = 0)) + -> Seq Scan on prt2_p4 + Filter: (b > 250) + -> Hash + -> Result + One-Time Filter: false +(43 rows) SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a < 450) t1 FULL JOIN (SELECT * FROM prt2 WHERE b > 250) t2 ON t1.a = t2.b WHERE t1.b = 0 OR t2.a = 0 ORDER BY t1.a, t2.b; - a | c | b | c ------+------+-----+------ - 0 | 0000 | | - 50 | 0050 | | - 100 | 0100 | | - 150 | 0150 | | - 200 | 0200 | | - 250 | 0250 | | - 300 | 0300 | 300 | 0300 - 350 | 0350 | | - 400 | 0400 | | - | | 375 | 0375 - | | 450 | 0450 - | | 525 | 0525 -(12 rows) + a | c | b | c +------+-------+-----+------ + -250 | -0250 | | + -200 | -0200 | | + -150 | -0150 | | + -100 | -0100 | | + -50 | -0050 | | + 0 | 0000 | | + 50 | 0050 | | + 100 | 0100 | | + 150 | 0150 | | + 200 | 0200 | | + 250 | 0250 | | + 300 | 0300 | 300 | 0300 + 350 | 0350 | | + 400 | 0400 | | + | | 375 | 0375 + | | 450 | 0450 + | | 525 | 0525 + | | 600 | 0600 + | | 675 | 0675 + | | 750 | 0750 +(20 rows) -- Semi-join EXPLAIN (COSTS OFF) SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t2.b FROM prt2 t2 WHERE t2.a = 0) AND t1.b = 0 ORDER BY t1.a; - QUERY PLAN --------------------------------------------------- + QUERY PLAN +--------------------------------------------------------------- Sort Sort Key: t1.a -> Append -> Hash Semi Join Hash Cond: (t1.a = t2.b) - -> Seq Scan on prt1_p1 t1 + -> Seq Scan on prt1_p0 t1 Filter: (b = 0) -> Hash - -> Seq Scan on prt2_p1 t2 + -> Seq Scan on prt2_p0 t2 Filter: (a = 0) -> Hash Semi Join Hash Cond: (t1_1.a = t2_1.b) - -> Seq Scan on prt1_p2 t1_1 + -> Seq Scan on prt1_p1 t1_1 Filter: (b = 0) -> Hash - -> Seq Scan on prt2_p2 t2_1 + -> Seq Scan on prt2_p1 t2_1 Filter: (a = 0) - -> Nested Loop Semi Join - Join Filter: (t1_2.a = t2_2.b) - -> Seq Scan on prt1_p3 t1_2 + -> Hash Semi Join + Hash Cond: (t1_2.a = t2_2.b) + -> Seq Scan on prt1_p2 t1_2 Filter: (b = 0) - -> Materialize - -> Seq Scan on prt2_p3 t2_2 + -> Hash + -> Seq Scan on prt2_p2 t2_2 Filter: (a = 0) -(24 rows) + -> Nested Loop + -> HashAggregate + Group Key: t2_3.b + -> Seq Scan on prt2_p3 t2_3 + Filter: (a = 0) + -> Index Scan using iprt1_p3_a on prt1_p3 t1_3 + Index Cond: (a = t2_3.b) + Filter: (b = 0) + -> Hash Semi Join + Hash Cond: (t1_4.a = t2_4.b) + -> Seq Scan on prt1_p4 t1_4 + Filter: (b = 0) + -> Hash + -> Seq Scan on prt2_p4 t2_4 + Filter: (a = 0) +(39 rows) SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t2.b FROM prt2 t2 WHERE t2.a = 0) AND t1.b = 0 ORDER BY t1.a; - a | b | c ------+---+------ - 0 | 0 | 0000 - 150 | 0 | 0150 - 300 | 0 | 0300 - 450 | 0 | 0450 -(4 rows) + a | b | c +------+---+------- + -150 | 0 | -0150 + 0 | 0 | 0000 + 150 | 0 | 0150 + 300 | 0 | 0300 + 450 | 0 | 0450 + 600 | 0 | 0600 + 750 | 0 | 0750 +(7 rows) + +EXPLAIN (COSTS OFF) +SELECT t1.* FROM prt2 t1 WHERE t1.b IN (SELECT t2.a FROM prt1 t2 WHERE t2.b = 0) AND t1.a = 0 ORDER BY t1.b; + QUERY PLAN +-------------------------------------------------- + Sort + Sort Key: t1.b + -> Append + -> Hash Semi Join + Hash Cond: (t1.b = t2.a) + -> Seq Scan on prt2_p0 t1 + Filter: (a = 0) + -> Hash + -> Seq Scan on prt1_p0 t2 + Filter: (b = 0) + -> Hash Semi Join + Hash Cond: (t1_1.b = t2_1.a) + -> Seq Scan on prt2_p1 t1_1 + Filter: (a = 0) + -> Hash + -> Seq Scan on prt1_p1 t2_1 + Filter: (b = 0) + -> Hash Semi Join + Hash Cond: (t1_2.b = t2_2.a) + -> Seq Scan on prt2_p2 t1_2 + Filter: (a = 0) + -> Hash + -> Seq Scan on prt1_p2 t2_2 + Filter: (b = 0) + -> Nested Loop Semi Join + Join Filter: (t1_3.b = t2_3.a) + -> Seq Scan on prt2_p3 t1_3 + Filter: (a = 0) + -> Seq Scan on prt1_p3 t2_3 + Filter: (b = 0) + -> Hash Semi Join + Hash Cond: (t1_4.b = t2_4.a) + -> Seq Scan on prt2_p4 t1_4 + Filter: (a = 0) + -> Hash + -> Seq Scan on prt1_p4 t2_4 + Filter: (b = 0) +(37 rows) + +SELECT t1.* FROM prt2 t1 WHERE t1.b IN (SELECT t2.a FROM prt1 t2 WHERE t2.b = 0) AND t1.a = 0 ORDER BY t1.b; + a | b | c +---+------+------- + 0 | -150 | -0150 + 0 | 0 | 0000 + 0 | 150 | 0150 + 0 | 300 | 0300 + 0 | 450 | 0450 + 0 | 600 | 0600 + 0 | 750 | 0750 +(7 rows) -- Anti-join with aggregates EXPLAIN (COSTS OFF) @@ -342,27 +527,82 @@ SELECT sum(t1.a), avg(t1.a), sum(t1.b), avg(t1.b) FROM prt1 t1 WHERE NOT EXISTS -> Append -> Hash Anti Join Hash Cond: (t1.a = t2.b) - -> Seq Scan on prt1_p1 t1 + -> Seq Scan on prt1_p0 t1 -> Hash - -> Seq Scan on prt2_p1 t2 + -> Seq Scan on prt2_p0 t2 -> Hash Anti Join Hash Cond: (t1_1.a = t2_1.b) - -> Seq Scan on prt1_p2 t1_1 + -> Seq Scan on prt1_p1 t1_1 -> Hash - -> Seq Scan on prt2_p2 t2_1 + -> Seq Scan on prt2_p1 t2_1 -> Hash Anti Join Hash Cond: (t1_2.a = t2_2.b) - -> Seq Scan on prt1_p3 t1_2 + -> Seq Scan on prt1_p2 t1_2 -> Hash - -> Seq Scan on prt2_p3 t2_2 -(17 rows) + -> Seq Scan on prt2_p2 t2_2 + -> Hash Anti Join + Hash Cond: (t1_3.a = t2_3.b) + -> Seq Scan on prt1_p3 t1_3 + -> Hash + -> Seq Scan on prt2_p3 t2_3 + -> Hash Anti Join + Hash Cond: (t1_4.a = t2_4.b) + -> Seq Scan on prt1_p4 t1_4 + -> Hash + -> Seq Scan on prt2_p4 t2_4 +(27 rows) SELECT sum(t1.a), avg(t1.a), sum(t1.b), avg(t1.b) FROM prt1 t1 WHERE NOT EXISTS (SELECT 1 FROM prt2 t2 WHERE t1.a = t2.b); - sum | avg | sum | avg --------+----------------------+------+--------------------- - 60000 | 300.0000000000000000 | 2400 | 12.0000000000000000 + sum | avg | sum | avg +-------+----------------------+------+-------------------- + 95550 | 273.0000000000000000 | 2200 | 6.2857142857142857 (1 row) +EXPLAIN (COSTS OFF) +SELECT t1.b, t1.c FROM prt2 t1 WHERE NOT EXISTS (SELECT 1 FROM prt1 t2 WHERE t1.b = t2.a) and t1.a = 0; + QUERY PLAN +-------------------------------------------------------------- + Append + -> Nested Loop Anti Join + -> Seq Scan on prt2_p0 t1 + Filter: (a = 0) + -> Index Only Scan using iprt1_p0_a on prt1_p0 t2 + Index Cond: (a = t1.b) + -> Hash Anti Join + Hash Cond: (t1_1.b = t2_1.a) + -> Seq Scan on prt2_p1 t1_1 + Filter: (a = 0) + -> Hash + -> Seq Scan on prt1_p1 t2_1 + -> Nested Loop Anti Join + -> Seq Scan on prt2_p2 t1_2 + Filter: (a = 0) + -> Index Only Scan using iprt1_p2_a on prt1_p2 t2_2 + Index Cond: (a = t1_2.b) + -> Nested Loop Anti Join + -> Seq Scan on prt2_p3 t1_3 + Filter: (a = 0) + -> Index Only Scan using iprt1_p3_a on prt1_p3 t2_3 + Index Cond: (a = t1_3.b) + -> Nested Loop Anti Join + -> Seq Scan on prt2_p4 t1_4 + Filter: (a = 0) + -> Index Only Scan using iprt1_p4_a on prt1_p4 t2_4 + Index Cond: (a = t1_4.b) +(27 rows) + +SELECT t1.b, t1.c FROM prt2 t1 WHERE NOT EXISTS (SELECT 1 FROM prt1 t2 WHERE t1.b = t2.a) and t1.a = 0; + b | c +------+------- + -225 | -0225 + -75 | -0075 + 75 | 0075 + 225 | 0225 + 375 | 0375 + 525 | 0525 + 675 | 0675 +(7 rows) + -- lateral reference EXPLAIN (COSTS OFF) SELECT * FROM prt1 t1 LEFT JOIN LATERAL @@ -374,49 +614,74 @@ SELECT * FROM prt1 t1 LEFT JOIN LATERAL Sort Key: t1.a -> Append -> Nested Loop Left Join - -> Seq Scan on prt1_p1 t1 + -> Seq Scan on prt1_p0 t1 Filter: (b = 0) -> Nested Loop - -> Index Only Scan using iprt1_p1_a on prt1_p1 t2 + -> Index Only Scan using iprt1_p0_a on prt1_p0 t2 Index Cond: (a = t1.a) - -> Index Scan using iprt2_p1_b on prt2_p1 t3 + -> Index Scan using iprt2_p0_b on prt2_p0 t3 Index Cond: (b = t2.a) -> Nested Loop Left Join - -> Seq Scan on prt1_p2 t1_1 + -> Seq Scan on prt1_p1 t1_1 Filter: (b = 0) -> Nested Loop - -> Index Only Scan using iprt1_p2_a on prt1_p2 t2_1 + -> Index Only Scan using iprt1_p1_a on prt1_p1 t2_1 Index Cond: (a = t1_1.a) - -> Index Scan using iprt2_p2_b on prt2_p2 t3_1 + -> Index Scan using iprt2_p1_b on prt2_p1 t3_1 Index Cond: (b = t2_1.a) -> Nested Loop Left Join - -> Seq Scan on prt1_p3 t1_2 + -> Seq Scan on prt1_p2 t1_2 Filter: (b = 0) -> Nested Loop - -> Index Only Scan using iprt1_p3_a on prt1_p3 t2_2 + -> Index Only Scan using iprt1_p2_a on prt1_p2 t2_2 Index Cond: (a = t1_2.a) - -> Index Scan using iprt2_p3_b on prt2_p3 t3_2 + -> Index Scan using iprt2_p2_b on prt2_p2 t3_2 Index Cond: (b = t2_2.a) -(27 rows) + -> Nested Loop Left Join + -> Seq Scan on prt1_p3 t1_3 + Filter: (b = 0) + -> Nested Loop + -> Index Only Scan using iprt1_p3_a on prt1_p3 t2_3 + Index Cond: (a = t1_3.a) + -> Index Scan using iprt2_p3_b on prt2_p3 t3_3 + Index Cond: (b = t2_3.a) + -> Nested Loop Left Join + -> Seq Scan on prt1_p4 t1_4 + Filter: (b = 0) + -> Nested Loop + -> Index Only Scan using iprt1_p4_a on prt1_p4 t2_4 + Index Cond: (a = t1_4.a) + -> Index Scan using iprt2_p4_b on prt2_p4 t3_4 + Index Cond: (b = t2_4.a) +(43 rows) SELECT * FROM prt1 t1 LEFT JOIN LATERAL (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.b) FROM prt1 t2 JOIN prt2 t3 ON (t2.a = t3.b)) ss ON t1.a = ss.t2a WHERE t1.b = 0 ORDER BY t1.a; - a | b | c | t2a | t3a | least ------+---+------+-----+-----+------- - 0 | 0 | 0000 | 0 | 0 | 0 - 50 | 0 | 0050 | | | - 100 | 0 | 0100 | | | - 150 | 0 | 0150 | 150 | 0 | 150 - 200 | 0 | 0200 | | | - 250 | 0 | 0250 | | | - 300 | 0 | 0300 | 300 | 0 | 300 - 350 | 0 | 0350 | | | - 400 | 0 | 0400 | | | - 450 | 0 | 0450 | 450 | 0 | 450 - 500 | 0 | 0500 | | | - 550 | 0 | 0550 | | | -(12 rows) + a | b | c | t2a | t3a | least +------+---+-------+------+-----+------- + -250 | 0 | -0250 | | | + -200 | 0 | -0200 | | | + -150 | 0 | -0150 | -150 | 0 | -150 + -100 | 0 | -0100 | | | + -50 | 0 | -0050 | | | + 0 | 0 | 0000 | 0 | 0 | 0 + 50 | 0 | 0050 | | | + 100 | 0 | 0100 | | | + 150 | 0 | 0150 | 150 | 0 | 150 + 200 | 0 | 0200 | | | + 250 | 0 | 0250 | | | + 300 | 0 | 0300 | 300 | 0 | 300 + 350 | 0 | 0350 | | | + 400 | 0 | 0400 | | | + 450 | 0 | 0450 | 450 | 0 | 450 + 500 | 0 | 0500 | | | + 550 | 0 | 0550 | | | + 600 | 0 | 0600 | 600 | 0 | 600 + 650 | 0 | 0650 | | | + 700 | 0 | 0700 | | | + 750 | 0 | 0750 | 750 | 0 | 750 +(21 rows) EXPLAIN (COSTS OFF) SELECT t1.a, ss.t2a, ss.t2c FROM prt1 t1 LEFT JOIN LATERAL @@ -430,64 +695,95 @@ SELECT t1.a, ss.t2a, ss.t2c FROM prt1 t1 LEFT JOIN LATERAL Hash Cond: ((t1.c)::text = (t2.c)::text) Filter: ((t1.b + COALESCE(t2.b, 0)) = 0) -> Append - -> Seq Scan on prt1_p1 t1 - -> Seq Scan on prt1_p2 t1_1 - -> Seq Scan on prt1_p3 t1_2 + -> Seq Scan on prt1_p0 t1 + -> Seq Scan on prt1_p1 t1_1 + -> Seq Scan on prt1_p2 t1_2 + -> Seq Scan on prt1_p3 t1_3 + -> Seq Scan on prt1_p4 t1_4 -> Hash -> Append -> Hash Join Hash Cond: (t2.a = t3.b) - -> Seq Scan on prt1_p1 t2 + -> Seq Scan on prt1_p0 t2 -> Hash - -> Seq Scan on prt2_p1 t3 + -> Seq Scan on prt2_p0 t3 -> Hash Join Hash Cond: (t2_1.a = t3_1.b) - -> Seq Scan on prt1_p2 t2_1 + -> Seq Scan on prt1_p1 t2_1 -> Hash - -> Seq Scan on prt2_p2 t3_1 + -> Seq Scan on prt2_p1 t3_1 -> Hash Join Hash Cond: (t2_2.a = t3_2.b) - -> Seq Scan on prt1_p3 t2_2 + -> Seq Scan on prt1_p2 t2_2 -> Hash - -> Seq Scan on prt2_p3 t3_2 -(26 rows) + -> Seq Scan on prt2_p2 t3_2 + -> Hash Join + Hash Cond: (t2_3.a = t3_3.b) + -> Seq Scan on prt1_p3 t2_3 + -> Hash + -> Seq Scan on prt2_p3 t3_3 + -> Hash Join + Hash Cond: (t2_4.a = t3_4.b) + -> Seq Scan on prt1_p4 t2_4 + -> Hash + -> Seq Scan on prt2_p4 t3_4 +(38 rows) SELECT t1.a, ss.t2a, ss.t2c FROM prt1 t1 LEFT JOIN LATERAL (SELECT t2.a AS t2a, t3.a AS t3a, t2.b t2b, t2.c t2c, least(t1.a,t2.a,t3.a) FROM prt1 t2 JOIN prt2 t3 ON (t2.a = t3.b)) ss ON t1.c = ss.t2c WHERE (t1.b + coalesce(ss.t2b, 0)) = 0 ORDER BY t1.a; - a | t2a | t2c ------+-----+------ - 0 | 0 | 0000 - 50 | | - 100 | | - 150 | 150 | 0150 - 200 | | - 250 | | - 300 | 300 | 0300 - 350 | | - 400 | | - 450 | 450 | 0450 - 500 | | - 550 | | -(12 rows) + a | t2a | t2c +------+------+------- + -250 | | + -200 | | + -150 | -150 | -0150 + -100 | | + -50 | | + 0 | 0 | 0000 + 50 | | + 100 | | + 150 | 150 | 0150 + 200 | | + 250 | | + 300 | 300 | 0300 + 350 | | + 400 | | + 450 | 450 | 0450 + 500 | | + 550 | | + 600 | 600 | 0600 + 650 | | + 700 | | + 750 | 750 | 0750 +(21 rows) -- -- partitioned by expression -- CREATE TABLE prt1_e (a int, b int, c int) PARTITION BY RANGE(((a + b)/2)); +CREATE TABLE prt1_e_p0 PARTITION OF prt1_e FOR VALUES FROM (MINVALUE) TO (0); CREATE TABLE prt1_e_p1 PARTITION OF prt1_e FOR VALUES FROM (0) TO (250); CREATE TABLE prt1_e_p2 PARTITION OF prt1_e FOR VALUES FROM (250) TO (500); CREATE TABLE prt1_e_p3 PARTITION OF prt1_e FOR VALUES FROM (500) TO (600); +CREATE TABLE prt1_e_p4 PARTITION OF prt1_e FOR VALUES FROM (600) TO (MAXVALUE); INSERT INTO prt1_e SELECT i, i, i % 25 FROM generate_series(0, 599, 2) i; +INSERT INTO prt1_e SELECT i, i, i % 25 FROM generate_series(-250, 0, 2) i; +INSERT INTO prt1_e SELECT i, i, i % 25 FROM generate_series(600, 799, 2) i; +CREATE INDEX iprt1_e_p0_ab2 on prt1_e_p1(((a+b)/2)); CREATE INDEX iprt1_e_p1_ab2 on prt1_e_p1(((a+b)/2)); CREATE INDEX iprt1_e_p2_ab2 on prt1_e_p2(((a+b)/2)); CREATE INDEX iprt1_e_p3_ab2 on prt1_e_p3(((a+b)/2)); +CREATE INDEX iprt1_e_p4_ab2 on prt1_e_p1(((a+b)/2)); ANALYZE prt1_e; CREATE TABLE prt2_e (a int, b int, c int) PARTITION BY RANGE(((b + a)/2)); +CREATE TABLE prt2_e_p0 PARTITION OF prt2_e FOR VALUES FROM (MINVALUE) TO (0); CREATE TABLE prt2_e_p1 PARTITION OF prt2_e FOR VALUES FROM (0) TO (250); CREATE TABLE prt2_e_p2 PARTITION OF prt2_e FOR VALUES FROM (250) TO (500); CREATE TABLE prt2_e_p3 PARTITION OF prt2_e FOR VALUES FROM (500) TO (600); +CREATE TABLE prt2_e_p4 PARTITION OF prt2_e FOR VALUES FROM (600) TO (MAXVALUE); INSERT INTO prt2_e SELECT i, i, i % 25 FROM generate_series(0, 599, 3) i; +INSERT INTO prt2_e SELECT i, i, i % 25 FROM generate_series(-250, 0, 3) i; +INSERT INTO prt2_e SELECT i, i, i % 25 FROM generate_series(600, 799, 3) i; ANALYZE prt2_e; EXPLAIN (COSTS OFF) SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_e t1, prt2_e t2 WHERE (t1.a + t1.b)/2 = (t2.b + t2.a)/2 AND t1.c = 0 ORDER BY t1.a, t2.b; @@ -498,32 +794,49 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_e t1, prt2_e t2 WHERE (t1.a + t1.b)/2 = -> Append -> Hash Join Hash Cond: (((t2.b + t2.a) / 2) = ((t1.a + t1.b) / 2)) - -> Seq Scan on prt2_e_p1 t2 + -> Seq Scan on prt2_e_p0 t2 -> Hash - -> Seq Scan on prt1_e_p1 t1 + -> Seq Scan on prt1_e_p0 t1 Filter: (c = 0) -> Hash Join - Hash Cond: (((t2_1.b + t2_1.a) / 2) = ((t1_1.a + t1_1.b) / 2)) - -> Seq Scan on prt2_e_p2 t2_1 + Hash Cond: (((t1_1.a + t1_1.b) / 2) = ((t2_1.b + t2_1.a) / 2)) + -> Seq Scan on prt1_e_p1 t1_1 + Filter: (c = 0) -> Hash - -> Seq Scan on prt1_e_p2 t1_1 - Filter: (c = 0) + -> Seq Scan on prt2_e_p1 t2_1 -> Hash Join Hash Cond: (((t2_2.b + t2_2.a) / 2) = ((t1_2.a + t1_2.b) / 2)) - -> Seq Scan on prt2_e_p3 t2_2 + -> Seq Scan on prt2_e_p2 t2_2 -> Hash - -> Seq Scan on prt1_e_p3 t1_2 + -> Seq Scan on prt1_e_p2 t1_2 Filter: (c = 0) -(21 rows) + -> Hash Join + Hash Cond: (((t2_3.b + t2_3.a) / 2) = ((t1_3.a + t1_3.b) / 2)) + -> Seq Scan on prt2_e_p3 t2_3 + -> Hash + -> Seq Scan on prt1_e_p3 t1_3 + Filter: (c = 0) + -> Hash Join + Hash Cond: (((t2_4.b + t2_4.a) / 2) = ((t1_4.a + t1_4.b) / 2)) + -> Seq Scan on prt2_e_p4 t2_4 + -> Hash + -> Seq Scan on prt1_e_p4 t1_4 + Filter: (c = 0) +(33 rows) SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_e t1, prt2_e t2 WHERE (t1.a + t1.b)/2 = (t2.b + t2.a)/2 AND t1.c = 0 ORDER BY t1.a, t2.b; - a | c | b | c ------+---+-----+--- - 0 | 0 | 0 | 0 - 150 | 0 | 150 | 0 - 300 | 0 | 300 | 0 - 450 | 0 | 450 | 0 -(4 rows) + a | c | b | c +------+---+------+--- + -250 | 0 | -250 | 0 + -100 | 0 | -100 | 0 + 0 | 0 | 0 | 0 + 0 | 0 | 0 | 0 + 150 | 0 | 150 | 0 + 300 | 0 | 300 | 0 + 450 | 0 | 450 | 0 + 600 | 0 | 600 | 0 + 750 | 0 | 750 | 0 +(9 rows) -- -- N-way join @@ -536,154 +849,232 @@ SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM prt1 t1, prt2 t2, prt1_e t Sort Key: t1.a -> Append -> Nested Loop - Join Filter: (t1.a = ((t3.a + t3.b) / 2)) + Join Filter: (t1.a = t2.b) -> Hash Join - Hash Cond: (t2.b = t1.a) - -> Seq Scan on prt2_p1 t2 + Hash Cond: (((t3.a + t3.b) / 2) = t1.a) + -> Seq Scan on prt1_e_p0 t3 -> Hash - -> Seq Scan on prt1_p1 t1 + -> Seq Scan on prt1_p0 t1 Filter: (b = 0) - -> Index Scan using iprt1_e_p1_ab2 on prt1_e_p1 t3 - Index Cond: (((a + b) / 2) = t2.b) + -> Index Scan using iprt2_p0_b on prt2_p0 t2 + Index Cond: (b = ((t3.a + t3.b) / 2)) -> Nested Loop Join Filter: (t1_1.a = ((t3_1.a + t3_1.b) / 2)) -> Hash Join Hash Cond: (t2_1.b = t1_1.a) - -> Seq Scan on prt2_p2 t2_1 + -> Seq Scan on prt2_p1 t2_1 -> Hash - -> Seq Scan on prt1_p2 t1_1 + -> Seq Scan on prt1_p1 t1_1 Filter: (b = 0) - -> Index Scan using iprt1_e_p2_ab2 on prt1_e_p2 t3_1 + -> Index Scan using iprt1_e_p4_ab2 on prt1_e_p1 t3_1 Index Cond: (((a + b) / 2) = t2_1.b) -> Nested Loop Join Filter: (t1_2.a = ((t3_2.a + t3_2.b) / 2)) -> Hash Join Hash Cond: (t2_2.b = t1_2.a) - -> Seq Scan on prt2_p3 t2_2 + -> Seq Scan on prt2_p2 t2_2 -> Hash - -> Seq Scan on prt1_p3 t1_2 + -> Seq Scan on prt1_p2 t1_2 Filter: (b = 0) - -> Index Scan using iprt1_e_p3_ab2 on prt1_e_p3 t3_2 + -> Index Scan using iprt1_e_p2_ab2 on prt1_e_p2 t3_2 Index Cond: (((a + b) / 2) = t2_2.b) -(33 rows) + -> Nested Loop + Join Filter: (t1_3.a = ((t3_3.a + t3_3.b) / 2)) + -> Nested Loop + -> Seq Scan on prt1_p3 t1_3 + Filter: (b = 0) + -> Index Scan using iprt2_p3_b on prt2_p3 t2_3 + Index Cond: (b = t1_3.a) + -> Index Scan using iprt1_e_p3_ab2 on prt1_e_p3 t3_3 + Index Cond: (((a + b) / 2) = t2_3.b) + -> Nested Loop + Join Filter: (t1_4.a = t2_4.b) + -> Hash Join + Hash Cond: (((t3_4.a + t3_4.b) / 2) = t1_4.a) + -> Seq Scan on prt1_e_p4 t3_4 + -> Hash + -> Seq Scan on prt1_p4 t1_4 + Filter: (b = 0) + -> Index Scan using iprt2_p4_b on prt2_p4 t2_4 + Index Cond: (b = ((t3_4.a + t3_4.b) / 2)) +(52 rows) SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM prt1 t1, prt2 t2, prt1_e t3 WHERE t1.a = t2.b AND t1.a = (t3.a + t3.b)/2 AND t1.b = 0 ORDER BY t1.a, t2.b; - a | c | b | c | ?column? | c ------+------+-----+------+----------+--- - 0 | 0000 | 0 | 0000 | 0 | 0 - 150 | 0150 | 150 | 0150 | 300 | 0 - 300 | 0300 | 300 | 0300 | 600 | 0 - 450 | 0450 | 450 | 0450 | 900 | 0 -(4 rows) + a | c | b | c | ?column? | c +------+-------+------+-------+----------+--- + -150 | -0150 | -150 | -0150 | -300 | 0 + 0 | 0000 | 0 | 0000 | 0 | 0 + 0 | 0000 | 0 | 0000 | 0 | 0 + 150 | 0150 | 150 | 0150 | 300 | 0 + 300 | 0300 | 300 | 0300 | 600 | 0 + 450 | 0450 | 450 | 0450 | 900 | 0 + 600 | 0600 | 600 | 0600 | 1200 | 0 + 750 | 0750 | 750 | 0750 | 1500 | 0 +(8 rows) EXPLAIN (COSTS OFF) SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b) LEFT JOIN prt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t1.b = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; - QUERY PLAN --------------------------------------------------------------- + QUERY PLAN +--------------------------------------------------------------------- Sort Sort Key: t1.a, t2.b, ((t3.a + t3.b)) -> Append -> Hash Right Join Hash Cond: (((t3.a + t3.b) / 2) = t1.a) - -> Seq Scan on prt1_e_p1 t3 + -> Seq Scan on prt1_e_p0 t3 -> Hash -> Hash Right Join Hash Cond: (t2.b = t1.a) - -> Seq Scan on prt2_p1 t2 + -> Seq Scan on prt2_p0 t2 -> Hash - -> Seq Scan on prt1_p1 t1 + -> Seq Scan on prt1_p0 t1 Filter: (b = 0) -> Hash Right Join Hash Cond: (((t3_1.a + t3_1.b) / 2) = t1_1.a) - -> Seq Scan on prt1_e_p2 t3_1 + -> Seq Scan on prt1_e_p1 t3_1 -> Hash -> Hash Right Join Hash Cond: (t2_1.b = t1_1.a) - -> Seq Scan on prt2_p2 t2_1 + -> Seq Scan on prt2_p1 t2_1 -> Hash - -> Seq Scan on prt1_p2 t1_1 + -> Seq Scan on prt1_p1 t1_1 Filter: (b = 0) -> Hash Right Join Hash Cond: (((t3_2.a + t3_2.b) / 2) = t1_2.a) - -> Seq Scan on prt1_e_p3 t3_2 + -> Seq Scan on prt1_e_p2 t3_2 -> Hash -> Hash Right Join Hash Cond: (t2_2.b = t1_2.a) - -> Seq Scan on prt2_p3 t2_2 + -> Seq Scan on prt2_p2 t2_2 -> Hash - -> Seq Scan on prt1_p3 t1_2 + -> Seq Scan on prt1_p2 t1_2 Filter: (b = 0) -(33 rows) + -> Nested Loop Left Join + -> Nested Loop Left Join + -> Seq Scan on prt1_p3 t1_3 + Filter: (b = 0) + -> Index Scan using iprt2_p3_b on prt2_p3 t2_3 + Index Cond: (t1_3.a = b) + -> Index Scan using iprt1_e_p3_ab2 on prt1_e_p3 t3_3 + Index Cond: (t1_3.a = ((a + b) / 2)) + -> Hash Right Join + Hash Cond: (((t3_4.a + t3_4.b) / 2) = t1_4.a) + -> Seq Scan on prt1_e_p4 t3_4 + -> Hash + -> Hash Right Join + Hash Cond: (t2_4.b = t1_4.a) + -> Seq Scan on prt2_p4 t2_4 + -> Hash + -> Seq Scan on prt1_p4 t1_4 + Filter: (b = 0) +(51 rows) SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b) LEFT JOIN prt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t1.b = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; - a | c | b | c | ?column? | c ------+------+-----+------+----------+--- - 0 | 0000 | 0 | 0000 | 0 | 0 - 50 | 0050 | | | 100 | 0 - 100 | 0100 | | | 200 | 0 - 150 | 0150 | 150 | 0150 | 300 | 0 - 200 | 0200 | | | 400 | 0 - 250 | 0250 | | | 500 | 0 - 300 | 0300 | 300 | 0300 | 600 | 0 - 350 | 0350 | | | 700 | 0 - 400 | 0400 | | | 800 | 0 - 450 | 0450 | 450 | 0450 | 900 | 0 - 500 | 0500 | | | 1000 | 0 - 550 | 0550 | | | 1100 | 0 -(12 rows) + a | c | b | c | ?column? | c +------+-------+------+-------+----------+--- + -250 | -0250 | | | -500 | 0 + -200 | -0200 | | | -400 | 0 + -150 | -0150 | -150 | -0150 | -300 | 0 + -100 | -0100 | | | -200 | 0 + -50 | -0050 | | | -100 | 0 + 0 | 0000 | 0 | 0000 | 0 | 0 + 0 | 0000 | 0 | 0000 | 0 | 0 + 50 | 0050 | | | 100 | 0 + 100 | 0100 | | | 200 | 0 + 150 | 0150 | 150 | 0150 | 300 | 0 + 200 | 0200 | | | 400 | 0 + 250 | 0250 | | | 500 | 0 + 300 | 0300 | 300 | 0300 | 600 | 0 + 350 | 0350 | | | 700 | 0 + 400 | 0400 | | | 800 | 0 + 450 | 0450 | 450 | 0450 | 900 | 0 + 500 | 0500 | | | 1000 | 0 + 550 | 0550 | | | 1100 | 0 + 600 | 0600 | 600 | 0600 | 1200 | 0 + 650 | 0650 | | | 1300 | 0 + 700 | 0700 | | | 1400 | 0 + 750 | 0750 | 750 | 0750 | 1500 | 0 +(22 rows) EXPLAIN (COSTS OFF) SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t3.c = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; - QUERY PLAN -------------------------------------------------------------------- + QUERY PLAN +--------------------------------------------------------------------- Sort Sort Key: t1.a, t2.b, ((t3.a + t3.b)) -> Append -> Nested Loop Left Join -> Hash Right Join Hash Cond: (t1.a = ((t3.a + t3.b) / 2)) - -> Seq Scan on prt1_p1 t1 + -> Seq Scan on prt1_p0 t1 -> Hash - -> Seq Scan on prt1_e_p1 t3 + -> Seq Scan on prt1_e_p0 t3 Filter: (c = 0) - -> Index Scan using iprt2_p1_b on prt2_p1 t2 + -> Index Scan using iprt2_p0_b on prt2_p0 t2 Index Cond: (t1.a = b) -> Nested Loop Left Join -> Hash Right Join Hash Cond: (t1_1.a = ((t3_1.a + t3_1.b) / 2)) - -> Seq Scan on prt1_p2 t1_1 + -> Seq Scan on prt1_p1 t1_1 -> Hash - -> Seq Scan on prt1_e_p2 t3_1 + -> Seq Scan on prt1_e_p1 t3_1 Filter: (c = 0) - -> Index Scan using iprt2_p2_b on prt2_p2 t2_1 + -> Index Scan using iprt2_p1_b on prt2_p1 t2_1 Index Cond: (t1_1.a = b) -> Nested Loop Left Join -> Hash Right Join Hash Cond: (t1_2.a = ((t3_2.a + t3_2.b) / 2)) - -> Seq Scan on prt1_p3 t1_2 + -> Seq Scan on prt1_p2 t1_2 -> Hash - -> Seq Scan on prt1_e_p3 t3_2 + -> Seq Scan on prt1_e_p2 t3_2 Filter: (c = 0) - -> Index Scan using iprt2_p3_b on prt2_p3 t2_2 + -> Index Scan using iprt2_p2_b on prt2_p2 t2_2 Index Cond: (t1_2.a = b) -(30 rows) + -> Nested Loop Left Join + -> Nested Loop Left Join + -> Seq Scan on prt1_e_p3 t3_3 + Filter: (c = 0) + -> Index Scan using iprt1_p3_a on prt1_p3 t1_3 + Index Cond: (a = ((t3_3.a + t3_3.b) / 2)) + -> Index Scan using iprt2_p3_b on prt2_p3 t2_3 + Index Cond: (t1_3.a = b) + -> Nested Loop Left Join + -> Hash Right Join + Hash Cond: (t1_4.a = ((t3_4.a + t3_4.b) / 2)) + -> Seq Scan on prt1_p4 t1_4 + -> Hash + -> Seq Scan on prt1_e_p4 t3_4 + Filter: (c = 0) + -> Index Scan using iprt2_p4_b on prt2_p4 t2_4 + Index Cond: (t1_4.a = b) +(47 rows) SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t3.c = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; - a | c | b | c | ?column? | c ------+------+-----+------+----------+--- - 0 | 0000 | 0 | 0000 | 0 | 0 - 50 | 0050 | | | 100 | 0 - 100 | 0100 | | | 200 | 0 - 150 | 0150 | 150 | 0150 | 300 | 0 - 200 | 0200 | | | 400 | 0 - 250 | 0250 | | | 500 | 0 - 300 | 0300 | 300 | 0300 | 600 | 0 - 350 | 0350 | | | 700 | 0 - 400 | 0400 | | | 800 | 0 - 450 | 0450 | 450 | 0450 | 900 | 0 - 500 | 0500 | | | 1000 | 0 - 550 | 0550 | | | 1100 | 0 -(12 rows) + a | c | b | c | ?column? | c +------+-------+------+-------+----------+--- + -250 | -0250 | | | -500 | 0 + -200 | -0200 | | | -400 | 0 + -150 | -0150 | -150 | -0150 | -300 | 0 + -100 | -0100 | | | -200 | 0 + -50 | -0050 | | | -100 | 0 + 0 | 0000 | 0 | 0000 | 0 | 0 + 0 | 0000 | 0 | 0000 | 0 | 0 + 50 | 0050 | | | 100 | 0 + 100 | 0100 | | | 200 | 0 + 150 | 0150 | 150 | 0150 | 300 | 0 + 200 | 0200 | | | 400 | 0 + 250 | 0250 | | | 500 | 0 + 300 | 0300 | 300 | 0300 | 600 | 0 + 350 | 0350 | | | 700 | 0 + 400 | 0400 | | | 800 | 0 + 450 | 0450 | 450 | 0450 | 900 | 0 + 500 | 0500 | | | 1000 | 0 + 550 | 0550 | | | 1100 | 0 + 600 | 0600 | 600 | 0600 | 1200 | 0 + 650 | 0650 | | | 1300 | 0 + 700 | 0700 | | | 1400 | 0 + 750 | 0750 | 750 | 0750 | 1500 | 0 +(22 rows) -- Cases with non-nullable expressions in subquery results; -- make sure these go to null as expected @@ -692,21 +1083,34 @@ SELECT t1.a, t1.phv, t2.b, t2.phv, t3.a + t3.b, t3.phv FROM ((SELECT 50 phv, * F QUERY PLAN ---------------------------------------------------------------------------------------------------------------- Sort - Sort Key: prt1_p1.a, prt2_p1.b, ((prt1_e_p1.a + prt1_e_p1.b)) + Sort Key: prt1_p0.a, prt2_p0.b, ((prt1_e_p0.a + prt1_e_p0.b)) -> Append -> Hash Full Join - Hash Cond: (prt1_p1.a = ((prt1_e_p1.a + prt1_e_p1.b) / 2)) - Filter: ((prt1_p1.a = (50)) OR (prt2_p1.b = (75)) OR (((prt1_e_p1.a + prt1_e_p1.b) / 2) = (50))) + Hash Cond: (prt1_p0.a = ((prt1_e_p0.a + prt1_e_p0.b) / 2)) + Filter: ((prt1_p0.a = (50)) OR (prt2_p0.b = (75)) OR (((prt1_e_p0.a + prt1_e_p0.b) / 2) = (50))) -> Hash Full Join - Hash Cond: (prt1_p1.a = prt2_p1.b) - -> Seq Scan on prt1_p1 + Hash Cond: (prt1_p0.a = prt2_p0.b) + -> Seq Scan on prt1_p0 Filter: (b = 0) -> Hash - -> Seq Scan on prt2_p1 + -> Seq Scan on prt2_p0 Filter: (a = 0) -> Hash - -> Seq Scan on prt1_e_p1 + -> Seq Scan on prt1_e_p0 Filter: (c = 0) + -> Hash Full Join + Hash Cond: (((prt1_e_p1.a + prt1_e_p1.b) / 2) = prt1_p1.a) + Filter: ((prt1_p1.a = (50)) OR (prt2_p1.b = (75)) OR (((prt1_e_p1.a + prt1_e_p1.b) / 2) = (50))) + -> Seq Scan on prt1_e_p1 + Filter: (c = 0) + -> Hash + -> Hash Full Join + Hash Cond: (prt1_p1.a = prt2_p1.b) + -> Seq Scan on prt1_p1 + Filter: (b = 0) + -> Hash + -> Seq Scan on prt2_p1 + Filter: (a = 0) -> Hash Full Join Hash Cond: (prt1_p2.a = ((prt1_e_p2.a + prt1_e_p2.b) / 2)) Filter: ((prt1_p2.a = (50)) OR (prt2_p2.b = (75)) OR (((prt1_e_p2.a + prt1_e_p2.b) / 2) = (50))) @@ -733,7 +1137,20 @@ SELECT t1.a, t1.phv, t2.b, t2.phv, t3.a + t3.b, t3.phv FROM ((SELECT 50 phv, * F -> Hash -> Seq Scan on prt1_e_p3 Filter: (c = 0) -(42 rows) + -> Hash Full Join + Hash Cond: (prt1_p4.a = ((prt1_e_p4.a + prt1_e_p4.b) / 2)) + Filter: ((prt1_p4.a = (50)) OR (prt2_p4.b = (75)) OR (((prt1_e_p4.a + prt1_e_p4.b) / 2) = (50))) + -> Hash Full Join + Hash Cond: (prt1_p4.a = prt2_p4.b) + -> Seq Scan on prt1_p4 + Filter: (b = 0) + -> Hash + -> Seq Scan on prt2_p4 + Filter: (a = 0) + -> Hash + -> Seq Scan on prt1_e_p4 + Filter: (c = 0) +(68 rows) SELECT t1.a, t1.phv, t2.b, t2.phv, t3.a + t3.b, t3.phv FROM ((SELECT 50 phv, * FROM prt1 WHERE prt1.b = 0) t1 FULL JOIN (SELECT 75 phv, * FROM prt2 WHERE prt2.a = 0) t2 ON (t1.a = t2.b)) FULL JOIN (SELECT 50 phv, * FROM prt1_e WHERE prt1_e.c = 0) t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t1.a = t1.phv OR t2.b = t2.phv OR (t3.a + t3.b)/2 = t3.phv ORDER BY t1.a, t2.b, t3.a + t3.b; a | phv | b | phv | ?column? | phv @@ -751,172 +1168,260 @@ SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1, prt1_e t2 WHER Sort Key: t1.a -> Append -> Nested Loop - Join Filter: (t1.a = t1_3.b) + Join Filter: (t1.a = t1_5.b) -> HashAggregate - Group Key: t1_3.b + Group Key: t1_5.b -> Hash Join - Hash Cond: (((t2.a + t2.b) / 2) = t1_3.b) - -> Seq Scan on prt1_e_p1 t2 + Hash Cond: (((t2.a + t2.b) / 2) = t1_5.b) + -> Seq Scan on prt1_e_p0 t2 -> Hash - -> Seq Scan on prt2_p1 t1_3 + -> Seq Scan on prt2_p0 t1_5 Filter: (a = 0) - -> Index Scan using iprt1_p1_a on prt1_p1 t1 + -> Index Scan using iprt1_p0_a on prt1_p0 t1 Index Cond: (a = ((t2.a + t2.b) / 2)) Filter: (b = 0) -> Nested Loop - Join Filter: (t1_1.a = t1_4.b) + Join Filter: (t1_1.a = t1_6.b) -> HashAggregate - Group Key: t1_4.b + Group Key: t1_6.b -> Hash Join - Hash Cond: (((t2_1.a + t2_1.b) / 2) = t1_4.b) - -> Seq Scan on prt1_e_p2 t2_1 + Hash Cond: (((t2_1.a + t2_1.b) / 2) = t1_6.b) + -> Seq Scan on prt1_e_p1 t2_1 -> Hash - -> Seq Scan on prt2_p2 t1_4 + -> Seq Scan on prt2_p1 t1_6 Filter: (a = 0) - -> Index Scan using iprt1_p2_a on prt1_p2 t1_1 + -> Index Scan using iprt1_p1_a on prt1_p1 t1_1 Index Cond: (a = ((t2_1.a + t2_1.b) / 2)) Filter: (b = 0) -> Nested Loop - Join Filter: (t1_2.a = t1_5.b) + Join Filter: (t1_2.a = t1_7.b) -> HashAggregate - Group Key: t1_5.b + Group Key: t1_7.b -> Nested Loop - -> Seq Scan on prt2_p3 t1_5 + -> Seq Scan on prt2_p2 t1_7 Filter: (a = 0) - -> Index Scan using iprt1_e_p3_ab2 on prt1_e_p3 t2_2 - Index Cond: (((a + b) / 2) = t1_5.b) - -> Index Scan using iprt1_p3_a on prt1_p3 t1_2 + -> Index Scan using iprt1_e_p2_ab2 on prt1_e_p2 t2_2 + Index Cond: (((a + b) / 2) = t1_7.b) + -> Index Scan using iprt1_p2_a on prt1_p2 t1_2 Index Cond: (a = ((t2_2.a + t2_2.b) / 2)) Filter: (b = 0) -(41 rows) + -> Nested Loop + Join Filter: (t1_3.a = t1_8.b) + -> HashAggregate + Group Key: t1_8.b + -> Nested Loop + -> Seq Scan on prt2_p3 t1_8 + Filter: (a = 0) + -> Index Scan using iprt1_e_p3_ab2 on prt1_e_p3 t2_3 + Index Cond: (((a + b) / 2) = t1_8.b) + -> Index Scan using iprt1_p3_a on prt1_p3 t1_3 + Index Cond: (a = ((t2_3.a + t2_3.b) / 2)) + Filter: (b = 0) + -> Nested Loop + Join Filter: (t1_4.a = t1_9.b) + -> HashAggregate + Group Key: t1_9.b + -> Hash Join + Hash Cond: (((t2_4.a + t2_4.b) / 2) = t1_9.b) + -> Seq Scan on prt1_e_p4 t2_4 + -> Hash + -> Seq Scan on prt2_p4 t1_9 + Filter: (a = 0) + -> Index Scan using iprt1_p4_a on prt1_p4 t1_4 + Index Cond: (a = ((t2_4.a + t2_4.b) / 2)) + Filter: (b = 0) +(66 rows) SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1, prt1_e t2 WHERE t1.a = 0 AND t1.b = (t2.a + t2.b)/2) AND t1.b = 0 ORDER BY t1.a; - a | b | c ------+---+------ - 0 | 0 | 0000 - 150 | 0 | 0150 - 300 | 0 | 0300 - 450 | 0 | 0450 -(4 rows) + a | b | c +------+---+------- + -150 | 0 | -0150 + 0 | 0 | 0000 + 150 | 0 | 0150 + 300 | 0 | 0300 + 450 | 0 | 0450 + 600 | 0 | 0600 + 750 | 0 | 0750 +(7 rows) EXPLAIN (COSTS OFF) SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1 WHERE t1.b IN (SELECT (t1.a + t1.b)/2 FROM prt1_e t1 WHERE t1.c = 0)) AND t1.b = 0 ORDER BY t1.a; - QUERY PLAN -------------------------------------------------------------------------- + QUERY PLAN +--------------------------------------------------------------------------- Sort Sort Key: t1.a -> Append -> Nested Loop -> HashAggregate - Group Key: t1_3.b + Group Key: t1_5.b -> Hash Semi Join - Hash Cond: (t1_3.b = ((t1_6.a + t1_6.b) / 2)) - -> Seq Scan on prt2_p1 t1_3 + Hash Cond: (t1_5.b = ((t1_10.a + t1_10.b) / 2)) + -> Seq Scan on prt2_p0 t1_5 -> Hash - -> Seq Scan on prt1_e_p1 t1_6 + -> Seq Scan on prt1_e_p0 t1_10 Filter: (c = 0) - -> Index Scan using iprt1_p1_a on prt1_p1 t1 - Index Cond: (a = t1_3.b) + -> Index Scan using iprt1_p0_a on prt1_p0 t1 + Index Cond: (a = t1_5.b) Filter: (b = 0) -> Nested Loop -> HashAggregate - Group Key: t1_4.b + Group Key: t1_6.b -> Hash Semi Join - Hash Cond: (t1_4.b = ((t1_7.a + t1_7.b) / 2)) - -> Seq Scan on prt2_p2 t1_4 + Hash Cond: (t1_6.b = ((t1_11.a + t1_11.b) / 2)) + -> Seq Scan on prt2_p1 t1_6 -> Hash - -> Seq Scan on prt1_e_p2 t1_7 + -> Seq Scan on prt1_e_p1 t1_11 Filter: (c = 0) - -> Index Scan using iprt1_p2_a on prt1_p2 t1_1 - Index Cond: (a = t1_4.b) + -> Index Scan using iprt1_p1_a on prt1_p1 t1_1 + Index Cond: (a = t1_6.b) Filter: (b = 0) -> Nested Loop -> HashAggregate - Group Key: t1_5.b + Group Key: t1_7.b -> Hash Semi Join - Hash Cond: (t1_5.b = ((t1_8.a + t1_8.b) / 2)) - -> Seq Scan on prt2_p3 t1_5 + Hash Cond: (t1_7.b = ((t1_12.a + t1_12.b) / 2)) + -> Seq Scan on prt2_p2 t1_7 -> Hash - -> Seq Scan on prt1_e_p3 t1_8 + -> Seq Scan on prt1_e_p2 t1_12 Filter: (c = 0) - -> Index Scan using iprt1_p3_a on prt1_p3 t1_2 - Index Cond: (a = t1_5.b) + -> Index Scan using iprt1_p2_a on prt1_p2 t1_2 + Index Cond: (a = t1_7.b) Filter: (b = 0) -(39 rows) + -> Nested Loop + -> HashAggregate + Group Key: t1_8.b + -> Hash Semi Join + Hash Cond: (t1_8.b = ((t1_13.a + t1_13.b) / 2)) + -> Seq Scan on prt2_p3 t1_8 + -> Hash + -> Seq Scan on prt1_e_p3 t1_13 + Filter: (c = 0) + -> Index Scan using iprt1_p3_a on prt1_p3 t1_3 + Index Cond: (a = t1_8.b) + Filter: (b = 0) + -> Nested Loop + -> HashAggregate + Group Key: t1_9.b + -> Hash Semi Join + Hash Cond: (t1_9.b = ((t1_14.a + t1_14.b) / 2)) + -> Seq Scan on prt2_p4 t1_9 + -> Hash + -> Seq Scan on prt1_e_p4 t1_14 + Filter: (c = 0) + -> Index Scan using iprt1_p4_a on prt1_p4 t1_4 + Index Cond: (a = t1_9.b) + Filter: (b = 0) +(63 rows) SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1 WHERE t1.b IN (SELECT (t1.a + t1.b)/2 FROM prt1_e t1 WHERE t1.c = 0)) AND t1.b = 0 ORDER BY t1.a; - a | b | c ------+---+------ - 0 | 0 | 0000 - 150 | 0 | 0150 - 300 | 0 | 0300 - 450 | 0 | 0450 -(4 rows) + a | b | c +------+---+------- + -150 | 0 | -0150 + 0 | 0 | 0000 + 150 | 0 | 0150 + 300 | 0 | 0300 + 450 | 0 | 0450 + 600 | 0 | 0600 + 750 | 0 | 0750 +(7 rows) -- test merge joins SET enable_hashjoin TO off; SET enable_nestloop TO off; EXPLAIN (COSTS OFF) SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1 WHERE t1.b IN (SELECT (t1.a + t1.b)/2 FROM prt1_e t1 WHERE t1.c = 0)) AND t1.b = 0 ORDER BY t1.a; - QUERY PLAN ----------------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------ Merge Append Sort Key: t1.a -> Merge Semi Join - Merge Cond: (t1.a = t1_3.b) + Merge Cond: (t1.a = t1_5.b) -> Sort Sort Key: t1.a - -> Seq Scan on prt1_p1 t1 + -> Seq Scan on prt1_p0 t1 Filter: (b = 0) -> Merge Semi Join - Merge Cond: (t1_3.b = (((t1_6.a + t1_6.b) / 2))) + Merge Cond: (t1_5.b = (((t1_10.a + t1_10.b) / 2))) -> Sort - Sort Key: t1_3.b - -> Seq Scan on prt2_p1 t1_3 + Sort Key: t1_5.b + -> Seq Scan on prt2_p0 t1_5 -> Sort - Sort Key: (((t1_6.a + t1_6.b) / 2)) - -> Seq Scan on prt1_e_p1 t1_6 + Sort Key: (((t1_10.a + t1_10.b) / 2)) + -> Seq Scan on prt1_e_p0 t1_10 Filter: (c = 0) -> Merge Semi Join - Merge Cond: (t1_1.a = t1_4.b) + Merge Cond: (t1_1.a = t1_6.b) -> Sort Sort Key: t1_1.a - -> Seq Scan on prt1_p2 t1_1 + -> Seq Scan on prt1_p1 t1_1 Filter: (b = 0) -> Merge Semi Join - Merge Cond: (t1_4.b = (((t1_7.a + t1_7.b) / 2))) + Merge Cond: (t1_6.b = (((t1_11.a + t1_11.b) / 2))) -> Sort - Sort Key: t1_4.b - -> Seq Scan on prt2_p2 t1_4 + Sort Key: t1_6.b + -> Seq Scan on prt2_p1 t1_6 -> Sort - Sort Key: (((t1_7.a + t1_7.b) / 2)) - -> Seq Scan on prt1_e_p2 t1_7 + Sort Key: (((t1_11.a + t1_11.b) / 2)) + -> Seq Scan on prt1_e_p1 t1_11 Filter: (c = 0) -> Merge Semi Join - Merge Cond: (t1_2.a = t1_5.b) + Merge Cond: (t1_2.a = t1_7.b) -> Sort Sort Key: t1_2.a - -> Seq Scan on prt1_p3 t1_2 + -> Seq Scan on prt1_p2 t1_2 Filter: (b = 0) -> Merge Semi Join - Merge Cond: (t1_5.b = (((t1_8.a + t1_8.b) / 2))) + Merge Cond: (t1_7.b = (((t1_12.a + t1_12.b) / 2))) -> Sort - Sort Key: t1_5.b - -> Seq Scan on prt2_p3 t1_5 + Sort Key: t1_7.b + -> Seq Scan on prt2_p2 t1_7 -> Sort - Sort Key: (((t1_8.a + t1_8.b) / 2)) - -> Seq Scan on prt1_e_p3 t1_8 + Sort Key: (((t1_12.a + t1_12.b) / 2)) + -> Seq Scan on prt1_e_p2 t1_12 Filter: (c = 0) -(47 rows) + -> Merge Semi Join + Merge Cond: (t1_3.a = t1_8.b) + -> Sort + Sort Key: t1_3.a + -> Seq Scan on prt1_p3 t1_3 + Filter: (b = 0) + -> Merge Semi Join + Merge Cond: (t1_8.b = (((t1_13.a + t1_13.b) / 2))) + -> Sort + Sort Key: t1_8.b + -> Seq Scan on prt2_p3 t1_8 + -> Sort + Sort Key: (((t1_13.a + t1_13.b) / 2)) + -> Seq Scan on prt1_e_p3 t1_13 + Filter: (c = 0) + -> Merge Semi Join + Merge Cond: (t1_4.a = t1_9.b) + -> Sort + Sort Key: t1_4.a + -> Seq Scan on prt1_p4 t1_4 + Filter: (b = 0) + -> Merge Semi Join + Merge Cond: (t1_9.b = (((t1_14.a + t1_14.b) / 2))) + -> Sort + Sort Key: t1_9.b + -> Seq Scan on prt2_p4 t1_9 + -> Sort + Sort Key: (((t1_14.a + t1_14.b) / 2)) + -> Seq Scan on prt1_e_p4 t1_14 + Filter: (c = 0) +(77 rows) SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1 WHERE t1.b IN (SELECT (t1.a + t1.b)/2 FROM prt1_e t1 WHERE t1.c = 0)) AND t1.b = 0 ORDER BY t1.a; - a | b | c ------+---+------ - 0 | 0 | 0000 - 150 | 0 | 0150 - 300 | 0 | 0300 - 450 | 0 | 0450 -(4 rows) + a | b | c +------+---+------- + -150 | 0 | -0150 + 0 | 0 | 0000 + 150 | 0 | 0150 + 300 | 0 | 0300 + 450 | 0 | 0450 + 600 | 0 | 0600 + 750 | 0 | 0750 +(7 rows) EXPLAIN (COSTS OFF) SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t3.c = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; @@ -933,14 +1438,14 @@ SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 Merge Cond: ((((t3.a + t3.b) / 2)) = t1.a) -> Sort Sort Key: (((t3.a + t3.b) / 2)) - -> Seq Scan on prt1_e_p1 t3 + -> Seq Scan on prt1_e_p0 t3 Filter: (c = 0) -> Sort Sort Key: t1.a - -> Seq Scan on prt1_p1 t1 + -> Seq Scan on prt1_p0 t1 -> Sort Sort Key: t2.b - -> Seq Scan on prt2_p1 t2 + -> Seq Scan on prt2_p0 t2 -> Merge Left Join Merge Cond: (t1_1.a = t2_1.b) -> Sort @@ -949,14 +1454,14 @@ SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 Merge Cond: ((((t3_1.a + t3_1.b) / 2)) = t1_1.a) -> Sort Sort Key: (((t3_1.a + t3_1.b) / 2)) - -> Seq Scan on prt1_e_p2 t3_1 + -> Seq Scan on prt1_e_p1 t3_1 Filter: (c = 0) -> Sort Sort Key: t1_1.a - -> Seq Scan on prt1_p2 t1_1 + -> Seq Scan on prt1_p1 t1_1 -> Sort Sort Key: t2_1.b - -> Seq Scan on prt2_p2 t2_1 + -> Seq Scan on prt2_p1 t2_1 -> Merge Left Join Merge Cond: (t1_2.a = t2_2.b) -> Sort @@ -965,32 +1470,74 @@ SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 Merge Cond: ((((t3_2.a + t3_2.b) / 2)) = t1_2.a) -> Sort Sort Key: (((t3_2.a + t3_2.b) / 2)) - -> Seq Scan on prt1_e_p3 t3_2 + -> Seq Scan on prt1_e_p2 t3_2 Filter: (c = 0) -> Sort Sort Key: t1_2.a - -> Seq Scan on prt1_p3 t1_2 + -> Seq Scan on prt1_p2 t1_2 -> Sort Sort Key: t2_2.b - -> Seq Scan on prt2_p3 t2_2 -(51 rows) + -> Seq Scan on prt2_p2 t2_2 + -> Merge Left Join + Merge Cond: (t1_3.a = t2_3.b) + -> Sort + Sort Key: t1_3.a + -> Merge Left Join + Merge Cond: ((((t3_3.a + t3_3.b) / 2)) = t1_3.a) + -> Sort + Sort Key: (((t3_3.a + t3_3.b) / 2)) + -> Seq Scan on prt1_e_p3 t3_3 + Filter: (c = 0) + -> Sort + Sort Key: t1_3.a + -> Seq Scan on prt1_p3 t1_3 + -> Sort + Sort Key: t2_3.b + -> Seq Scan on prt2_p3 t2_3 + -> Merge Left Join + Merge Cond: (t1_4.a = t2_4.b) + -> Sort + Sort Key: t1_4.a + -> Merge Left Join + Merge Cond: ((((t3_4.a + t3_4.b) / 2)) = t1_4.a) + -> Sort + Sort Key: (((t3_4.a + t3_4.b) / 2)) + -> Seq Scan on prt1_e_p4 t3_4 + Filter: (c = 0) + -> Sort + Sort Key: t1_4.a + -> Seq Scan on prt1_p4 t1_4 + -> Sort + Sort Key: t2_4.b + -> Seq Scan on prt2_p4 t2_4 +(83 rows) SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t3.c = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; - a | c | b | c | ?column? | c ------+------+-----+------+----------+--- - 0 | 0000 | 0 | 0000 | 0 | 0 - 50 | 0050 | | | 100 | 0 - 100 | 0100 | | | 200 | 0 - 150 | 0150 | 150 | 0150 | 300 | 0 - 200 | 0200 | | | 400 | 0 - 250 | 0250 | | | 500 | 0 - 300 | 0300 | 300 | 0300 | 600 | 0 - 350 | 0350 | | | 700 | 0 - 400 | 0400 | | | 800 | 0 - 450 | 0450 | 450 | 0450 | 900 | 0 - 500 | 0500 | | | 1000 | 0 - 550 | 0550 | | | 1100 | 0 -(12 rows) + a | c | b | c | ?column? | c +------+-------+------+-------+----------+--- + -250 | -0250 | | | -500 | 0 + -200 | -0200 | | | -400 | 0 + -150 | -0150 | -150 | -0150 | -300 | 0 + -100 | -0100 | | | -200 | 0 + -50 | -0050 | | | -100 | 0 + 0 | 0000 | 0 | 0000 | 0 | 0 + 0 | 0000 | 0 | 0000 | 0 | 0 + 50 | 0050 | | | 100 | 0 + 100 | 0100 | | | 200 | 0 + 150 | 0150 | 150 | 0150 | 300 | 0 + 200 | 0200 | | | 400 | 0 + 250 | 0250 | | | 500 | 0 + 300 | 0300 | 300 | 0300 | 600 | 0 + 350 | 0350 | | | 700 | 0 + 400 | 0400 | | | 800 | 0 + 450 | 0450 | 450 | 0450 | 900 | 0 + 500 | 0500 | | | 1000 | 0 + 550 | 0550 | | | 1100 | 0 + 600 | 0600 | 600 | 0600 | 1200 | 0 + 650 | 0650 | | | 1300 | 0 + 700 | 0700 | | | 1400 | 0 + 750 | 0750 | 750 | 0750 | 1500 | 0 +(22 rows) -- MergeAppend on nullable column EXPLAIN (COSTS OFF) @@ -998,8 +1545,18 @@ SELECT t1.a, t2.b FROM (SELECT * FROM prt1 WHERE a < 450) t1 LEFT JOIN (SELECT * QUERY PLAN ----------------------------------------------------------- Sort - Sort Key: prt1_p1.a, b + Sort Key: prt1_p0.a, b -> Append + -> Merge Left Join + Merge Cond: (prt1_p0.a = b) + -> Sort + Sort Key: prt1_p0.a + -> Seq Scan on prt1_p0 + Filter: ((a < 450) AND (b = 0)) + -> Sort + Sort Key: b + -> Result + One-Time Filter: false -> Merge Left Join Merge Cond: (prt1_p1.a = b) -> Sort @@ -1020,21 +1577,26 @@ SELECT t1.a, t2.b FROM (SELECT * FROM prt1 WHERE a < 450) t1 LEFT JOIN (SELECT * Sort Key: prt2_p2.b -> Seq Scan on prt2_p2 Filter: (b > 250) -(23 rows) +(33 rows) SELECT t1.a, t2.b FROM (SELECT * FROM prt1 WHERE a < 450) t1 LEFT JOIN (SELECT * FROM prt2 WHERE b > 250) t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a, t2.b; - a | b ------+----- - 0 | - 50 | - 100 | - 150 | - 200 | - 250 | - 300 | 300 - 350 | - 400 | -(9 rows) + a | b +------+----- + -250 | + -200 | + -150 | + -100 | + -50 | + 0 | + 50 | + 100 | + 150 | + 200 | + 250 | + 300 | 300 + 350 | + 400 | +(14 rows) -- merge join when expression with whole-row reference needs to be sorted; -- partitionwise join does not apply @@ -1048,175 +1610,2412 @@ SELECT t1.a, t2.b FROM prt1 t1, prt2 t2 WHERE t1::text = t2::text AND t1.a = t2. Sort Key: t1.a, ((((t1.*)::prt1))::text) -> Result -> Append - -> Seq Scan on prt1_p1 t1 - -> Seq Scan on prt1_p2 t1_1 - -> Seq Scan on prt1_p3 t1_2 + -> Seq Scan on prt1_p0 t1 + -> Seq Scan on prt1_p1 t1_1 + -> Seq Scan on prt1_p2 t1_2 + -> Seq Scan on prt1_p3 t1_3 + -> Seq Scan on prt1_p4 t1_4 -> Sort Sort Key: t2.b, ((((t2.*)::prt2))::text) -> Result -> Append - -> Seq Scan on prt2_p1 t2 - -> Seq Scan on prt2_p2 t2_1 - -> Seq Scan on prt2_p3 t2_2 -(16 rows) + -> Seq Scan on prt2_p0 t2 + -> Seq Scan on prt2_p1 t2_1 + -> Seq Scan on prt2_p2 t2_2 + -> Seq Scan on prt2_p3 t2_3 + -> Seq Scan on prt2_p4 t2_4 +(20 rows) SELECT t1.a, t2.b FROM prt1 t1, prt2 t2 WHERE t1::text = t2::text AND t1.a = t2.b ORDER BY t1.a; - a | b -----+---- - 0 | 0 - 6 | 6 - 12 | 12 - 18 | 18 - 24 | 24 -(5 rows) + a | b +-----+----- + -24 | -24 + -18 | -18 + -12 | -12 + -6 | -6 + 0 | 0 + 6 | 6 + 12 | 12 + 18 | 18 + 24 | 24 +(9 rows) RESET enable_hashjoin; RESET enable_nestloop; --- --- partitioned by multiple columns --- -CREATE TABLE prt1_m (a int, b int, c int) PARTITION BY RANGE(a, ((a + b)/2)); -CREATE TABLE prt1_m_p1 PARTITION OF prt1_m FOR VALUES FROM (0, 0) TO (250, 250); -CREATE TABLE prt1_m_p2 PARTITION OF prt1_m FOR VALUES FROM (250, 250) TO (500, 500); -CREATE TABLE prt1_m_p3 PARTITION OF prt1_m FOR VALUES FROM (500, 500) TO (600, 600); -INSERT INTO prt1_m SELECT i, i, i % 25 FROM generate_series(0, 599, 2) i; -ANALYZE prt1_m; -CREATE TABLE prt2_m (a int, b int, c int) PARTITION BY RANGE(((b + a)/2), b); -CREATE TABLE prt2_m_p1 PARTITION OF prt2_m FOR VALUES FROM (0, 0) TO (250, 250); -CREATE TABLE prt2_m_p2 PARTITION OF prt2_m FOR VALUES FROM (250, 250) TO (500, 500); -CREATE TABLE prt2_m_p3 PARTITION OF prt2_m FOR VALUES FROM (500, 500) TO (600, 600); -INSERT INTO prt2_m SELECT i, i, i % 25 FROM generate_series(0, 599, 3) i; -ANALYZE prt2_m; +-- test default partition behavior for range, partition-wise join is not +-- possible since more than one partition on one side matches default partition +-- on the other side. Default partition from prt1 matches default partition and +-- prt2_p4 from prt2 and default partition from prt2 matches default partition +-- and prt1_p0 from prt1 +ALTER TABLE prt1 DETACH PARTITION prt1_p3; +ALTER TABLE prt1 ATTACH PARTITION prt1_p3 DEFAULT; +ANALYZE prt1; +ALTER TABLE prt2 DETACH PARTITION prt2_p3; +ALTER TABLE prt2 ATTACH PARTITION prt2_p3 DEFAULT; +ANALYZE prt2; EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_m WHERE prt1_m.c = 0) t1 FULL JOIN (SELECT * FROM prt2_m WHERE prt2_m.c = 0) t2 ON (t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2) ORDER BY t1.a, t2.b; - QUERY PLAN ------------------------------------------------------------------------------------------------------------------------------------- +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.b = 0 ORDER BY t1.a, t2.b; + QUERY PLAN +-------------------------------------------------- + Sort + Sort Key: t1.a + -> Hash Join + Hash Cond: (t2.b = t1.a) + -> Append + -> Seq Scan on prt2_p0 t2 + -> Seq Scan on prt2_p1 t2_1 + -> Seq Scan on prt2_p2 t2_2 + -> Seq Scan on prt2_p4 t2_3 + -> Seq Scan on prt2_p3 t2_4 + -> Hash + -> Append + -> Seq Scan on prt1_p0 t1 + Filter: (b = 0) + -> Seq Scan on prt1_p1 t1_1 + Filter: (b = 0) + -> Seq Scan on prt1_p2 t1_2 + Filter: (b = 0) + -> Seq Scan on prt1_p4 t1_3 + Filter: (b = 0) + -> Seq Scan on prt1_p3 t1_4 + Filter: (b = 0) +(22 rows) + +-- partition-wise join should be possible when we drop the first and last +-- partitions from both sides +ALTER TABLE prt1 DETACH PARTITION prt1_p0; +ALTER TABLE prt1 DETACH PARTITION prt1_p4; +ANALYZE prt1; +ALTER TABLE prt2 DETACH PARTITION prt2_p0; +ALTER TABLE prt2 DETACH PARTITION prt2_p4; +ANALYZE prt2; +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.b = 0 ORDER BY t1.a, t2.b; + QUERY PLAN +-------------------------------------------------- + Sort + Sort Key: t1.a + -> Append + -> Hash Join + Hash Cond: (t2.b = t1.a) + -> Seq Scan on prt2_p1 t2 + -> Hash + -> Seq Scan on prt1_p1 t1 + Filter: (b = 0) + -> Hash Join + Hash Cond: (t2_1.b = t1_1.a) + -> Seq Scan on prt2_p2 t2_1 + -> Hash + -> Seq Scan on prt1_p2 t1_1 + Filter: (b = 0) + -> Hash Join + Hash Cond: (t2_2.b = t1_2.a) + -> Seq Scan on prt2_p3 t2_2 + -> Hash + -> Seq Scan on prt1_p3 t1_2 + Filter: (b = 0) +(21 rows) + +-- restore the partitioned tables for rest of the tests +ALTER TABLE prt1 ATTACH PARTITION prt1_p0 FOR VALUES FROM (MINVALUE) TO (0); +ALTER TABLE prt1 ATTACH PARTITION prt1_p4 FOR VALUES FROM (600) TO (800); +ALTER TABLE prt1 DETACH PARTITION prt1_p3; +ALTER TABLE prt1 ATTACH PARTITION prt1_p3 FOR VALUES FROM (500) TO (600); +ANALYZE prt1; +ALTER TABLE prt2 ATTACH PARTITION prt2_p0 FOR VALUES FROM (-250) TO (0); +ALTER TABLE prt2 ATTACH PARTITION prt2_p4 FOR VALUES FROM (600) TO (MAXVALUE); +ALTER TABLE prt2 DETACH PARTITION prt2_p3; +ALTER TABLE prt2 ATTACH PARTITION prt2_p3 FOR VALUES FROM (500) TO (600); +ANALYZE prt2; +-- Add an extra partition to prt2 , Partition-wise join is possible with +-- extra partitions on inner side are allowed +DROP TABLE prt2_p4; +CREATE TABLE prt2_p4 PARTITION OF prt2 FOR VALUES FROM (600) TO (800); +CREATE TABLE prt2_p5 PARTITION OF prt2 FOR VALUES FROM (800) TO (1000); +INSERT INTO prt2 SELECT i % 25, i, to_char(i, 'FM0000') FROM generate_series(600, 999) i WHERE i % 3 = 0; +ANALYZE prt2; +-- inner join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 INNER JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a; + QUERY PLAN +--------------------------------------------------------------- + Sort + Sort Key: t1.a + -> Append + -> Hash Join + Hash Cond: (t2.b = t1.a) + -> Seq Scan on prt2_p0 t2 + -> Hash + -> Seq Scan on prt1_p0 t1 + Filter: (b = 0) + -> Hash Join + Hash Cond: (t2_1.b = t1_1.a) + -> Seq Scan on prt2_p1 t2_1 + -> Hash + -> Seq Scan on prt1_p1 t1_1 + Filter: (b = 0) + -> Hash Join + Hash Cond: (t2_2.b = t1_2.a) + -> Seq Scan on prt2_p2 t2_2 + -> Hash + -> Seq Scan on prt1_p2 t1_2 + Filter: (b = 0) + -> Nested Loop + -> Seq Scan on prt1_p3 t1_3 + Filter: (b = 0) + -> Index Scan using iprt2_p3_b on prt2_p3 t2_3 + Index Cond: (b = t1_3.a) + -> Hash Join + Hash Cond: (t2_4.b = t1_4.a) + -> Seq Scan on prt2_p4 t2_4 + -> Hash + -> Seq Scan on prt1_p4 t1_4 + Filter: (b = 0) +(32 rows) + +SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 INNER JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a; + a | c | a | c +------+-------+---+------- + -150 | -0150 | 0 | -0150 + 0 | 0000 | 0 | 0000 + 150 | 0150 | 0 | 0150 + 300 | 0300 | 0 | 0300 + 450 | 0450 | 0 | 0450 + 600 | 0600 | 0 | 0600 + 750 | 0750 | 0 | 0750 +(7 rows) + +-- left join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a; + QUERY PLAN +--------------------------------------------------------------- + Sort + Sort Key: t1.a + -> Append + -> Hash Right Join + Hash Cond: (t2.b = t1.a) + -> Seq Scan on prt2_p0 t2 + -> Hash + -> Seq Scan on prt1_p0 t1 + Filter: (b = 0) + -> Hash Right Join + Hash Cond: (t2_1.b = t1_1.a) + -> Seq Scan on prt2_p1 t2_1 + -> Hash + -> Seq Scan on prt1_p1 t1_1 + Filter: (b = 0) + -> Hash Right Join + Hash Cond: (t2_2.b = t1_2.a) + -> Seq Scan on prt2_p2 t2_2 + -> Hash + -> Seq Scan on prt1_p2 t1_2 + Filter: (b = 0) + -> Nested Loop Left Join + -> Seq Scan on prt1_p3 t1_3 + Filter: (b = 0) + -> Index Scan using iprt2_p3_b on prt2_p3 t2_3 + Index Cond: (t1_3.a = b) + -> Hash Right Join + Hash Cond: (t2_4.b = t1_4.a) + -> Seq Scan on prt2_p4 t2_4 + -> Hash + -> Seq Scan on prt1_p4 t1_4 + Filter: (b = 0) +(32 rows) + +SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a; + a | c | a | c +------+-------+---+------- + -250 | -0250 | | + -200 | -0200 | | + -150 | -0150 | 0 | -0150 + -100 | -0100 | | + -50 | -0050 | | + 0 | 0000 | 0 | 0000 + 50 | 0050 | | + 100 | 0100 | | + 150 | 0150 | 0 | 0150 + 200 | 0200 | | + 250 | 0250 | | + 300 | 0300 | 0 | 0300 + 350 | 0350 | | + 400 | 0400 | | + 450 | 0450 | 0 | 0450 + 500 | 0500 | | + 550 | 0550 | | + 600 | 0600 | 0 | 0600 + 650 | 0650 | | + 700 | 0700 | | + 750 | 0750 | 0 | 0750 +(21 rows) + +-- semi join +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from prt1 t1 where exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c; + QUERY PLAN +-------------------------------------------------------------------- + Sort + Sort Key: t1.a, t1.c + -> Append + -> Hash Semi Join + Hash Cond: (t1.a = t2.b) + -> Seq Scan on prt1_p0 t1 + Filter: (b = 0) + -> Hash + -> Seq Scan on prt2_p0 t2 + -> Hash Semi Join + Hash Cond: (t1_1.a = t2_1.b) + -> Seq Scan on prt1_p1 t1_1 + Filter: (b = 0) + -> Hash + -> Seq Scan on prt2_p1 t2_1 + -> Hash Semi Join + Hash Cond: (t1_2.a = t2_2.b) + -> Seq Scan on prt1_p2 t1_2 + Filter: (b = 0) + -> Hash + -> Seq Scan on prt2_p2 t2_2 + -> Nested Loop Semi Join + -> Seq Scan on prt1_p3 t1_3 + Filter: (b = 0) + -> Index Only Scan using iprt2_p3_b on prt2_p3 t2_3 + Index Cond: (b = t1_3.a) + -> Hash Semi Join + Hash Cond: (t1_4.a = t2_4.b) + -> Seq Scan on prt1_p4 t1_4 + Filter: (b = 0) + -> Hash + -> Seq Scan on prt2_p4 t2_4 +(32 rows) + +select t1.a, t1.b, t1.c from prt1 t1 where exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c; + a | b | c +------+---+------- + -150 | 0 | -0150 + 0 | 0 | 0000 + 150 | 0 | 0150 + 300 | 0 | 0300 + 450 | 0 | 0450 + 600 | 0 | 0600 + 750 | 0 | 0750 +(7 rows) + +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from prt2 t1 where exists (select 1 from prt1 t2 WHERE t1.b = t2.a) and t1.a = 0 order by t1.a, t1.b, t1.c; + QUERY PLAN +-------------------------------------------------------------------- + Sort + Sort Key: t1.b, t1.c + -> Append + -> Nested Loop Semi Join + -> Seq Scan on prt2_p0 t1 + Filter: (a = 0) + -> Index Only Scan using iprt1_p0_a on prt1_p0 t2 + Index Cond: (a = t1.b) + -> Nested Loop Semi Join + -> Seq Scan on prt2_p1 t1_1 + Filter: (a = 0) + -> Index Only Scan using iprt1_p1_a on prt1_p1 t2_1 + Index Cond: (a = t1_1.b) + -> Nested Loop Semi Join + -> Seq Scan on prt2_p2 t1_2 + Filter: (a = 0) + -> Index Only Scan using iprt1_p2_a on prt1_p2 t2_2 + Index Cond: (a = t1_2.b) + -> Nested Loop Semi Join + -> Seq Scan on prt2_p3 t1_3 + Filter: (a = 0) + -> Index Only Scan using iprt1_p3_a on prt1_p3 t2_3 + Index Cond: (a = t1_3.b) + -> Nested Loop Semi Join + -> Seq Scan on prt2_p4 t1_4 + Filter: (a = 0) + -> Index Only Scan using iprt1_p4_a on prt1_p4 t2_4 + Index Cond: (a = t1_4.b) +(28 rows) + +select t1.a, t1.b, t1.c from prt2 t1 where exists (select 1 from prt1 t2 WHERE t1.b = t2.a) and t1.a = 0 order by t1.a, t1.b, t1.c; + a | b | c +---+------+------- + 0 | -150 | -0150 + 0 | 0 | 0000 + 0 | 150 | 0150 + 0 | 300 | 0300 + 0 | 450 | 0450 + 0 | 600 | 0600 + 0 | 750 | 0750 +(7 rows) + +-- anti join +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from prt1 t1 where not exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c; + QUERY PLAN +-------------------------------------------------------------------- + Sort + Sort Key: t1.a, t1.c + -> Append + -> Hash Anti Join + Hash Cond: (t1.a = t2.b) + -> Seq Scan on prt1_p0 t1 + Filter: (b = 0) + -> Hash + -> Seq Scan on prt2_p0 t2 + -> Hash Anti Join + Hash Cond: (t1_1.a = t2_1.b) + -> Seq Scan on prt1_p1 t1_1 + Filter: (b = 0) + -> Hash + -> Seq Scan on prt2_p1 t2_1 + -> Hash Anti Join + Hash Cond: (t1_2.a = t2_2.b) + -> Seq Scan on prt1_p2 t1_2 + Filter: (b = 0) + -> Hash + -> Seq Scan on prt2_p2 t2_2 + -> Nested Loop Anti Join + -> Seq Scan on prt1_p3 t1_3 + Filter: (b = 0) + -> Index Only Scan using iprt2_p3_b on prt2_p3 t2_3 + Index Cond: (b = t1_3.a) + -> Hash Anti Join + Hash Cond: (t1_4.a = t2_4.b) + -> Seq Scan on prt1_p4 t1_4 + Filter: (b = 0) + -> Hash + -> Seq Scan on prt2_p4 t2_4 +(32 rows) + +select t1.a, t1.b, t1.c from prt1 t1 where not exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c; + a | b | c +------+---+------- + -250 | 0 | -0250 + -200 | 0 | -0200 + -100 | 0 | -0100 + -50 | 0 | -0050 + 50 | 0 | 0050 + 100 | 0 | 0100 + 200 | 0 | 0200 + 250 | 0 | 0250 + 350 | 0 | 0350 + 400 | 0 | 0400 + 500 | 0 | 0500 + 550 | 0 | 0550 + 650 | 0 | 0650 + 700 | 0 | 0700 +(14 rows) + +-- 3-way join when not every pair of joining relation can use partition-wise +-- join +EXPLAIN (COSTS OFF) +SELECT t1.a, t2.a, t3.c FROM prt1 t1 RIGHT JOIN prt2 t2 ON (t1.a = t2.b) INNER JOIN prt1 t3 ON (t2.b = t3.a) WHERE t2.a = 0 ORDER BY t1.a, t2.a, t3.c; + QUERY PLAN +--------------------------------------------------------------------- + Sort + Sort Key: t1.a, t3.c + -> Append + -> Nested Loop Left Join + -> Nested Loop + -> Seq Scan on prt2_p0 t2 + Filter: (a = 0) + -> Index Scan using iprt1_p0_a on prt1_p0 t3 + Index Cond: (a = t2.b) + -> Index Only Scan using iprt1_p0_a on prt1_p0 t1 + Index Cond: (a = t2.b) + -> Hash Right Join + Hash Cond: (t1_1.a = t2_1.b) + -> Seq Scan on prt1_p1 t1_1 + -> Hash + -> Hash Join + Hash Cond: (t3_1.a = t2_1.b) + -> Seq Scan on prt1_p1 t3_1 + -> Hash + -> Seq Scan on prt2_p1 t2_1 + Filter: (a = 0) + -> Nested Loop Left Join + -> Nested Loop + -> Seq Scan on prt2_p2 t2_2 + Filter: (a = 0) + -> Index Scan using iprt1_p2_a on prt1_p2 t3_2 + Index Cond: (a = t2_2.b) + -> Index Only Scan using iprt1_p2_a on prt1_p2 t1_2 + Index Cond: (a = t2_2.b) + -> Nested Loop Left Join + -> Nested Loop + -> Seq Scan on prt2_p3 t2_3 + Filter: (a = 0) + -> Index Scan using iprt1_p3_a on prt1_p3 t3_3 + Index Cond: (a = t2_3.b) + -> Index Only Scan using iprt1_p3_a on prt1_p3 t1_3 + Index Cond: (a = t2_3.b) + -> Hash Right Join + Hash Cond: (t1_4.a = t2_4.b) + -> Seq Scan on prt1_p4 t1_4 + -> Hash + -> Hash Join + Hash Cond: (t3_4.a = t2_4.b) + -> Seq Scan on prt1_p4 t3_4 + -> Hash + -> Seq Scan on prt2_p4 t2_4 + Filter: (a = 0) +(47 rows) + +SELECT t1.a, t2.a, t3.c FROM prt1 t1 RIGHT JOIN prt2 t2 ON (t1.a = t2.b) INNER JOIN prt1 t3 ON (t2.b = t3.a) WHERE t2.a = 0 ORDER BY t1.a, t2.a, t3.c; + a | a | c +------+---+------- + -150 | 0 | -0150 + 0 | 0 | 0000 + 150 | 0 | 0150 + 300 | 0 | 0300 + 450 | 0 | 0450 + 600 | 0 | 0600 + 750 | 0 | 0750 +(7 rows) + +-- partition-wise join can not handle missing partition on the inner side +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b WHERE t2.a = 0 ORDER BY t2.b; + QUERY PLAN +-------------------------------------------------- + Sort + Sort Key: t2.b + -> Hash Right Join + Hash Cond: (t1.a = t2.b) + -> Append + -> Seq Scan on prt1_p0 t1 + -> Seq Scan on prt1_p1 t1_1 + -> Seq Scan on prt1_p2 t1_2 + -> Seq Scan on prt1_p3 t1_3 + -> Seq Scan on prt1_p4 t1_4 + -> Hash + -> Append + -> Seq Scan on prt2_p0 t2 + Filter: (a = 0) + -> Seq Scan on prt2_p1 t2_1 + Filter: (a = 0) + -> Seq Scan on prt2_p2 t2_2 + Filter: (a = 0) + -> Seq Scan on prt2_p3 t2_3 + Filter: (a = 0) + -> Seq Scan on prt2_p4 t2_4 + Filter: (a = 0) + -> Seq Scan on prt2_p5 t2_5 + Filter: (a = 0) +(24 rows) + +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 FULL JOIN prt2 t2 ON t1.a = t2.b WHERE coalesce(t1.b, 0) + coalesce(t2.a, 0) = 0 ORDER BY t1.a, t2.a; + QUERY PLAN +--------------------------------------------------------------- + Sort + Sort Key: t1.a, t2.a + -> Hash Full Join + Hash Cond: (t1.a = t2.b) + Filter: ((COALESCE(t1.b, 0) + COALESCE(t2.a, 0)) = 0) + -> Append + -> Seq Scan on prt1_p0 t1 + -> Seq Scan on prt1_p1 t1_1 + -> Seq Scan on prt1_p2 t1_2 + -> Seq Scan on prt1_p3 t1_3 + -> Seq Scan on prt1_p4 t1_4 + -> Hash + -> Append + -> Seq Scan on prt2_p0 t2 + -> Seq Scan on prt2_p1 t2_1 + -> Seq Scan on prt2_p2 t2_2 + -> Seq Scan on prt2_p3 t2_3 + -> Seq Scan on prt2_p4 t2_4 + -> Seq Scan on prt2_p5 t2_5 +(19 rows) + +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from prt2 t1 where not exists (select 1 from prt1 t2 WHERE t1.b = t2.a) and t1.a = 0 order by t1.a, t1.b, t1.c; + QUERY PLAN +-------------------------------------------------- + Sort + Sort Key: t1.b, t1.c + -> Hash Anti Join + Hash Cond: (t1.b = t2.a) + -> Append + -> Seq Scan on prt2_p0 t1 + Filter: (a = 0) + -> Seq Scan on prt2_p1 t1_1 + Filter: (a = 0) + -> Seq Scan on prt2_p2 t1_2 + Filter: (a = 0) + -> Seq Scan on prt2_p3 t1_3 + Filter: (a = 0) + -> Seq Scan on prt2_p4 t1_4 + Filter: (a = 0) + -> Seq Scan on prt2_p5 t1_5 + Filter: (a = 0) + -> Hash + -> Append + -> Seq Scan on prt1_p0 t2 + -> Seq Scan on prt1_p1 t2_1 + -> Seq Scan on prt1_p2 t2_2 + -> Seq Scan on prt1_p3 t2_3 + -> Seq Scan on prt1_p4 t2_4 +(24 rows) + +-- Partition-wise join can not handle the case when one partition from one side +-- matches with multiple partitions on the other side +DROP TABLE prt2_p4; +DROP TABLE prt2_p5; +CREATE TABLE prt2_p4 PARTITION OF prt2 FOR VALUES FROM (600) TO (700); +CREATE TABLE prt2_p5 PARTITION OF prt2 FOR VALUES FROM (700) TO (1000); +INSERT INTO prt2 SELECT i % 25, i, to_char(i, 'FM0000') FROM generate_series(600, 999, 3) i; +ANALYZE prt2; +-- inner join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 INNER JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a; + QUERY PLAN +-------------------------------------------------- + Sort + Sort Key: t1.a + -> Hash Join + Hash Cond: (t2.b = t1.a) + -> Append + -> Seq Scan on prt2_p0 t2 + -> Seq Scan on prt2_p1 t2_1 + -> Seq Scan on prt2_p2 t2_2 + -> Seq Scan on prt2_p3 t2_3 + -> Seq Scan on prt2_p4 t2_4 + -> Seq Scan on prt2_p5 t2_5 + -> Hash + -> Append + -> Seq Scan on prt1_p0 t1 + Filter: (b = 0) + -> Seq Scan on prt1_p1 t1_1 + Filter: (b = 0) + -> Seq Scan on prt1_p2 t1_2 + Filter: (b = 0) + -> Seq Scan on prt1_p3 t1_3 + Filter: (b = 0) + -> Seq Scan on prt1_p4 t1_4 + Filter: (b = 0) +(23 rows) + +-- left join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a; + QUERY PLAN +-------------------------------------------------- + Sort + Sort Key: t1.a + -> Hash Right Join + Hash Cond: (t2.b = t1.a) + -> Append + -> Seq Scan on prt2_p0 t2 + -> Seq Scan on prt2_p1 t2_1 + -> Seq Scan on prt2_p2 t2_2 + -> Seq Scan on prt2_p3 t2_3 + -> Seq Scan on prt2_p4 t2_4 + -> Seq Scan on prt2_p5 t2_5 + -> Hash + -> Append + -> Seq Scan on prt1_p0 t1 + Filter: (b = 0) + -> Seq Scan on prt1_p1 t1_1 + Filter: (b = 0) + -> Seq Scan on prt1_p2 t1_2 + Filter: (b = 0) + -> Seq Scan on prt1_p3 t1_3 + Filter: (b = 0) + -> Seq Scan on prt1_p4 t1_4 + Filter: (b = 0) +(23 rows) + +-- right join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b WHERE t2.a = 0 ORDER BY t2.a; + QUERY PLAN +-------------------------------------------- + Hash Right Join + Hash Cond: (t1.a = t2.b) + -> Append + -> Seq Scan on prt1_p0 t1 + -> Seq Scan on prt1_p1 t1_1 + -> Seq Scan on prt1_p2 t1_2 + -> Seq Scan on prt1_p3 t1_3 + -> Seq Scan on prt1_p4 t1_4 + -> Hash + -> Append + -> Seq Scan on prt2_p0 t2 + Filter: (a = 0) + -> Seq Scan on prt2_p1 t2_1 + Filter: (a = 0) + -> Seq Scan on prt2_p2 t2_2 + Filter: (a = 0) + -> Seq Scan on prt2_p3 t2_3 + Filter: (a = 0) + -> Seq Scan on prt2_p4 t2_4 + Filter: (a = 0) + -> Seq Scan on prt2_p5 t2_5 + Filter: (a = 0) +(22 rows) + +-- full join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 FULL JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b + t2.a = 0 ORDER BY t1.a, t2.a; + QUERY PLAN +-------------------------------------------------- + Sort + Sort Key: t1.a, t2.a + -> Hash Join + Hash Cond: (t1.a = t2.b) + Join Filter: ((t1.b + t2.a) = 0) + -> Append + -> Seq Scan on prt1_p0 t1 + -> Seq Scan on prt1_p1 t1_1 + -> Seq Scan on prt1_p2 t1_2 + -> Seq Scan on prt1_p3 t1_3 + -> Seq Scan on prt1_p4 t1_4 + -> Hash + -> Append + -> Seq Scan on prt2_p0 t2 + -> Seq Scan on prt2_p1 t2_1 + -> Seq Scan on prt2_p2 t2_2 + -> Seq Scan on prt2_p3 t2_3 + -> Seq Scan on prt2_p4 t2_4 + -> Seq Scan on prt2_p5 t2_5 +(19 rows) + +-- semi join +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from prt1 t1 where exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c; + QUERY PLAN +-------------------------------------------------- + Sort + Sort Key: t1.a, t1.c + -> Hash Semi Join + Hash Cond: (t1.a = t2.b) + -> Append + -> Seq Scan on prt1_p0 t1 + Filter: (b = 0) + -> Seq Scan on prt1_p1 t1_1 + Filter: (b = 0) + -> Seq Scan on prt1_p2 t1_2 + Filter: (b = 0) + -> Seq Scan on prt1_p3 t1_3 + Filter: (b = 0) + -> Seq Scan on prt1_p4 t1_4 + Filter: (b = 0) + -> Hash + -> Append + -> Seq Scan on prt2_p0 t2 + -> Seq Scan on prt2_p1 t2_1 + -> Seq Scan on prt2_p2 t2_2 + -> Seq Scan on prt2_p3 t2_3 + -> Seq Scan on prt2_p4 t2_4 + -> Seq Scan on prt2_p5 t2_5 +(23 rows) + +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from prt2 t1 where exists (select 1 from prt1 t2 WHERE t1.b = t2.a) and t1.a = 0 order by t1.a, t1.b, t1.c; + QUERY PLAN +-------------------------------------------------- + Sort + Sort Key: t1.b, t1.c + -> Hash Semi Join + Hash Cond: (t1.b = t2.a) + -> Append + -> Seq Scan on prt2_p0 t1 + Filter: (a = 0) + -> Seq Scan on prt2_p1 t1_1 + Filter: (a = 0) + -> Seq Scan on prt2_p2 t1_2 + Filter: (a = 0) + -> Seq Scan on prt2_p3 t1_3 + Filter: (a = 0) + -> Seq Scan on prt2_p4 t1_4 + Filter: (a = 0) + -> Seq Scan on prt2_p5 t1_5 + Filter: (a = 0) + -> Hash + -> Append + -> Seq Scan on prt1_p0 t2 + -> Seq Scan on prt1_p1 t2_1 + -> Seq Scan on prt1_p2 t2_2 + -> Seq Scan on prt1_p3 t2_3 + -> Seq Scan on prt1_p4 t2_4 +(24 rows) + +-- anti join +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from prt1 t1 where not exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c; + QUERY PLAN +-------------------------------------------------- + Sort + Sort Key: t1.a, t1.c + -> Hash Anti Join + Hash Cond: (t1.a = t2.b) + -> Append + -> Seq Scan on prt1_p0 t1 + Filter: (b = 0) + -> Seq Scan on prt1_p1 t1_1 + Filter: (b = 0) + -> Seq Scan on prt1_p2 t1_2 + Filter: (b = 0) + -> Seq Scan on prt1_p3 t1_3 + Filter: (b = 0) + -> Seq Scan on prt1_p4 t1_4 + Filter: (b = 0) + -> Hash + -> Append + -> Seq Scan on prt2_p0 t2 + -> Seq Scan on prt2_p1 t2_1 + -> Seq Scan on prt2_p2 t2_2 + -> Seq Scan on prt2_p3 t2_3 + -> Seq Scan on prt2_p4 t2_4 + -> Seq Scan on prt2_p5 t2_5 +(23 rows) + +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from prt2 t1 where not exists (select 1 from prt1 t2 WHERE t1.b = t2.a) and t1.a = 0 order by t1.a, t1.b, t1.c; + QUERY PLAN +-------------------------------------------------- + Sort + Sort Key: t1.b, t1.c + -> Hash Anti Join + Hash Cond: (t1.b = t2.a) + -> Append + -> Seq Scan on prt2_p0 t1 + Filter: (a = 0) + -> Seq Scan on prt2_p1 t1_1 + Filter: (a = 0) + -> Seq Scan on prt2_p2 t1_2 + Filter: (a = 0) + -> Seq Scan on prt2_p3 t1_3 + Filter: (a = 0) + -> Seq Scan on prt2_p4 t1_4 + Filter: (a = 0) + -> Seq Scan on prt2_p5 t1_5 + Filter: (a = 0) + -> Hash + -> Append + -> Seq Scan on prt1_p0 t2 + -> Seq Scan on prt1_p1 t2_1 + -> Seq Scan on prt1_p2 t2_2 + -> Seq Scan on prt1_p3 t2_3 + -> Seq Scan on prt1_p4 t2_4 +(24 rows) + +-- +-- partitioned by multiple columns +-- +CREATE TABLE prt1_m (a int, b int, c int) PARTITION BY RANGE(a, ((a + b)/2)); +CREATE TABLE prt1_m_p1 PARTITION OF prt1_m FOR VALUES FROM (0, 0) TO (250, 250); +CREATE TABLE prt1_m_p2 PARTITION OF prt1_m FOR VALUES FROM (250, 250) TO (500, 500); +CREATE TABLE prt1_m_p3 PARTITION OF prt1_m FOR VALUES FROM (500, 500) TO (600, 600); +INSERT INTO prt1_m SELECT i, i, i % 25 FROM generate_series(0, 599, 2) i; +ANALYZE prt1_m; +CREATE TABLE prt2_m (a int, b int, c int) PARTITION BY RANGE(((b + a)/2), b); +CREATE TABLE prt2_m_p1 PARTITION OF prt2_m FOR VALUES FROM (0, 0) TO (250, 250); +CREATE TABLE prt2_m_p2 PARTITION OF prt2_m FOR VALUES FROM (250, 250) TO (500, 500); +CREATE TABLE prt2_m_p3 PARTITION OF prt2_m FOR VALUES FROM (500, 500) TO (600, 600); +INSERT INTO prt2_m SELECT i, i, i % 25 FROM generate_series(0, 599, 3) i; +ANALYZE prt2_m; +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_m WHERE prt1_m.c = 0) t1 FULL JOIN (SELECT * FROM prt2_m WHERE prt2_m.c = 0) t2 ON (t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2) ORDER BY t1.a, t2.b; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------ + Sort + Sort Key: prt1_m_p1.a, prt2_m_p1.b + -> Append + -> Hash Full Join + Hash Cond: ((prt1_m_p1.a = ((prt2_m_p1.b + prt2_m_p1.a) / 2)) AND (((prt1_m_p1.a + prt1_m_p1.b) / 2) = prt2_m_p1.b)) + -> Seq Scan on prt1_m_p1 + Filter: (c = 0) + -> Hash + -> Seq Scan on prt2_m_p1 + Filter: (c = 0) + -> Hash Full Join + Hash Cond: ((prt1_m_p2.a = ((prt2_m_p2.b + prt2_m_p2.a) / 2)) AND (((prt1_m_p2.a + prt1_m_p2.b) / 2) = prt2_m_p2.b)) + -> Seq Scan on prt1_m_p2 + Filter: (c = 0) + -> Hash + -> Seq Scan on prt2_m_p2 + Filter: (c = 0) + -> Hash Full Join + Hash Cond: ((prt1_m_p3.a = ((prt2_m_p3.b + prt2_m_p3.a) / 2)) AND (((prt1_m_p3.a + prt1_m_p3.b) / 2) = prt2_m_p3.b)) + -> Seq Scan on prt1_m_p3 + Filter: (c = 0) + -> Hash + -> Seq Scan on prt2_m_p3 + Filter: (c = 0) +(24 rows) + +SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_m WHERE prt1_m.c = 0) t1 FULL JOIN (SELECT * FROM prt2_m WHERE prt2_m.c = 0) t2 ON (t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2) ORDER BY t1.a, t2.b; + a | c | b | c +-----+---+-----+--- + 0 | 0 | 0 | 0 + 50 | 0 | | + 100 | 0 | | + 150 | 0 | 150 | 0 + 200 | 0 | | + 250 | 0 | | + 300 | 0 | 300 | 0 + 350 | 0 | | + 400 | 0 | | + 450 | 0 | 450 | 0 + 500 | 0 | | + 550 | 0 | | + | | 75 | 0 + | | 225 | 0 + | | 375 | 0 + | | 525 | 0 +(16 rows) + +-- +-- tests for list partitioned tables. +-- +\set part_mod 17 +\set cond_mod 47 +\set num_rows 500 +CREATE TABLE plt1 (a int, b int, c varchar) PARTITION BY LIST(c); +CREATE TABLE plt1_p1 PARTITION OF plt1 FOR VALUES IN ('0001','0002','0003'); +CREATE TABLE plt1_p2 PARTITION OF plt1 FOR VALUES IN ('0004','0005','0006'); +CREATE TABLE plt1_p3 PARTITION OF plt1 FOR VALUES IN ('0008','0009'); +CREATE TABLE plt1_p4 PARTITION OF plt1 FOR VALUES IN ('0000','0010'); +INSERT INTO plt1 SELECT i, i % :cond_mod, to_char(i % :part_mod, 'FM0000') FROM generate_series(0, :num_rows) i WHERE i % :part_mod NOT IN (7, 11, 12, 13, 14, 15, 16); +ANALYSE plt1; +-- plt2 have missing starting 0001, additional 0007, missing ending 0010 +-- and additional 0011 and 0012 bounds +CREATE TABLE plt2 (a int, b int, c varchar) PARTITION BY LIST(c); +CREATE TABLE plt2_p1 PARTITION OF plt2 FOR VALUES IN ('0002','0003'); +CREATE TABLE plt2_p2 PARTITION OF plt2 FOR VALUES IN ('0004','0005','0006'); +CREATE TABLE plt2_p3 PARTITION OF plt2 FOR VALUES IN ('0007','0008','0009'); +CREATE TABLE plt2_p4 PARTITION OF plt2 FOR VALUES IN ('0000','0011','0012'); +INSERT INTO plt2 SELECT i, i % :cond_mod, to_char(i % :part_mod, 'FM0000') FROM generate_series(0, :num_rows) i WHERE i % :part_mod NOT IN (1, 10, 13, 14, 15, 16); +ANALYSE plt2; +-- Partition-wise-join is possible with some partition bounds overlap +-- with each other completely and some partialy for inner,left,right, +-- full, semi and anti joins +-- inner join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a; + QUERY PLAN +------------------------------------------------------------ + Sort + Sort Key: t1.a + -> Append + -> Hash Join + Hash Cond: ((t1.c)::text = (t2.c)::text) + Join Filter: ((t1.b + t2.b) = 0) + -> Seq Scan on plt1_p4 t1 + -> Hash + -> Seq Scan on plt2_p4 t2 + -> Hash Join + Hash Cond: ((t2_1.c)::text = (t1_1.c)::text) + Join Filter: ((t1_1.b + t2_1.b) = 0) + -> Seq Scan on plt2_p1 t2_1 + -> Hash + -> Seq Scan on plt1_p1 t1_1 + -> Hash Join + Hash Cond: ((t1_2.c)::text = (t2_2.c)::text) + Join Filter: ((t1_2.b + t2_2.b) = 0) + -> Seq Scan on plt1_p2 t1_2 + -> Hash + -> Seq Scan on plt2_p2 t2_2 + -> Hash Join + Hash Cond: ((t1_3.c)::text = (t2_3.c)::text) + Join Filter: ((t1_3.b + t2_3.b) = 0) + -> Seq Scan on plt1_p3 t1_3 + -> Hash + -> Seq Scan on plt2_p3 t2_3 +(27 rows) + +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a; + a | c | a | c +-----+------+-----+------ + 0 | 0000 | 0 | 0000 + 94 | 0009 | 94 | 0009 + 141 | 0005 | 141 | 0005 + 329 | 0006 | 329 | 0006 + 376 | 0002 | 376 | 0002 +(5 rows) + +-- left join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a; + QUERY PLAN +------------------------------------------------------------ + Sort + Sort Key: t1.a + -> Append + -> Hash Left Join + Hash Cond: ((t1.c)::text = (t2.c)::text) + Filter: ((t1.b + COALESCE(t2.b, 0)) = 0) + -> Seq Scan on plt1_p4 t1 + -> Hash + -> Seq Scan on plt2_p4 t2 + -> Hash Right Join + Hash Cond: ((t2_1.c)::text = (t1_1.c)::text) + Filter: ((t1_1.b + COALESCE(t2_1.b, 0)) = 0) + -> Seq Scan on plt2_p1 t2_1 + -> Hash + -> Seq Scan on plt1_p1 t1_1 + -> Hash Left Join + Hash Cond: ((t1_2.c)::text = (t2_2.c)::text) + Filter: ((t1_2.b + COALESCE(t2_2.b, 0)) = 0) + -> Seq Scan on plt1_p2 t1_2 + -> Hash + -> Seq Scan on plt2_p2 t2_2 + -> Hash Left Join + Hash Cond: ((t1_3.c)::text = (t2_3.c)::text) + Filter: ((t1_3.b + COALESCE(t2_3.b, 0)) = 0) + -> Seq Scan on plt1_p3 t1_3 + -> Hash + -> Seq Scan on plt2_p3 t2_3 +(27 rows) + +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a; + a | c | a | c +-----+------+-----+------ + 0 | 0000 | 0 | 0000 + 94 | 0009 | 94 | 0009 + 141 | 0005 | 141 | 0005 + 188 | 0001 | | + 282 | 0010 | | + 329 | 0006 | 329 | 0006 + 376 | 0002 | 376 | 0002 +(7 rows) + +-- right join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t2.a; + QUERY PLAN +------------------------------------------------------------ + Sort + Sort Key: t2.a + -> Append + -> Hash Right Join + Hash Cond: ((t1.c)::text = (t2.c)::text) + Filter: ((COALESCE(t1.b, 0) + t2.b) = 0) + -> Seq Scan on plt1_p4 t1 + -> Hash + -> Seq Scan on plt2_p4 t2 + -> Hash Left Join + Hash Cond: ((t2_1.c)::text = (t1_1.c)::text) + Filter: ((COALESCE(t1_1.b, 0) + t2_1.b) = 0) + -> Seq Scan on plt2_p1 t2_1 + -> Hash + -> Seq Scan on plt1_p1 t1_1 + -> Hash Left Join + Hash Cond: ((t2_2.c)::text = (t1_2.c)::text) + Filter: ((COALESCE(t1_2.b, 0) + t2_2.b) = 0) + -> Seq Scan on plt2_p2 t2_2 + -> Hash + -> Seq Scan on plt1_p2 t1_2 + -> Hash Right Join + Hash Cond: ((t1_3.c)::text = (t2_3.c)::text) + Filter: ((COALESCE(t1_3.b, 0) + t2_3.b) = 0) + -> Seq Scan on plt1_p3 t1_3 + -> Hash + -> Seq Scan on plt2_p3 t2_3 +(27 rows) + +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t1.a; + a | c | a | c +-----+------+-----+------ + 0 | 0000 | 0 | 0000 + 94 | 0009 | 94 | 0009 + 141 | 0005 | 141 | 0005 + 329 | 0006 | 329 | 0006 + 376 | 0002 | 376 | 0002 + | | 470 | 0011 +(6 rows) + +-- full join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a; + QUERY PLAN +------------------------------------------------------------------------- + Sort + Sort Key: t1.a, t2.a + -> Append + -> Hash Full Join + Hash Cond: ((t1.c)::text = (t2.c)::text) + Filter: ((COALESCE(t1.b, 0) + COALESCE(t2.b, 0)) = 0) + -> Seq Scan on plt1_p4 t1 + -> Hash + -> Seq Scan on plt2_p4 t2 + -> Hash Full Join + Hash Cond: ((t2_1.c)::text = (t1_1.c)::text) + Filter: ((COALESCE(t1_1.b, 0) + COALESCE(t2_1.b, 0)) = 0) + -> Seq Scan on plt2_p1 t2_1 + -> Hash + -> Seq Scan on plt1_p1 t1_1 + -> Hash Full Join + Hash Cond: ((t1_2.c)::text = (t2_2.c)::text) + Filter: ((COALESCE(t1_2.b, 0) + COALESCE(t2_2.b, 0)) = 0) + -> Seq Scan on plt1_p2 t1_2 + -> Hash + -> Seq Scan on plt2_p2 t2_2 + -> Hash Full Join + Hash Cond: ((t1_3.c)::text = (t2_3.c)::text) + Filter: ((COALESCE(t1_3.b, 0) + COALESCE(t2_3.b, 0)) = 0) + -> Seq Scan on plt1_p3 t1_3 + -> Hash + -> Seq Scan on plt2_p3 t2_3 +(27 rows) + +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a; + a | c | a | c +-----+------+-----+------ + 0 | 0000 | 0 | 0000 + 94 | 0009 | 94 | 0009 + 141 | 0005 | 141 | 0005 + 188 | 0001 | | + 282 | 0010 | | + 329 | 0006 | 329 | 0006 + 376 | 0002 | 376 | 0002 + | | 470 | 0011 +(8 rows) + +-- semi join +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + QUERY PLAN +-------------------------------------------------------------- + Sort + Sort Key: t1.a, t1.c + -> Append + -> Hash Join + Hash Cond: ((t2.c)::text = (t1.c)::text) + -> HashAggregate + Group Key: (t2.c)::text + -> Seq Scan on plt2_p4 t2 + -> Hash + -> Seq Scan on plt1_p4 t1 + Filter: (b = 0) + -> Nested Loop + Join Filter: ((t1_1.c)::text = (t2_1.c)::text) + -> HashAggregate + Group Key: (t2_1.c)::text + -> Seq Scan on plt2_p1 t2_1 + -> Materialize + -> Seq Scan on plt1_p1 t1_1 + Filter: (b = 0) + -> Nested Loop Semi Join + Join Filter: ((t1_2.c)::text = (t2_2.c)::text) + -> Seq Scan on plt1_p2 t1_2 + Filter: (b = 0) + -> Seq Scan on plt2_p2 t2_2 + -> Nested Loop Semi Join + Join Filter: ((t1_3.c)::text = (t2_3.c)::text) + -> Seq Scan on plt1_p3 t1_3 + Filter: (b = 0) + -> Seq Scan on plt2_p3 t2_3 +(29 rows) + +select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + a | b | c +-----+---+------ + 0 | 0 | 0000 + 94 | 0 | 0009 + 141 | 0 | 0005 + 329 | 0 | 0006 + 376 | 0 | 0002 +(5 rows) + +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + QUERY PLAN +-------------------------------------------------------------- + Sort + Sort Key: t1.a, t1.c + -> Append + -> Nested Loop + Join Filter: ((t1.c)::text = (t2.c)::text) + -> HashAggregate + Group Key: (t2.c)::text + -> Seq Scan on plt1_p4 t2 + -> Materialize + -> Seq Scan on plt2_p4 t1 + Filter: (b = 0) + -> Nested Loop Semi Join + Join Filter: ((t1_1.c)::text = (t2_1.c)::text) + -> Seq Scan on plt2_p1 t1_1 + Filter: (b = 0) + -> Seq Scan on plt1_p1 t2_1 + -> Nested Loop Semi Join + Join Filter: ((t1_2.c)::text = (t2_2.c)::text) + -> Seq Scan on plt2_p2 t1_2 + Filter: (b = 0) + -> Seq Scan on plt1_p2 t2_2 + -> Nested Loop Semi Join + Join Filter: ((t1_3.c)::text = (t2_3.c)::text) + -> Seq Scan on plt2_p3 t1_3 + Filter: (b = 0) + -> Seq Scan on plt1_p3 t2_3 +(26 rows) + +select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + a | b | c +-----+---+------ + 0 | 0 | 0000 + 94 | 0 | 0009 + 141 | 0 | 0005 + 329 | 0 | 0006 + 376 | 0 | 0002 +(5 rows) + +-- anti join +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + QUERY PLAN +-------------------------------------------------------------- + Sort + Sort Key: t1.a, t1.c + -> Append + -> Nested Loop Anti Join + Join Filter: ((t1.c)::text = (t2.c)::text) + -> Seq Scan on plt1_p4 t1 + Filter: (b = 0) + -> Seq Scan on plt2_p4 t2 + -> Hash Anti Join + Hash Cond: ((t1_1.c)::text = (t2_1.c)::text) + -> Seq Scan on plt1_p1 t1_1 + Filter: (b = 0) + -> Hash + -> Seq Scan on plt2_p1 t2_1 + -> Nested Loop Anti Join + Join Filter: ((t1_2.c)::text = (t2_2.c)::text) + -> Seq Scan on plt1_p2 t1_2 + Filter: (b = 0) + -> Seq Scan on plt2_p2 t2_2 + -> Nested Loop Anti Join + Join Filter: ((t1_3.c)::text = (t2_3.c)::text) + -> Seq Scan on plt1_p3 t1_3 + Filter: (b = 0) + -> Seq Scan on plt2_p3 t2_3 +(24 rows) + +select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + a | b | c +-----+---+------ + 188 | 0 | 0001 + 282 | 0 | 0010 +(2 rows) + +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + QUERY PLAN +-------------------------------------------------------------- + Sort + Sort Key: t1.a, t1.c + -> Append + -> Hash Anti Join + Hash Cond: ((t1.c)::text = (t2.c)::text) + -> Seq Scan on plt2_p4 t1 + Filter: (b = 0) + -> Hash + -> Seq Scan on plt1_p4 t2 + -> Nested Loop Anti Join + Join Filter: ((t1_1.c)::text = (t2_1.c)::text) + -> Seq Scan on plt2_p1 t1_1 + Filter: (b = 0) + -> Seq Scan on plt1_p1 t2_1 + -> Nested Loop Anti Join + Join Filter: ((t1_2.c)::text = (t2_2.c)::text) + -> Seq Scan on plt2_p2 t1_2 + Filter: (b = 0) + -> Seq Scan on plt1_p2 t2_2 + -> Nested Loop Anti Join + Join Filter: ((t1_3.c)::text = (t2_3.c)::text) + -> Seq Scan on plt2_p3 t1_3 + Filter: (b = 0) + -> Seq Scan on plt1_p3 t2_3 +(24 rows) + +select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + a | b | c +-----+---+------ + 470 | 0 | 0011 +(1 row) + +-- +-- list partitioned by expression +-- +CREATE TABLE plt1_e (a int, b int, c text) PARTITION BY LIST(ltrim(c, 'A')); +CREATE TABLE plt1_e_p1 PARTITION OF plt1_e FOR VALUES IN ('0002', '0003'); +CREATE TABLE plt1_e_p2 PARTITION OF plt1_e FOR VALUES IN ('0004', '0005', '0006'); +CREATE TABLE plt1_e_p3 PARTITION OF plt1_e FOR VALUES IN ('0008', '0009'); +CREATE TABLE plt1_e_p4 PARTITION OF plt1_e FOR VALUES IN ('0000'); +INSERT INTO plt1_e SELECT i, i % :cond_mod, to_char(i % :part_mod, 'FM0000') FROM generate_series(0, :num_rows) i WHERE i % :part_mod NOT IN (1, 7, 10, 11, 12, 13, 14, 15, 16); +ANALYZE plt1_e; +-- test partition matching with N-way join +EXPLAIN (COSTS OFF) +SELECT avg(t1.a), avg(t2.b), avg(t3.a + t3.b), t1.c, t2.c, t3.c FROM plt1 t1, plt2 t2, plt1_e t3 WHERE t1.b = t2.b AND t1.c = t2.c AND ltrim(t3.c, 'A') = t1.c GROUP BY t1.c, t2.c, t3.c ORDER BY t1.c, t2.c, t3.c; + QUERY PLAN +------------------------------------------------------------------------------------------------ + GroupAggregate + Group Key: t1.c, t2.c, t3.c + -> Sort + Sort Key: t1.c, t3.c + -> Append + -> Hash Join + Hash Cond: ((t1.c)::text = ltrim(t3.c, 'A'::text)) + -> Hash Join + Hash Cond: ((t2.b = t1.b) AND ((t2.c)::text = (t1.c)::text)) + -> Seq Scan on plt2_p4 t2 + -> Hash + -> Seq Scan on plt1_p4 t1 + -> Hash + -> Seq Scan on plt1_e_p4 t3 + -> Hash Join + Hash Cond: ((t1_1.c)::text = ltrim(t3_1.c, 'A'::text)) + -> Hash Join + Hash Cond: ((t1_1.b = t2_1.b) AND ((t1_1.c)::text = (t2_1.c)::text)) + -> Seq Scan on plt1_p1 t1_1 + -> Hash + -> Seq Scan on plt2_p1 t2_1 + -> Hash + -> Seq Scan on plt1_e_p1 t3_1 + -> Hash Join + Hash Cond: ((t1_2.c)::text = ltrim(t3_2.c, 'A'::text)) + -> Hash Join + Hash Cond: ((t1_2.b = t2_2.b) AND ((t1_2.c)::text = (t2_2.c)::text)) + -> Seq Scan on plt1_p2 t1_2 + -> Hash + -> Seq Scan on plt2_p2 t2_2 + -> Hash + -> Seq Scan on plt1_e_p2 t3_2 + -> Hash Join + Hash Cond: ((t1_3.c)::text = ltrim(t3_3.c, 'A'::text)) + -> Hash Join + Hash Cond: ((t2_3.b = t1_3.b) AND ((t2_3.c)::text = (t1_3.c)::text)) + -> Seq Scan on plt2_p3 t2_3 + -> Hash + -> Seq Scan on plt1_p3 t1_3 + -> Hash + -> Seq Scan on plt1_e_p3 t3_3 +(41 rows) + +SELECT avg(t1.a), avg(t2.b), avg(t3.a + t3.b), t1.c, t2.c, t3.c FROM plt1 t1, plt2 t2, plt1_e t3 WHERE t1.b = t2.b AND t1.c = t2.c AND ltrim(t3.c, 'A') = t1.c GROUP BY t1.c, t2.c, t3.c ORDER BY t1.c, t2.c, t3.c; + avg | avg | avg | c | c | c +----------------------+---------------------+----------------------+------+------+------ + 246.5000000000000000 | 22.4666666666666667 | 268.9666666666666667 | 0000 | 0000 | 0000 + 248.5000000000000000 | 21.3333333333333333 | 269.8333333333333333 | 0002 | 0002 | 0002 + 249.5000000000000000 | 22.3333333333333333 | 271.8333333333333333 | 0003 | 0003 | 0003 + 250.5000000000000000 | 23.3333333333333333 | 273.8333333333333333 | 0004 | 0004 | 0004 + 251.5000000000000000 | 22.7666666666666667 | 274.2666666666666667 | 0005 | 0005 | 0005 + 252.5000000000000000 | 22.2000000000000000 | 274.7000000000000000 | 0006 | 0006 | 0006 + 246.0000000000000000 | 23.9655172413793103 | 269.9655172413793103 | 0008 | 0008 | 0008 + 247.0000000000000000 | 23.3448275862068966 | 270.3448275862068966 | 0009 | 0009 | 0009 +(8 rows) + +-- Add an extra partition to plt2 , Partition-wise join is possible with +-- partitions on inner side are allowed +CREATE TABLE plt2_p5 PARTITION OF plt2 FOR VALUES IN ('0013','0014'); +INSERT INTO plt2 SELECT i, i % :cond_mod, to_char(i % :part_mod, 'FM0000') FROM generate_series(0, :num_rows) i WHERE i % :part_mod IN (13, 14); +ANALYZE plt2; +-- inner join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a; + QUERY PLAN +------------------------------------------------------------ + Sort + Sort Key: t1.a + -> Append + -> Hash Join + Hash Cond: ((t1.c)::text = (t2.c)::text) + Join Filter: ((t1.b + t2.b) = 0) + -> Seq Scan on plt1_p4 t1 + -> Hash + -> Seq Scan on plt2_p4 t2 + -> Hash Join + Hash Cond: ((t2_1.c)::text = (t1_1.c)::text) + Join Filter: ((t1_1.b + t2_1.b) = 0) + -> Seq Scan on plt2_p1 t2_1 + -> Hash + -> Seq Scan on plt1_p1 t1_1 + -> Hash Join + Hash Cond: ((t1_2.c)::text = (t2_2.c)::text) + Join Filter: ((t1_2.b + t2_2.b) = 0) + -> Seq Scan on plt1_p2 t1_2 + -> Hash + -> Seq Scan on plt2_p2 t2_2 + -> Hash Join + Hash Cond: ((t1_3.c)::text = (t2_3.c)::text) + Join Filter: ((t1_3.b + t2_3.b) = 0) + -> Seq Scan on plt1_p3 t1_3 + -> Hash + -> Seq Scan on plt2_p3 t2_3 +(27 rows) + +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a; + a | c | a | c +-----+------+-----+------ + 0 | 0000 | 0 | 0000 + 94 | 0009 | 94 | 0009 + 141 | 0005 | 141 | 0005 + 329 | 0006 | 329 | 0006 + 376 | 0002 | 376 | 0002 +(5 rows) + +-- left join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a; + QUERY PLAN +------------------------------------------------------------ + Sort + Sort Key: t1.a + -> Append + -> Hash Left Join + Hash Cond: ((t1.c)::text = (t2.c)::text) + Filter: ((t1.b + COALESCE(t2.b, 0)) = 0) + -> Seq Scan on plt1_p4 t1 + -> Hash + -> Seq Scan on plt2_p4 t2 + -> Hash Right Join + Hash Cond: ((t2_1.c)::text = (t1_1.c)::text) + Filter: ((t1_1.b + COALESCE(t2_1.b, 0)) = 0) + -> Seq Scan on plt2_p1 t2_1 + -> Hash + -> Seq Scan on plt1_p1 t1_1 + -> Hash Left Join + Hash Cond: ((t1_2.c)::text = (t2_2.c)::text) + Filter: ((t1_2.b + COALESCE(t2_2.b, 0)) = 0) + -> Seq Scan on plt1_p2 t1_2 + -> Hash + -> Seq Scan on plt2_p2 t2_2 + -> Hash Left Join + Hash Cond: ((t1_3.c)::text = (t2_3.c)::text) + Filter: ((t1_3.b + COALESCE(t2_3.b, 0)) = 0) + -> Seq Scan on plt1_p3 t1_3 + -> Hash + -> Seq Scan on plt2_p3 t2_3 +(27 rows) + +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a; + a | c | a | c +-----+------+-----+------ + 0 | 0000 | 0 | 0000 + 94 | 0009 | 94 | 0009 + 141 | 0005 | 141 | 0005 + 188 | 0001 | | + 282 | 0010 | | + 329 | 0006 | 329 | 0006 + 376 | 0002 | 376 | 0002 +(7 rows) + +-- right join, partition-wise join can not handle extra partition on the outer +-- side +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t2.a; + QUERY PLAN +-------------------------------------------------- + Sort + Sort Key: t2.a + -> Hash Right Join + Hash Cond: ((t1.c)::text = (t2.c)::text) + Filter: ((COALESCE(t1.b, 0) + t2.b) = 0) + -> Append + -> Seq Scan on plt1_p4 t1 + -> Seq Scan on plt1_p1 t1_1 + -> Seq Scan on plt1_p2 t1_2 + -> Seq Scan on plt1_p3 t1_3 + -> Hash + -> Append + -> Seq Scan on plt2_p4 t2 + -> Seq Scan on plt2_p1 t2_1 + -> Seq Scan on plt2_p2 t2_2 + -> Seq Scan on plt2_p3 t2_3 + -> Seq Scan on plt2_p5 t2_4 +(17 rows) + +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t1.a; + a | c | a | c +-----+------+-----+------ + 0 | 0000 | 0 | 0000 + 94 | 0009 | 94 | 0009 + 141 | 0005 | 141 | 0005 + 329 | 0006 | 329 | 0006 + 376 | 0002 | 376 | 0002 + | | 47 | 0013 + | | 470 | 0011 + | | 235 | 0014 +(8 rows) + +-- full join, partition-wise join can not handle extra partition on the outer +-- side +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a; + QUERY PLAN +--------------------------------------------------------------- + Sort + Sort Key: t1.a, t2.a + -> Hash Full Join + Hash Cond: ((t1.c)::text = (t2.c)::text) + Filter: ((COALESCE(t1.b, 0) + COALESCE(t2.b, 0)) = 0) + -> Append + -> Seq Scan on plt1_p4 t1 + -> Seq Scan on plt1_p1 t1_1 + -> Seq Scan on plt1_p2 t1_2 + -> Seq Scan on plt1_p3 t1_3 + -> Hash + -> Append + -> Seq Scan on plt2_p4 t2 + -> Seq Scan on plt2_p1 t2_1 + -> Seq Scan on plt2_p2 t2_2 + -> Seq Scan on plt2_p3 t2_3 + -> Seq Scan on plt2_p5 t2_4 +(17 rows) + +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a; + a | c | a | c +-----+------+-----+------ + 0 | 0000 | 0 | 0000 + 94 | 0009 | 94 | 0009 + 141 | 0005 | 141 | 0005 + 188 | 0001 | | + 282 | 0010 | | + 329 | 0006 | 329 | 0006 + 376 | 0002 | 376 | 0002 + | | 47 | 0013 + | | 235 | 0014 + | | 470 | 0011 +(10 rows) + +-- semi join +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + QUERY PLAN +-------------------------------------------------------------- + Sort + Sort Key: t1.a, t1.c + -> Append + -> Hash Join + Hash Cond: ((t2.c)::text = (t1.c)::text) + -> HashAggregate + Group Key: (t2.c)::text + -> Seq Scan on plt2_p4 t2 + -> Hash + -> Seq Scan on plt1_p4 t1 + Filter: (b = 0) + -> Nested Loop + Join Filter: ((t1_1.c)::text = (t2_1.c)::text) + -> HashAggregate + Group Key: (t2_1.c)::text + -> Seq Scan on plt2_p1 t2_1 + -> Materialize + -> Seq Scan on plt1_p1 t1_1 + Filter: (b = 0) + -> Nested Loop Semi Join + Join Filter: ((t1_2.c)::text = (t2_2.c)::text) + -> Seq Scan on plt1_p2 t1_2 + Filter: (b = 0) + -> Seq Scan on plt2_p2 t2_2 + -> Nested Loop Semi Join + Join Filter: ((t1_3.c)::text = (t2_3.c)::text) + -> Seq Scan on plt1_p3 t1_3 + Filter: (b = 0) + -> Seq Scan on plt2_p3 t2_3 +(29 rows) + +select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + a | b | c +-----+---+------ + 0 | 0 | 0000 + 94 | 0 | 0009 + 141 | 0 | 0005 + 329 | 0 | 0006 + 376 | 0 | 0002 +(5 rows) + +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + QUERY PLAN +-------------------------------------------------------------- + Sort + Sort Key: t1.a, t1.c + -> Append + -> Nested Loop + Join Filter: ((t1.c)::text = (t2.c)::text) + -> HashAggregate + Group Key: (t2.c)::text + -> Seq Scan on plt1_p4 t2 + -> Materialize + -> Seq Scan on plt2_p4 t1 + Filter: (b = 0) + -> Nested Loop Semi Join + Join Filter: ((t1_1.c)::text = (t2_1.c)::text) + -> Seq Scan on plt2_p1 t1_1 + Filter: (b = 0) + -> Seq Scan on plt1_p1 t2_1 + -> Nested Loop Semi Join + Join Filter: ((t1_2.c)::text = (t2_2.c)::text) + -> Seq Scan on plt2_p2 t1_2 + Filter: (b = 0) + -> Seq Scan on plt1_p2 t2_2 + -> Nested Loop Semi Join + Join Filter: ((t1_3.c)::text = (t2_3.c)::text) + -> Seq Scan on plt2_p3 t1_3 + Filter: (b = 0) + -> Seq Scan on plt1_p3 t2_3 +(26 rows) + +select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + a | b | c +-----+---+------ + 0 | 0 | 0000 + 94 | 0 | 0009 + 141 | 0 | 0005 + 329 | 0 | 0006 + 376 | 0 | 0002 +(5 rows) + +-- anti join +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + QUERY PLAN +-------------------------------------------------------------- + Sort + Sort Key: t1.a, t1.c + -> Append + -> Nested Loop Anti Join + Join Filter: ((t1.c)::text = (t2.c)::text) + -> Seq Scan on plt1_p4 t1 + Filter: (b = 0) + -> Seq Scan on plt2_p4 t2 + -> Hash Anti Join + Hash Cond: ((t1_1.c)::text = (t2_1.c)::text) + -> Seq Scan on plt1_p1 t1_1 + Filter: (b = 0) + -> Hash + -> Seq Scan on plt2_p1 t2_1 + -> Nested Loop Anti Join + Join Filter: ((t1_2.c)::text = (t2_2.c)::text) + -> Seq Scan on plt1_p2 t1_2 + Filter: (b = 0) + -> Seq Scan on plt2_p2 t2_2 + -> Nested Loop Anti Join + Join Filter: ((t1_3.c)::text = (t2_3.c)::text) + -> Seq Scan on plt1_p3 t1_3 + Filter: (b = 0) + -> Seq Scan on plt2_p3 t2_3 +(24 rows) + +select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + a | b | c +-----+---+------ + 188 | 0 | 0001 + 282 | 0 | 0010 +(2 rows) + +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + QUERY PLAN +-------------------------------------------------- + Sort + Sort Key: t1.a, t1.c + -> Hash Anti Join + Hash Cond: ((t1.c)::text = (t2.c)::text) + -> Append + -> Seq Scan on plt2_p4 t1 + Filter: (b = 0) + -> Seq Scan on plt2_p1 t1_1 + Filter: (b = 0) + -> Seq Scan on plt2_p2 t1_2 + Filter: (b = 0) + -> Seq Scan on plt2_p3 t1_3 + Filter: (b = 0) + -> Seq Scan on plt2_p5 t1_4 + Filter: (b = 0) + -> Hash + -> Append + -> Seq Scan on plt1_p4 t2 + -> Seq Scan on plt1_p1 t2_1 + -> Seq Scan on plt1_p2 t2_2 + -> Seq Scan on plt1_p3 t2_3 +(21 rows) + +select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + a | b | c +-----+---+------ + 47 | 0 | 0013 + 235 | 0 | 0014 + 470 | 0 | 0011 +(3 rows) + +-- Partition-wise join can not handle the case when one partition from one side +-- matches with multiple partitions on the other side +DROP TABLE plt2_p5; +CREATE TABLE plt2_p5 PARTITION OF plt2 FOR VALUES IN ('0001','0013','0014'); +INSERT INTO plt2 SELECT i, i % :cond_mod, to_char(i % :part_mod, 'FM0000') FROM generate_series(0, :num_rows) i WHERE i % :part_mod IN (1, 13, 14); +ANALYZE plt2; +-- inner join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a; + QUERY PLAN +-------------------------------------------------- + Sort + Sort Key: t1.a + -> Hash Join + Hash Cond: ((t1.c)::text = (t2.c)::text) + Join Filter: ((t1.b + t2.b) = 0) + -> Append + -> Seq Scan on plt1_p4 t1 + -> Seq Scan on plt1_p1 t1_1 + -> Seq Scan on plt1_p2 t1_2 + -> Seq Scan on plt1_p3 t1_3 + -> Hash + -> Append + -> Seq Scan on plt2_p4 t2 + -> Seq Scan on plt2_p5 t2_1 + -> Seq Scan on plt2_p1 t2_2 + -> Seq Scan on plt2_p2 t2_3 + -> Seq Scan on plt2_p3 t2_4 +(17 rows) + +-- left join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a; + QUERY PLAN +-------------------------------------------------- + Sort + Sort Key: t1.a + -> Hash Left Join + Hash Cond: ((t1.c)::text = (t2.c)::text) + Filter: ((t1.b + COALESCE(t2.b, 0)) = 0) + -> Append + -> Seq Scan on plt1_p4 t1 + -> Seq Scan on plt1_p1 t1_1 + -> Seq Scan on plt1_p2 t1_2 + -> Seq Scan on plt1_p3 t1_3 + -> Hash + -> Append + -> Seq Scan on plt2_p4 t2 + -> Seq Scan on plt2_p5 t2_1 + -> Seq Scan on plt2_p1 t2_2 + -> Seq Scan on plt2_p2 t2_3 + -> Seq Scan on plt2_p3 t2_4 +(17 rows) + +-- right join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t2.a; + QUERY PLAN +-------------------------------------------------- + Sort + Sort Key: t2.a + -> Hash Right Join + Hash Cond: ((t1.c)::text = (t2.c)::text) + Filter: ((COALESCE(t1.b, 0) + t2.b) = 0) + -> Append + -> Seq Scan on plt1_p4 t1 + -> Seq Scan on plt1_p1 t1_1 + -> Seq Scan on plt1_p2 t1_2 + -> Seq Scan on plt1_p3 t1_3 + -> Hash + -> Append + -> Seq Scan on plt2_p4 t2 + -> Seq Scan on plt2_p5 t2_1 + -> Seq Scan on plt2_p1 t2_2 + -> Seq Scan on plt2_p2 t2_3 + -> Seq Scan on plt2_p3 t2_4 +(17 rows) + +-- full join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a; + QUERY PLAN +--------------------------------------------------------------- + Sort + Sort Key: t1.a, t2.a + -> Hash Full Join + Hash Cond: ((t1.c)::text = (t2.c)::text) + Filter: ((COALESCE(t1.b, 0) + COALESCE(t2.b, 0)) = 0) + -> Append + -> Seq Scan on plt1_p4 t1 + -> Seq Scan on plt1_p1 t1_1 + -> Seq Scan on plt1_p2 t1_2 + -> Seq Scan on plt1_p3 t1_3 + -> Hash + -> Append + -> Seq Scan on plt2_p4 t2 + -> Seq Scan on plt2_p5 t2_1 + -> Seq Scan on plt2_p1 t2_2 + -> Seq Scan on plt2_p2 t2_3 + -> Seq Scan on plt2_p3 t2_4 +(17 rows) + +-- semi join +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + QUERY PLAN +-------------------------------------------------------------- + Sort + Sort Key: t1.a, t1.c + -> Hash Join + Hash Cond: ((t1.c)::text = (t2.c)::text) + -> Append + -> Seq Scan on plt1_p4 t1 + Filter: (b = 0) + -> Seq Scan on plt1_p1 t1_1 + Filter: (b = 0) + -> Seq Scan on plt1_p2 t1_2 + Filter: (b = 0) + -> Seq Scan on plt1_p3 t1_3 + Filter: (b = 0) + -> Hash + -> HashAggregate + Group Key: (t2.c)::text + -> Result + -> Append + -> Seq Scan on plt2_p4 t2 + -> Seq Scan on plt2_p5 t2_1 + -> Seq Scan on plt2_p1 t2_2 + -> Seq Scan on plt2_p2 t2_3 + -> Seq Scan on plt2_p3 t2_4 +(23 rows) + +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + QUERY PLAN +-------------------------------------------------------------- + Sort + Sort Key: t1.a, t1.c + -> Hash Join + Hash Cond: ((t1.c)::text = (t2.c)::text) + -> Append + -> Seq Scan on plt2_p4 t1 + Filter: (b = 0) + -> Seq Scan on plt2_p5 t1_1 + Filter: (b = 0) + -> Seq Scan on plt2_p1 t1_2 + Filter: (b = 0) + -> Seq Scan on plt2_p2 t1_3 + Filter: (b = 0) + -> Seq Scan on plt2_p3 t1_4 + Filter: (b = 0) + -> Hash + -> HashAggregate + Group Key: (t2.c)::text + -> Result + -> Append + -> Seq Scan on plt1_p4 t2 + -> Seq Scan on plt1_p1 t2_1 + -> Seq Scan on plt1_p2 t2_2 + -> Seq Scan on plt1_p3 t2_3 +(24 rows) + +-- anti join +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + QUERY PLAN +---------------------------------------------------- + Sort + Sort Key: t1.a, t1.c + -> Nested Loop Anti Join + Join Filter: ((t1.c)::text = (t2.c)::text) + -> Append + -> Seq Scan on plt1_p4 t1 + Filter: (b = 0) + -> Seq Scan on plt1_p1 t1_1 + Filter: (b = 0) + -> Seq Scan on plt1_p2 t1_2 + Filter: (b = 0) + -> Seq Scan on plt1_p3 t1_3 + Filter: (b = 0) + -> Materialize + -> Append + -> Seq Scan on plt2_p4 t2 + -> Seq Scan on plt2_p5 t2_1 + -> Seq Scan on plt2_p1 t2_2 + -> Seq Scan on plt2_p2 t2_3 + -> Seq Scan on plt2_p3 t2_4 +(20 rows) + +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + QUERY PLAN +-------------------------------------------------- + Sort + Sort Key: t1.a, t1.c + -> Hash Anti Join + Hash Cond: ((t1.c)::text = (t2.c)::text) + -> Append + -> Seq Scan on plt2_p4 t1 + Filter: (b = 0) + -> Seq Scan on plt2_p5 t1_1 + Filter: (b = 0) + -> Seq Scan on plt2_p1 t1_2 + Filter: (b = 0) + -> Seq Scan on plt2_p2 t1_3 + Filter: (b = 0) + -> Seq Scan on plt2_p3 t1_4 + Filter: (b = 0) + -> Hash + -> Append + -> Seq Scan on plt1_p4 t2 + -> Seq Scan on plt1_p1 t2_1 + -> Seq Scan on plt1_p2 t2_2 + -> Seq Scan on plt1_p3 t2_3 +(21 rows) + +-- partition have a NULL on one side, Partition-wise join is possible with +-- NULL when NULL comparision is not strict i.e. NULL=NULL allowed +-- in this case NULL will be treated as addition partition bounds. +DROP TABLE plt2_p5; +DROP TABLE plt2_p4; +CREATE TABLE plt2_p4 PARTITION OF plt2 FOR VALUES IN ('0000',NULL,'0012'); +INSERT INTO plt2 SELECT i, i % :cond_mod, case when i % :part_mod = 11 then NULL else to_char(i % :part_mod, 'FM0000') end FROM generate_series(0, :num_rows) i WHERE i % :part_mod IN (0,11,12); +ANALYZE plt2; +-- inner join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a; + QUERY PLAN +------------------------------------------------------------ + Sort + Sort Key: t1.a + -> Append + -> Hash Join + Hash Cond: ((t2.c)::text = (t1.c)::text) + Join Filter: ((t1.b + t2.b) = 0) + -> Seq Scan on plt2_p4 t2 + -> Hash + -> Seq Scan on plt1_p4 t1 + -> Hash Join + Hash Cond: ((t2_1.c)::text = (t1_1.c)::text) + Join Filter: ((t1_1.b + t2_1.b) = 0) + -> Seq Scan on plt2_p1 t2_1 + -> Hash + -> Seq Scan on plt1_p1 t1_1 + -> Hash Join + Hash Cond: ((t1_2.c)::text = (t2_2.c)::text) + Join Filter: ((t1_2.b + t2_2.b) = 0) + -> Seq Scan on plt1_p2 t1_2 + -> Hash + -> Seq Scan on plt2_p2 t2_2 + -> Hash Join + Hash Cond: ((t1_3.c)::text = (t2_3.c)::text) + Join Filter: ((t1_3.b + t2_3.b) = 0) + -> Seq Scan on plt1_p3 t1_3 + -> Hash + -> Seq Scan on plt2_p3 t2_3 +(27 rows) + +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a; + a | c | a | c +-----+------+-----+------ + 0 | 0000 | 0 | 0000 + 94 | 0009 | 94 | 0009 + 141 | 0005 | 141 | 0005 + 329 | 0006 | 329 | 0006 + 376 | 0002 | 376 | 0002 +(5 rows) + +-- left join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a; + QUERY PLAN +------------------------------------------------------------ + Sort + Sort Key: t1.a + -> Append + -> Hash Right Join + Hash Cond: ((t2.c)::text = (t1.c)::text) + Filter: ((t1.b + COALESCE(t2.b, 0)) = 0) + -> Seq Scan on plt2_p4 t2 + -> Hash + -> Seq Scan on plt1_p4 t1 + -> Hash Right Join + Hash Cond: ((t2_1.c)::text = (t1_1.c)::text) + Filter: ((t1_1.b + COALESCE(t2_1.b, 0)) = 0) + -> Seq Scan on plt2_p1 t2_1 + -> Hash + -> Seq Scan on plt1_p1 t1_1 + -> Hash Left Join + Hash Cond: ((t1_2.c)::text = (t2_2.c)::text) + Filter: ((t1_2.b + COALESCE(t2_2.b, 0)) = 0) + -> Seq Scan on plt1_p2 t1_2 + -> Hash + -> Seq Scan on plt2_p2 t2_2 + -> Hash Left Join + Hash Cond: ((t1_3.c)::text = (t2_3.c)::text) + Filter: ((t1_3.b + COALESCE(t2_3.b, 0)) = 0) + -> Seq Scan on plt1_p3 t1_3 + -> Hash + -> Seq Scan on plt2_p3 t2_3 +(27 rows) + +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a; + a | c | a | c +-----+------+-----+------ + 0 | 0000 | 0 | 0000 + 94 | 0009 | 94 | 0009 + 141 | 0005 | 141 | 0005 + 188 | 0001 | | + 282 | 0010 | | + 329 | 0006 | 329 | 0006 + 376 | 0002 | 376 | 0002 +(7 rows) + +-- right join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t2.a; + QUERY PLAN +------------------------------------------------------------ + Sort + Sort Key: t2.a + -> Append + -> Hash Left Join + Hash Cond: ((t2.c)::text = (t1.c)::text) + Filter: ((COALESCE(t1.b, 0) + t2.b) = 0) + -> Seq Scan on plt2_p4 t2 + -> Hash + -> Seq Scan on plt1_p4 t1 + -> Hash Left Join + Hash Cond: ((t2_1.c)::text = (t1_1.c)::text) + Filter: ((COALESCE(t1_1.b, 0) + t2_1.b) = 0) + -> Seq Scan on plt2_p1 t2_1 + -> Hash + -> Seq Scan on plt1_p1 t1_1 + -> Hash Left Join + Hash Cond: ((t2_2.c)::text = (t1_2.c)::text) + Filter: ((COALESCE(t1_2.b, 0) + t2_2.b) = 0) + -> Seq Scan on plt2_p2 t2_2 + -> Hash + -> Seq Scan on plt1_p2 t1_2 + -> Hash Right Join + Hash Cond: ((t1_3.c)::text = (t2_3.c)::text) + Filter: ((COALESCE(t1_3.b, 0) + t2_3.b) = 0) + -> Seq Scan on plt1_p3 t1_3 + -> Hash + -> Seq Scan on plt2_p3 t2_3 +(27 rows) + +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t1.a; + a | c | a | c +-----+------+-----+------ + 0 | 0000 | 0 | 0000 + 94 | 0009 | 94 | 0009 + 141 | 0005 | 141 | 0005 + 329 | 0006 | 329 | 0006 + 376 | 0002 | 376 | 0002 + | | 470 | +(6 rows) + +-- full join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a; + QUERY PLAN +------------------------------------------------------------------------- + Sort + Sort Key: t1.a, t2.a + -> Append + -> Hash Full Join + Hash Cond: ((t2.c)::text = (t1.c)::text) + Filter: ((COALESCE(t1.b, 0) + COALESCE(t2.b, 0)) = 0) + -> Seq Scan on plt2_p4 t2 + -> Hash + -> Seq Scan on plt1_p4 t1 + -> Hash Full Join + Hash Cond: ((t2_1.c)::text = (t1_1.c)::text) + Filter: ((COALESCE(t1_1.b, 0) + COALESCE(t2_1.b, 0)) = 0) + -> Seq Scan on plt2_p1 t2_1 + -> Hash + -> Seq Scan on plt1_p1 t1_1 + -> Hash Full Join + Hash Cond: ((t1_2.c)::text = (t2_2.c)::text) + Filter: ((COALESCE(t1_2.b, 0) + COALESCE(t2_2.b, 0)) = 0) + -> Seq Scan on plt1_p2 t1_2 + -> Hash + -> Seq Scan on plt2_p2 t2_2 + -> Hash Full Join + Hash Cond: ((t1_3.c)::text = (t2_3.c)::text) + Filter: ((COALESCE(t1_3.b, 0) + COALESCE(t2_3.b, 0)) = 0) + -> Seq Scan on plt1_p3 t1_3 + -> Hash + -> Seq Scan on plt2_p3 t2_3 +(27 rows) + +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a; + a | c | a | c +-----+------+-----+------ + 0 | 0000 | 0 | 0000 + 94 | 0009 | 94 | 0009 + 141 | 0005 | 141 | 0005 + 188 | 0001 | | + 282 | 0010 | | + 329 | 0006 | 329 | 0006 + 376 | 0002 | 376 | 0002 + | | 470 | +(8 rows) + +-- semi join +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + QUERY PLAN +-------------------------------------------------------------- + Sort + Sort Key: t1.a, t1.c + -> Append + -> Nested Loop + Join Filter: ((t1.c)::text = (t2.c)::text) + -> HashAggregate + Group Key: (t2.c)::text + -> Seq Scan on plt2_p4 t2 + -> Materialize + -> Seq Scan on plt1_p4 t1 + Filter: (b = 0) + -> Nested Loop + Join Filter: ((t1_1.c)::text = (t2_1.c)::text) + -> HashAggregate + Group Key: (t2_1.c)::text + -> Seq Scan on plt2_p1 t2_1 + -> Materialize + -> Seq Scan on plt1_p1 t1_1 + Filter: (b = 0) + -> Nested Loop Semi Join + Join Filter: ((t1_2.c)::text = (t2_2.c)::text) + -> Seq Scan on plt1_p2 t1_2 + Filter: (b = 0) + -> Seq Scan on plt2_p2 t2_2 + -> Nested Loop Semi Join + Join Filter: ((t1_3.c)::text = (t2_3.c)::text) + -> Seq Scan on plt1_p3 t1_3 + Filter: (b = 0) + -> Seq Scan on plt2_p3 t2_3 +(29 rows) + +select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + a | b | c +-----+---+------ + 0 | 0 | 0000 + 94 | 0 | 0009 + 141 | 0 | 0005 + 329 | 0 | 0006 + 376 | 0 | 0002 +(5 rows) + +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + QUERY PLAN +-------------------------------------------------------------- + Sort + Sort Key: t1.a, t1.c + -> Append + -> Nested Loop + Join Filter: ((t1.c)::text = (t2.c)::text) + -> HashAggregate + Group Key: (t2.c)::text + -> Seq Scan on plt1_p4 t2 + -> Materialize + -> Seq Scan on plt2_p4 t1 + Filter: (b = 0) + -> Nested Loop Semi Join + Join Filter: ((t1_1.c)::text = (t2_1.c)::text) + -> Seq Scan on plt2_p1 t1_1 + Filter: (b = 0) + -> Seq Scan on plt1_p1 t2_1 + -> Nested Loop Semi Join + Join Filter: ((t1_2.c)::text = (t2_2.c)::text) + -> Seq Scan on plt2_p2 t1_2 + Filter: (b = 0) + -> Seq Scan on plt1_p2 t2_2 + -> Nested Loop Semi Join + Join Filter: ((t1_3.c)::text = (t2_3.c)::text) + -> Seq Scan on plt2_p3 t1_3 + Filter: (b = 0) + -> Seq Scan on plt1_p3 t2_3 +(26 rows) + +select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + a | b | c +-----+---+------ + 0 | 0 | 0000 + 94 | 0 | 0009 + 141 | 0 | 0005 + 329 | 0 | 0006 + 376 | 0 | 0002 +(5 rows) + +-- anti join +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + QUERY PLAN +-------------------------------------------------------------- + Sort + Sort Key: t1.a, t1.c + -> Append + -> Nested Loop Anti Join + Join Filter: ((t1.c)::text = (t2.c)::text) + -> Seq Scan on plt1_p4 t1 + Filter: (b = 0) + -> Seq Scan on plt2_p4 t2 + -> Hash Anti Join + Hash Cond: ((t1_1.c)::text = (t2_1.c)::text) + -> Seq Scan on plt1_p1 t1_1 + Filter: (b = 0) + -> Hash + -> Seq Scan on plt2_p1 t2_1 + -> Nested Loop Anti Join + Join Filter: ((t1_2.c)::text = (t2_2.c)::text) + -> Seq Scan on plt1_p2 t1_2 + Filter: (b = 0) + -> Seq Scan on plt2_p2 t2_2 + -> Nested Loop Anti Join + Join Filter: ((t1_3.c)::text = (t2_3.c)::text) + -> Seq Scan on plt1_p3 t1_3 + Filter: (b = 0) + -> Seq Scan on plt2_p3 t2_3 +(24 rows) + +select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + a | b | c +-----+---+------ + 188 | 0 | 0001 + 282 | 0 | 0010 +(2 rows) + +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + QUERY PLAN +-------------------------------------------------------------- + Sort + Sort Key: t1.a, t1.c + -> Append + -> Hash Anti Join + Hash Cond: ((t1.c)::text = (t2.c)::text) + -> Seq Scan on plt2_p4 t1 + Filter: (b = 0) + -> Hash + -> Seq Scan on plt1_p4 t2 + -> Nested Loop Anti Join + Join Filter: ((t1_1.c)::text = (t2_1.c)::text) + -> Seq Scan on plt2_p1 t1_1 + Filter: (b = 0) + -> Seq Scan on plt1_p1 t2_1 + -> Nested Loop Anti Join + Join Filter: ((t1_2.c)::text = (t2_2.c)::text) + -> Seq Scan on plt2_p2 t1_2 + Filter: (b = 0) + -> Seq Scan on plt1_p2 t2_2 + -> Nested Loop Anti Join + Join Filter: ((t1_3.c)::text = (t2_3.c)::text) + -> Seq Scan on plt2_p3 t1_3 + Filter: (b = 0) + -> Seq Scan on plt1_p3 t2_3 +(24 rows) + +select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + a | b | c +-----+---+--- + 470 | 0 | +(1 row) + +-- partition have a NULL on both side with different partition bounds w.r.t other side +-- NULL when NULL comparision is not strict i.e. NULL=NULL allowed +-- Partition-wise join can not handle the case when one partition from one side +-- matches with multiple partitions on the other side +DROP TABLE plt1_p3; +CREATE TABLE plt1_p3 PARTITION OF plt1 FOR VALUES IN (NULL,'0008','0009'); +INSERT INTO plt1 SELECT i, i % :cond_mod, case when i % :part_mod = 7 then NULL else to_char(i % :part_mod, 'FM0000') end FROM generate_series(0, :num_rows) i WHERE i % :part_mod IN (7,8,9); +ANALYZE plt1; +-- inner join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a; + QUERY PLAN +-------------------------------------------------- + Sort + Sort Key: t1.a + -> Hash Join + Hash Cond: ((t2.c)::text = (t1.c)::text) + Join Filter: ((t1.b + t2.b) = 0) + -> Append + -> Seq Scan on plt2_p4 t2 + -> Seq Scan on plt2_p1 t2_1 + -> Seq Scan on plt2_p2 t2_2 + -> Seq Scan on plt2_p3 t2_3 + -> Hash + -> Append + -> Seq Scan on plt1_p4 t1 + -> Seq Scan on plt1_p1 t1_1 + -> Seq Scan on plt1_p2 t1_2 + -> Seq Scan on plt1_p3 t1_3 +(16 rows) + +-- left join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a; + QUERY PLAN +-------------------------------------------------- + Sort + Sort Key: t1.a + -> Hash Right Join + Hash Cond: ((t2.c)::text = (t1.c)::text) + Filter: ((t1.b + COALESCE(t2.b, 0)) = 0) + -> Append + -> Seq Scan on plt2_p4 t2 + -> Seq Scan on plt2_p1 t2_1 + -> Seq Scan on plt2_p2 t2_2 + -> Seq Scan on plt2_p3 t2_3 + -> Hash + -> Append + -> Seq Scan on plt1_p4 t1 + -> Seq Scan on plt1_p1 t1_1 + -> Seq Scan on plt1_p2 t1_2 + -> Seq Scan on plt1_p3 t1_3 +(16 rows) + +-- right join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t2.a; + QUERY PLAN +-------------------------------------------------- Sort - Sort Key: prt1_m_p1.a, prt2_m_p1.b - -> Append - -> Hash Full Join - Hash Cond: ((prt1_m_p1.a = ((prt2_m_p1.b + prt2_m_p1.a) / 2)) AND (((prt1_m_p1.a + prt1_m_p1.b) / 2) = prt2_m_p1.b)) - -> Seq Scan on prt1_m_p1 - Filter: (c = 0) - -> Hash - -> Seq Scan on prt2_m_p1 - Filter: (c = 0) - -> Hash Full Join - Hash Cond: ((prt1_m_p2.a = ((prt2_m_p2.b + prt2_m_p2.a) / 2)) AND (((prt1_m_p2.a + prt1_m_p2.b) / 2) = prt2_m_p2.b)) - -> Seq Scan on prt1_m_p2 - Filter: (c = 0) - -> Hash - -> Seq Scan on prt2_m_p2 - Filter: (c = 0) - -> Hash Full Join - Hash Cond: ((prt1_m_p3.a = ((prt2_m_p3.b + prt2_m_p3.a) / 2)) AND (((prt1_m_p3.a + prt1_m_p3.b) / 2) = prt2_m_p3.b)) - -> Seq Scan on prt1_m_p3 - Filter: (c = 0) - -> Hash - -> Seq Scan on prt2_m_p3 - Filter: (c = 0) -(24 rows) + Sort Key: t2.a + -> Hash Left Join + Hash Cond: ((t2.c)::text = (t1.c)::text) + Filter: ((COALESCE(t1.b, 0) + t2.b) = 0) + -> Append + -> Seq Scan on plt2_p4 t2 + -> Seq Scan on plt2_p1 t2_1 + -> Seq Scan on plt2_p2 t2_2 + -> Seq Scan on plt2_p3 t2_3 + -> Hash + -> Append + -> Seq Scan on plt1_p4 t1 + -> Seq Scan on plt1_p1 t1_1 + -> Seq Scan on plt1_p2 t1_2 + -> Seq Scan on plt1_p3 t1_3 +(16 rows) -SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_m WHERE prt1_m.c = 0) t1 FULL JOIN (SELECT * FROM prt2_m WHERE prt2_m.c = 0) t2 ON (t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2) ORDER BY t1.a, t2.b; - a | c | b | c ------+---+-----+--- - 0 | 0 | 0 | 0 - 50 | 0 | | - 100 | 0 | | - 150 | 0 | 150 | 0 - 200 | 0 | | - 250 | 0 | | - 300 | 0 | 300 | 0 - 350 | 0 | | - 400 | 0 | | - 450 | 0 | 450 | 0 - 500 | 0 | | - 550 | 0 | | - | | 75 | 0 - | | 225 | 0 - | | 375 | 0 - | | 525 | 0 +-- full join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a; + QUERY PLAN +--------------------------------------------------------------- + Sort + Sort Key: t1.a, t2.a + -> Hash Full Join + Hash Cond: ((t2.c)::text = (t1.c)::text) + Filter: ((COALESCE(t1.b, 0) + COALESCE(t2.b, 0)) = 0) + -> Append + -> Seq Scan on plt2_p4 t2 + -> Seq Scan on plt2_p1 t2_1 + -> Seq Scan on plt2_p2 t2_2 + -> Seq Scan on plt2_p3 t2_3 + -> Hash + -> Append + -> Seq Scan on plt1_p4 t1 + -> Seq Scan on plt1_p1 t1_1 + -> Seq Scan on plt1_p2 t1_2 + -> Seq Scan on plt1_p3 t1_3 (16 rows) --- --- tests for list partitioned tables. --- -CREATE TABLE plt1 (a int, b int, c text) PARTITION BY LIST(c); -CREATE TABLE plt1_p1 PARTITION OF plt1 FOR VALUES IN ('0000', '0003', '0004', '0010'); -CREATE TABLE plt1_p2 PARTITION OF plt1 FOR VALUES IN ('0001', '0005', '0002', '0009'); -CREATE TABLE plt1_p3 PARTITION OF plt1 FOR VALUES IN ('0006', '0007', '0008', '0011'); -INSERT INTO plt1 SELECT i, i, to_char(i/50, 'FM0000') FROM generate_series(0, 599, 2) i; -ANALYZE plt1; -CREATE TABLE plt2 (a int, b int, c text) PARTITION BY LIST(c); -CREATE TABLE plt2_p1 PARTITION OF plt2 FOR VALUES IN ('0000', '0003', '0004', '0010'); -CREATE TABLE plt2_p2 PARTITION OF plt2 FOR VALUES IN ('0001', '0005', '0002', '0009'); -CREATE TABLE plt2_p3 PARTITION OF plt2 FOR VALUES IN ('0006', '0007', '0008', '0011'); -INSERT INTO plt2 SELECT i, i, to_char(i/50, 'FM0000') FROM generate_series(0, 599, 3) i; -ANALYZE plt2; --- --- list partitioned by expression --- -CREATE TABLE plt1_e (a int, b int, c text) PARTITION BY LIST(ltrim(c, 'A')); -CREATE TABLE plt1_e_p1 PARTITION OF plt1_e FOR VALUES IN ('0000', '0003', '0004', '0010'); -CREATE TABLE plt1_e_p2 PARTITION OF plt1_e FOR VALUES IN ('0001', '0005', '0002', '0009'); -CREATE TABLE plt1_e_p3 PARTITION OF plt1_e FOR VALUES IN ('0006', '0007', '0008', '0011'); -INSERT INTO plt1_e SELECT i, i, 'A' || to_char(i/50, 'FM0000') FROM generate_series(0, 599, 2) i; -ANALYZE plt1_e; --- test partition matching with N-way join +-- semi join EXPLAIN (COSTS OFF) -SELECT avg(t1.a), avg(t2.b), avg(t3.a + t3.b), t1.c, t2.c, t3.c FROM plt1 t1, plt2 t2, plt1_e t3 WHERE t1.b = t2.b AND t1.c = t2.c AND ltrim(t3.c, 'A') = t1.c GROUP BY t1.c, t2.c, t3.c ORDER BY t1.c, t2.c, t3.c; - QUERY PLAN --------------------------------------------------------------------------------- - GroupAggregate - Group Key: t1.c, t2.c, t3.c - -> Sort - Sort Key: t1.c, t3.c +select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + QUERY PLAN +-------------------------------------------------------------- + Sort + Sort Key: t1.a, t1.c + -> Hash Join + Hash Cond: ((t1.c)::text = (t2.c)::text) -> Append - -> Hash Join - Hash Cond: (t1.c = ltrim(t3.c, 'A'::text)) - -> Hash Join - Hash Cond: ((t1.b = t2.b) AND (t1.c = t2.c)) - -> Seq Scan on plt1_p1 t1 - -> Hash - -> Seq Scan on plt2_p1 t2 - -> Hash - -> Seq Scan on plt1_e_p1 t3 - -> Hash Join - Hash Cond: (t1_1.c = ltrim(t3_1.c, 'A'::text)) - -> Hash Join - Hash Cond: ((t1_1.b = t2_1.b) AND (t1_1.c = t2_1.c)) - -> Seq Scan on plt1_p2 t1_1 - -> Hash - -> Seq Scan on plt2_p2 t2_1 - -> Hash - -> Seq Scan on plt1_e_p2 t3_1 - -> Hash Join - Hash Cond: (t1_2.c = ltrim(t3_2.c, 'A'::text)) - -> Hash Join - Hash Cond: ((t1_2.b = t2_2.b) AND (t1_2.c = t2_2.c)) - -> Seq Scan on plt1_p3 t1_2 - -> Hash - -> Seq Scan on plt2_p3 t2_2 - -> Hash - -> Seq Scan on plt1_e_p3 t3_2 -(32 rows) + -> Seq Scan on plt1_p4 t1 + Filter: (b = 0) + -> Seq Scan on plt1_p1 t1_1 + Filter: (b = 0) + -> Seq Scan on plt1_p2 t1_2 + Filter: (b = 0) + -> Seq Scan on plt1_p3 t1_3 + Filter: (b = 0) + -> Hash + -> HashAggregate + Group Key: (t2.c)::text + -> Result + -> Append + -> Seq Scan on plt2_p4 t2 + -> Seq Scan on plt2_p1 t2_1 + -> Seq Scan on plt2_p2 t2_2 + -> Seq Scan on plt2_p3 t2_3 +(22 rows) -SELECT avg(t1.a), avg(t2.b), avg(t3.a + t3.b), t1.c, t2.c, t3.c FROM plt1 t1, plt2 t2, plt1_e t3 WHERE t1.b = t2.b AND t1.c = t2.c AND ltrim(t3.c, 'A') = t1.c GROUP BY t1.c, t2.c, t3.c ORDER BY t1.c, t2.c, t3.c; - avg | avg | avg | c | c | c -----------------------+----------------------+-----------------------+------+------+------- - 24.0000000000000000 | 24.0000000000000000 | 48.0000000000000000 | 0000 | 0000 | A0000 - 75.0000000000000000 | 75.0000000000000000 | 148.0000000000000000 | 0001 | 0001 | A0001 - 123.0000000000000000 | 123.0000000000000000 | 248.0000000000000000 | 0002 | 0002 | A0002 - 174.0000000000000000 | 174.0000000000000000 | 348.0000000000000000 | 0003 | 0003 | A0003 - 225.0000000000000000 | 225.0000000000000000 | 448.0000000000000000 | 0004 | 0004 | A0004 - 273.0000000000000000 | 273.0000000000000000 | 548.0000000000000000 | 0005 | 0005 | A0005 - 324.0000000000000000 | 324.0000000000000000 | 648.0000000000000000 | 0006 | 0006 | A0006 - 375.0000000000000000 | 375.0000000000000000 | 748.0000000000000000 | 0007 | 0007 | A0007 - 423.0000000000000000 | 423.0000000000000000 | 848.0000000000000000 | 0008 | 0008 | A0008 - 474.0000000000000000 | 474.0000000000000000 | 948.0000000000000000 | 0009 | 0009 | A0009 - 525.0000000000000000 | 525.0000000000000000 | 1048.0000000000000000 | 0010 | 0010 | A0010 - 573.0000000000000000 | 573.0000000000000000 | 1148.0000000000000000 | 0011 | 0011 | A0011 -(12 rows) +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + QUERY PLAN +-------------------------------------------------------------- + Sort + Sort Key: t1.a, t1.c + -> Hash Join + Hash Cond: ((t1.c)::text = (t2.c)::text) + -> Append + -> Seq Scan on plt2_p4 t1 + Filter: (b = 0) + -> Seq Scan on plt2_p1 t1_1 + Filter: (b = 0) + -> Seq Scan on plt2_p2 t1_2 + Filter: (b = 0) + -> Seq Scan on plt2_p3 t1_3 + Filter: (b = 0) + -> Hash + -> HashAggregate + Group Key: (t2.c)::text + -> Result + -> Append + -> Seq Scan on plt1_p4 t2 + -> Seq Scan on plt1_p1 t2_1 + -> Seq Scan on plt1_p2 t2_2 + -> Seq Scan on plt1_p3 t2_3 +(22 rows) + +-- anti join +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + QUERY PLAN +-------------------------------------------------- + Sort + Sort Key: t1.a, t1.c + -> Hash Anti Join + Hash Cond: ((t1.c)::text = (t2.c)::text) + -> Append + -> Seq Scan on plt1_p4 t1 + Filter: (b = 0) + -> Seq Scan on plt1_p1 t1_1 + Filter: (b = 0) + -> Seq Scan on plt1_p2 t1_2 + Filter: (b = 0) + -> Seq Scan on plt1_p3 t1_3 + Filter: (b = 0) + -> Hash + -> Append + -> Seq Scan on plt2_p4 t2 + -> Seq Scan on plt2_p1 t2_1 + -> Seq Scan on plt2_p2 t2_2 + -> Seq Scan on plt2_p3 t2_3 +(19 rows) + +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + QUERY PLAN +-------------------------------------------------- + Sort + Sort Key: t1.a, t1.c + -> Hash Anti Join + Hash Cond: ((t1.c)::text = (t2.c)::text) + -> Append + -> Seq Scan on plt2_p4 t1 + Filter: (b = 0) + -> Seq Scan on plt2_p1 t1_1 + Filter: (b = 0) + -> Seq Scan on plt2_p2 t1_2 + Filter: (b = 0) + -> Seq Scan on plt2_p3 t1_3 + Filter: (b = 0) + -> Hash + -> Append + -> Seq Scan on plt1_p4 t2 + -> Seq Scan on plt1_p1 t2_1 + -> Seq Scan on plt1_p2 t2_2 + -> Seq Scan on plt1_p3 t2_3 +(19 rows) -- joins where one of the relations is proven empty EXPLAIN (COSTS OFF) @@ -1241,22 +4040,22 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a = 1 AND a = 2) t1 -------------------------------------------------- Hash Left Join Hash Cond: (t2.b = a) - -> Append - -> Hash Join - Hash Cond: (t3.a = t2.b) - -> Seq Scan on prt1_p1 t3 - -> Hash - -> Seq Scan on prt2_p1 t2 - -> Hash Join - Hash Cond: (t3_1.a = t2_1.b) - -> Seq Scan on prt1_p2 t3_1 - -> Hash - -> Seq Scan on prt2_p2 t2_1 - -> Hash Join - Hash Cond: (t3_2.a = t2_2.b) - -> Seq Scan on prt1_p3 t3_2 - -> Hash - -> Seq Scan on prt2_p3 t2_2 + -> Hash Join + Hash Cond: (t3.a = t2.b) + -> Append + -> Seq Scan on prt1_p0 t3 + -> Seq Scan on prt1_p1 t3_1 + -> Seq Scan on prt1_p2 t3_2 + -> Seq Scan on prt1_p3 t3_3 + -> Seq Scan on prt1_p4 t3_4 + -> Hash + -> Append + -> Seq Scan on prt2_p0 t2 + -> Seq Scan on prt2_p1 t2_1 + -> Seq Scan on prt2_p2 t2_2 + -> Seq Scan on prt2_p3 t2_3 + -> Seq Scan on prt2_p4 t2_4 + -> Seq Scan on prt2_p5 t2_5 -> Hash -> Result One-Time Filter: false @@ -1271,16 +4070,22 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a = 1 AND a = 2) t1 -> Hash Left Join Hash Cond: (t2.b = a) -> Append - -> Seq Scan on prt2_p1 t2 + -> Seq Scan on prt2_p0 t2 Filter: (a = 0) - -> Seq Scan on prt2_p2 t2_1 + -> Seq Scan on prt2_p1 t2_1 Filter: (a = 0) - -> Seq Scan on prt2_p3 t2_2 + -> Seq Scan on prt2_p2 t2_2 + Filter: (a = 0) + -> Seq Scan on prt2_p3 t2_3 + Filter: (a = 0) + -> Seq Scan on prt2_p4 t2_4 + Filter: (a = 0) + -> Seq Scan on prt2_p5 t2_5 Filter: (a = 0) -> Hash -> Result One-Time Filter: false -(14 rows) +(20 rows) -- -- tests for hash partitioned tables. @@ -1356,41 +4161,9 @@ SELECT avg(t1.a), avg(t2.b), avg(t3.a + t3.b), t1.c, t2.c, t3.c FROM pht1 t1, ph 273.0000000000000000 | 273.0000000000000000 | 548.0000000000000000 | 0005 | 0005 | A0005 (6 rows) --- test default partition behavior for range -ALTER TABLE prt1 DETACH PARTITION prt1_p3; -ALTER TABLE prt1 ATTACH PARTITION prt1_p3 DEFAULT; -ANALYZE prt1; -ALTER TABLE prt2 DETACH PARTITION prt2_p3; -ALTER TABLE prt2 ATTACH PARTITION prt2_p3 DEFAULT; -ANALYZE prt2; -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.b = 0 ORDER BY t1.a, t2.b; - QUERY PLAN --------------------------------------------------- - Sort - Sort Key: t1.a - -> Append - -> Hash Join - Hash Cond: (t2.b = t1.a) - -> Seq Scan on prt2_p1 t2 - -> Hash - -> Seq Scan on prt1_p1 t1 - Filter: (b = 0) - -> Hash Join - Hash Cond: (t2_1.b = t1_1.a) - -> Seq Scan on prt2_p2 t2_1 - -> Hash - -> Seq Scan on prt1_p2 t1_1 - Filter: (b = 0) - -> Hash Join - Hash Cond: (t2_2.b = t1_2.a) - -> Seq Scan on prt2_p3 t2_2 - -> Hash - -> Seq Scan on prt1_p3 t1_2 - Filter: (b = 0) -(21 rows) - --- test default partition behavior for list +-- test default partition behavior for list, should not use partition-wise join +-- since default partition from one side matches multiple partitions on the +-- other ALTER TABLE plt1 DETACH PARTITION plt1_p3; ALTER TABLE plt1 ATTACH PARTITION plt1_p3 DEFAULT; ANALYZE plt1; @@ -1405,26 +4178,24 @@ SELECT avg(t1.a), avg(t2.b), t1.c, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c Sort Key: t1.c -> HashAggregate Group Key: t1.c, t2.c - -> Append - -> Hash Join - Hash Cond: (t2.c = t1.c) - -> Seq Scan on plt2_p1 t2 - -> Hash - -> Seq Scan on plt1_p1 t1 + -> Hash Join + Hash Cond: ((t2.c)::text = (t1.c)::text) + -> Append + -> Seq Scan on plt2_p4 t2 + -> Seq Scan on plt2_p1 t2_1 + -> Seq Scan on plt2_p2 t2_2 + -> Seq Scan on plt2_p3 t2_3 + -> Hash + -> Append + -> Seq Scan on plt1_p4 t1 Filter: ((a % 25) = 0) - -> Hash Join - Hash Cond: (t2_1.c = t1_1.c) - -> Seq Scan on plt2_p2 t2_1 - -> Hash - -> Seq Scan on plt1_p2 t1_1 + -> Seq Scan on plt1_p1 t1_1 Filter: ((a % 25) = 0) - -> Hash Join - Hash Cond: (t2_2.c = t1_2.c) - -> Seq Scan on plt2_p3 t2_2 - -> Hash - -> Seq Scan on plt1_p3 t1_2 + -> Seq Scan on plt1_p2 t1_2 Filter: ((a % 25) = 0) -(23 rows) + -> Seq Scan on plt1_p3 t1_3 + Filter: ((a % 25) = 0) +(21 rows) -- -- multiple levels of partitioning @@ -1826,64 +4597,70 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt4_n t2 WHERE t1.a = t2.a; Hash Join Hash Cond: (t1.a = t2.a) -> Append - -> Seq Scan on prt1_p1 t1 - -> Seq Scan on prt1_p2 t1_1 - -> Seq Scan on prt1_p3 t1_2 + -> Seq Scan on prt1_p0 t1 + -> Seq Scan on prt1_p1 t1_1 + -> Seq Scan on prt1_p2 t1_2 + -> Seq Scan on prt1_p3 t1_3 + -> Seq Scan on prt1_p4 t1_4 -> Hash -> Append -> Seq Scan on prt4_n_p1 t2 -> Seq Scan on prt4_n_p2 t2_1 -> Seq Scan on prt4_n_p3 t2_2 -(11 rows) +(13 rows) EXPLAIN (COSTS OFF) SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt4_n t2, prt2 t3 WHERE t1.a = t2.a and t1.a = t3.b; - QUERY PLAN --------------------------------------------------------- + QUERY PLAN +---------------------------------------------------------- Hash Join - Hash Cond: (t2.a = t1.a) + Hash Cond: (t3.b = t1.a) -> Append - -> Seq Scan on prt4_n_p1 t2 - -> Seq Scan on prt4_n_p2 t2_1 - -> Seq Scan on prt4_n_p3 t2_2 + -> Seq Scan on prt2_p0 t3 + -> Seq Scan on prt2_p1 t3_1 + -> Seq Scan on prt2_p2 t3_2 + -> Seq Scan on prt2_p3 t3_3 + -> Seq Scan on prt2_p4 t3_4 + -> Seq Scan on prt2_p5 t3_5 -> Hash - -> Append - -> Hash Join - Hash Cond: (t1.a = t3.b) - -> Seq Scan on prt1_p1 t1 - -> Hash - -> Seq Scan on prt2_p1 t3 - -> Hash Join - Hash Cond: (t1_1.a = t3_1.b) - -> Seq Scan on prt1_p2 t1_1 - -> Hash - -> Seq Scan on prt2_p2 t3_1 - -> Hash Join - Hash Cond: (t1_2.a = t3_2.b) - -> Seq Scan on prt1_p3 t1_2 - -> Hash - -> Seq Scan on prt2_p3 t3_2 + -> Hash Join + Hash Cond: (t1.a = t2.a) + -> Append + -> Seq Scan on prt1_p0 t1 + -> Seq Scan on prt1_p1 t1_1 + -> Seq Scan on prt1_p2 t1_2 + -> Seq Scan on prt1_p3 t1_3 + -> Seq Scan on prt1_p4 t1_4 + -> Hash + -> Append + -> Seq Scan on prt4_n_p1 t2 + -> Seq Scan on prt4_n_p2 t2_1 + -> Seq Scan on prt4_n_p3 t2_2 (23 rows) -- partitionwise join can not be applied if there are no equi-join conditions -- between partition keys EXPLAIN (COSTS OFF) SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1 LEFT JOIN prt2 t2 ON (t1.a < t2.b); - QUERY PLAN ---------------------------------------------------------- + QUERY PLAN +-------------------------------------------- Nested Loop Left Join + Join Filter: (t1.a < t2.b) -> Append - -> Seq Scan on prt1_p1 t1 - -> Seq Scan on prt1_p2 t1_1 - -> Seq Scan on prt1_p3 t1_2 - -> Append - -> Index Scan using iprt2_p1_b on prt2_p1 t2 - Index Cond: (t1.a < b) - -> Index Scan using iprt2_p2_b on prt2_p2 t2_1 - Index Cond: (t1.a < b) - -> Index Scan using iprt2_p3_b on prt2_p3 t2_2 - Index Cond: (t1.a < b) -(12 rows) + -> Seq Scan on prt1_p0 t1 + -> Seq Scan on prt1_p1 t1_1 + -> Seq Scan on prt1_p2 t1_2 + -> Seq Scan on prt1_p3 t1_3 + -> Seq Scan on prt1_p4 t1_4 + -> Materialize + -> Append + -> Seq Scan on prt2_p0 t2 + -> Seq Scan on prt2_p1 t2_1 + -> Seq Scan on prt2_p2 t2_2 + -> Seq Scan on prt2_p3 t2_3 + -> Seq Scan on prt2_p4 t2_4 + -> Seq Scan on prt2_p5 t2_5 +(16 rows) -- equi-join with join condition on partial keys does not qualify for -- partitionwise join @@ -1969,16 +4746,17 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_n t1 JOIN prt2_n t2 ON (t1.c = t2.c) JOI -> Seq Scan on prt2_n_p2 t2_1 -> Hash -> Hash Join - Hash Cond: (t3.c = (t1.c)::text) + Hash Cond: ((t3.c)::text = (t1.c)::text) -> Append - -> Seq Scan on plt1_p1 t3 - -> Seq Scan on plt1_p2 t3_1 - -> Seq Scan on plt1_p3 t3_2 + -> Seq Scan on plt1_p4 t3 + -> Seq Scan on plt1_p1 t3_1 + -> Seq Scan on plt1_p2 t3_2 + -> Seq Scan on plt1_p3 t3_3 -> Hash -> Append -> Seq Scan on prt1_n_p1 t1 -> Seq Scan on prt1_n_p2 t1_1 -(16 rows) +(17 rows) -- partitionwise join can not be applied for a join between list and range -- partitioned table @@ -1989,14 +4767,16 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_n t1 FULL JOIN prt1 t2 ON (t1.c = t2.c); Hash Full Join Hash Cond: ((t2.c)::text = (t1.c)::text) -> Append - -> Seq Scan on prt1_p1 t2 - -> Seq Scan on prt1_p2 t2_1 - -> Seq Scan on prt1_p3 t2_2 + -> Seq Scan on prt1_p0 t2 + -> Seq Scan on prt1_p1 t2_1 + -> Seq Scan on prt1_p2 t2_2 + -> Seq Scan on prt1_p3 t2_3 + -> Seq Scan on prt1_p4 t2_4 -> Hash -> Append -> Seq Scan on prt1_n_p1 t1 -> Seq Scan on prt1_n_p2 t1_1 -(10 rows) +(12 rows) -- partitionwise join can not be applied if only one of joining table has -- default partition @@ -2012,16 +4792,23 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.b = -> Hash Join Hash Cond: (t2.b = t1.a) -> Append - -> Seq Scan on prt2_p1 t2 - -> Seq Scan on prt2_p2 t2_1 - -> Seq Scan on prt2_p3 t2_2 + -> Seq Scan on prt2_p0 t2 + -> Seq Scan on prt2_p1 t2_1 + -> Seq Scan on prt2_p2 t2_2 + -> Seq Scan on prt2_p3 t2_3 + -> Seq Scan on prt2_p4 t2_4 + -> Seq Scan on prt2_p5 t2_5 -> Hash -> Append - -> Seq Scan on prt1_p1 t1 + -> Seq Scan on prt1_p0 t1 Filter: (b = 0) - -> Seq Scan on prt1_p2 t1_1 + -> Seq Scan on prt1_p1 t1_1 Filter: (b = 0) - -> Seq Scan on prt1_p3 t1_2 + -> Seq Scan on prt1_p2 t1_2 Filter: (b = 0) -(16 rows) + -> Seq Scan on prt1_p3 t1_3 + Filter: (b = 0) + -> Seq Scan on prt1_p4 t1_4 + Filter: (b = 0) +(23 rows) diff --git a/src/test/regress/sql/partition_join.sql b/src/test/regress/sql/partition_join.sql index c1c9859651..0e884835fb 100644 --- a/src/test/regress/sql/partition_join.sql +++ b/src/test/regress/sql/partition_join.sql @@ -10,25 +10,39 @@ SET enable_partitionwise_join to true; -- partitioned by a single column -- CREATE TABLE prt1 (a int, b int, c varchar) PARTITION BY RANGE(a); +CREATE TABLE prt1_p0 PARTITION OF prt1 FOR VALUES FROM (MINVALUE) TO (0); CREATE TABLE prt1_p1 PARTITION OF prt1 FOR VALUES FROM (0) TO (250); CREATE TABLE prt1_p3 PARTITION OF prt1 FOR VALUES FROM (500) TO (600); CREATE TABLE prt1_p2 PARTITION OF prt1 FOR VALUES FROM (250) TO (500); -INSERT INTO prt1 SELECT i, i % 25, to_char(i, 'FM0000') FROM generate_series(0, 599) i WHERE i % 2 = 0; +CREATE TABLE prt1_p4 PARTITION OF prt1 FOR VALUES FROM (600) TO (800); +INSERT INTO prt1 SELECT i, i % 25, to_char(i, 'FM0000') FROM generate_series(-250, 799) i WHERE i % 2 = 0; +CREATE INDEX iprt1_p0_a on prt1_p0(a); CREATE INDEX iprt1_p1_a on prt1_p1(a); CREATE INDEX iprt1_p2_a on prt1_p2(a); CREATE INDEX iprt1_p3_a on prt1_p3(a); +CREATE INDEX iprt1_p4_a on prt1_p4(a); ANALYZE prt1; +-- prt2 have missing starting MINVALUE to -250 range and +-- extra bounds from 800 to MAXVALUE CREATE TABLE prt2 (a int, b int, c varchar) PARTITION BY RANGE(b); +CREATE TABLE prt2_p0 PARTITION OF prt2 FOR VALUES FROM (-250) TO (0); CREATE TABLE prt2_p1 PARTITION OF prt2 FOR VALUES FROM (0) TO (250); CREATE TABLE prt2_p2 PARTITION OF prt2 FOR VALUES FROM (250) TO (500); CREATE TABLE prt2_p3 PARTITION OF prt2 FOR VALUES FROM (500) TO (600); -INSERT INTO prt2 SELECT i % 25, i, to_char(i, 'FM0000') FROM generate_series(0, 599) i WHERE i % 3 = 0; +CREATE TABLE prt2_p4 PARTITION OF prt2 FOR VALUES FROM (600) TO (MAXVALUE); +INSERT INTO prt2 SELECT i % 25, i, to_char(i, 'FM0000') FROM generate_series(-250, 799) i WHERE i % 3 = 0; +CREATE INDEX iprt2_p0_b on prt2_p0(b); CREATE INDEX iprt2_p1_b on prt2_p1(b); CREATE INDEX iprt2_p2_b on prt2_p2(b); CREATE INDEX iprt2_p3_b on prt2_p3(b); +CREATE INDEX iprt2_p4_b on prt2_p4(b); ANALYZE prt2; +-- Partition-wise-join is possible with some partition bounds overlap +-- with each other completely and some partialy for inner,left,right, +-- full, semi and anti joins + -- inner join EXPLAIN (COSTS OFF) SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.b = 0 ORDER BY t1.a, t2.b; @@ -67,11 +81,19 @@ EXPLAIN (COSTS OFF) SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t2.b FROM prt2 t2 WHERE t2.a = 0) AND t1.b = 0 ORDER BY t1.a; SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t2.b FROM prt2 t2 WHERE t2.a = 0) AND t1.b = 0 ORDER BY t1.a; +EXPLAIN (COSTS OFF) +SELECT t1.* FROM prt2 t1 WHERE t1.b IN (SELECT t2.a FROM prt1 t2 WHERE t2.b = 0) AND t1.a = 0 ORDER BY t1.b; +SELECT t1.* FROM prt2 t1 WHERE t1.b IN (SELECT t2.a FROM prt1 t2 WHERE t2.b = 0) AND t1.a = 0 ORDER BY t1.b; + -- Anti-join with aggregates EXPLAIN (COSTS OFF) SELECT sum(t1.a), avg(t1.a), sum(t1.b), avg(t1.b) FROM prt1 t1 WHERE NOT EXISTS (SELECT 1 FROM prt2 t2 WHERE t1.a = t2.b); SELECT sum(t1.a), avg(t1.a), sum(t1.b), avg(t1.b) FROM prt1 t1 WHERE NOT EXISTS (SELECT 1 FROM prt2 t2 WHERE t1.a = t2.b); +EXPLAIN (COSTS OFF) +SELECT t1.b, t1.c FROM prt2 t1 WHERE NOT EXISTS (SELECT 1 FROM prt1 t2 WHERE t1.b = t2.a) and t1.a = 0; +SELECT t1.b, t1.c FROM prt2 t1 WHERE NOT EXISTS (SELECT 1 FROM prt1 t2 WHERE t1.b = t2.a) and t1.a = 0; + -- lateral reference EXPLAIN (COSTS OFF) SELECT * FROM prt1 t1 LEFT JOIN LATERAL @@ -93,20 +115,30 @@ SELECT t1.a, ss.t2a, ss.t2c FROM prt1 t1 LEFT JOIN LATERAL -- partitioned by expression -- CREATE TABLE prt1_e (a int, b int, c int) PARTITION BY RANGE(((a + b)/2)); +CREATE TABLE prt1_e_p0 PARTITION OF prt1_e FOR VALUES FROM (MINVALUE) TO (0); CREATE TABLE prt1_e_p1 PARTITION OF prt1_e FOR VALUES FROM (0) TO (250); CREATE TABLE prt1_e_p2 PARTITION OF prt1_e FOR VALUES FROM (250) TO (500); CREATE TABLE prt1_e_p3 PARTITION OF prt1_e FOR VALUES FROM (500) TO (600); +CREATE TABLE prt1_e_p4 PARTITION OF prt1_e FOR VALUES FROM (600) TO (MAXVALUE); INSERT INTO prt1_e SELECT i, i, i % 25 FROM generate_series(0, 599, 2) i; +INSERT INTO prt1_e SELECT i, i, i % 25 FROM generate_series(-250, 0, 2) i; +INSERT INTO prt1_e SELECT i, i, i % 25 FROM generate_series(600, 799, 2) i; +CREATE INDEX iprt1_e_p0_ab2 on prt1_e_p1(((a+b)/2)); CREATE INDEX iprt1_e_p1_ab2 on prt1_e_p1(((a+b)/2)); CREATE INDEX iprt1_e_p2_ab2 on prt1_e_p2(((a+b)/2)); CREATE INDEX iprt1_e_p3_ab2 on prt1_e_p3(((a+b)/2)); +CREATE INDEX iprt1_e_p4_ab2 on prt1_e_p1(((a+b)/2)); ANALYZE prt1_e; CREATE TABLE prt2_e (a int, b int, c int) PARTITION BY RANGE(((b + a)/2)); +CREATE TABLE prt2_e_p0 PARTITION OF prt2_e FOR VALUES FROM (MINVALUE) TO (0); CREATE TABLE prt2_e_p1 PARTITION OF prt2_e FOR VALUES FROM (0) TO (250); CREATE TABLE prt2_e_p2 PARTITION OF prt2_e FOR VALUES FROM (250) TO (500); CREATE TABLE prt2_e_p3 PARTITION OF prt2_e FOR VALUES FROM (500) TO (600); +CREATE TABLE prt2_e_p4 PARTITION OF prt2_e FOR VALUES FROM (600) TO (MAXVALUE); INSERT INTO prt2_e SELECT i, i, i % 25 FROM generate_series(0, 599, 3) i; +INSERT INTO prt2_e SELECT i, i, i % 25 FROM generate_series(-250, 0, 3) i; +INSERT INTO prt2_e SELECT i, i, i % 25 FROM generate_series(600, 799, 3) i; ANALYZE prt2_e; EXPLAIN (COSTS OFF) @@ -169,6 +201,128 @@ SELECT t1.a, t2.b FROM prt1 t1, prt2 t2 WHERE t1::text = t2::text AND t1.a = t2. RESET enable_hashjoin; RESET enable_nestloop; +-- test default partition behavior for range, partition-wise join is not +-- possible since more than one partition on one side matches default partition +-- on the other side. Default partition from prt1 matches default partition and +-- prt2_p4 from prt2 and default partition from prt2 matches default partition +-- and prt1_p0 from prt1 +ALTER TABLE prt1 DETACH PARTITION prt1_p3; +ALTER TABLE prt1 ATTACH PARTITION prt1_p3 DEFAULT; +ANALYZE prt1; +ALTER TABLE prt2 DETACH PARTITION prt2_p3; +ALTER TABLE prt2 ATTACH PARTITION prt2_p3 DEFAULT; +ANALYZE prt2; +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.b = 0 ORDER BY t1.a, t2.b; + +-- partition-wise join should be possible when we drop the first and last +-- partitions from both sides +ALTER TABLE prt1 DETACH PARTITION prt1_p0; +ALTER TABLE prt1 DETACH PARTITION prt1_p4; +ANALYZE prt1; +ALTER TABLE prt2 DETACH PARTITION prt2_p0; +ALTER TABLE prt2 DETACH PARTITION prt2_p4; +ANALYZE prt2; +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.b = 0 ORDER BY t1.a, t2.b; + +-- restore the partitioned tables for rest of the tests +ALTER TABLE prt1 ATTACH PARTITION prt1_p0 FOR VALUES FROM (MINVALUE) TO (0); +ALTER TABLE prt1 ATTACH PARTITION prt1_p4 FOR VALUES FROM (600) TO (800); +ALTER TABLE prt1 DETACH PARTITION prt1_p3; +ALTER TABLE prt1 ATTACH PARTITION prt1_p3 FOR VALUES FROM (500) TO (600); +ANALYZE prt1; +ALTER TABLE prt2 ATTACH PARTITION prt2_p0 FOR VALUES FROM (-250) TO (0); +ALTER TABLE prt2 ATTACH PARTITION prt2_p4 FOR VALUES FROM (600) TO (MAXVALUE); +ALTER TABLE prt2 DETACH PARTITION prt2_p3; +ALTER TABLE prt2 ATTACH PARTITION prt2_p3 FOR VALUES FROM (500) TO (600); +ANALYZE prt2; + +-- Add an extra partition to prt2 , Partition-wise join is possible with +-- extra partitions on inner side are allowed +DROP TABLE prt2_p4; +CREATE TABLE prt2_p4 PARTITION OF prt2 FOR VALUES FROM (600) TO (800); +CREATE TABLE prt2_p5 PARTITION OF prt2 FOR VALUES FROM (800) TO (1000); +INSERT INTO prt2 SELECT i % 25, i, to_char(i, 'FM0000') FROM generate_series(600, 999) i WHERE i % 3 = 0; +ANALYZE prt2; + +-- inner join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 INNER JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a; +SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 INNER JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a; + +-- left join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a; +SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a; + +-- semi join +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from prt1 t1 where exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c; +select t1.a, t1.b, t1.c from prt1 t1 where exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c; + +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from prt2 t1 where exists (select 1 from prt1 t2 WHERE t1.b = t2.a) and t1.a = 0 order by t1.a, t1.b, t1.c; +select t1.a, t1.b, t1.c from prt2 t1 where exists (select 1 from prt1 t2 WHERE t1.b = t2.a) and t1.a = 0 order by t1.a, t1.b, t1.c; + +-- anti join +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from prt1 t1 where not exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c; +select t1.a, t1.b, t1.c from prt1 t1 where not exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c; + +-- 3-way join when not every pair of joining relation can use partition-wise +-- join +EXPLAIN (COSTS OFF) +SELECT t1.a, t2.a, t3.c FROM prt1 t1 RIGHT JOIN prt2 t2 ON (t1.a = t2.b) INNER JOIN prt1 t3 ON (t2.b = t3.a) WHERE t2.a = 0 ORDER BY t1.a, t2.a, t3.c; +SELECT t1.a, t2.a, t3.c FROM prt1 t1 RIGHT JOIN prt2 t2 ON (t1.a = t2.b) INNER JOIN prt1 t3 ON (t2.b = t3.a) WHERE t2.a = 0 ORDER BY t1.a, t2.a, t3.c; + +-- partition-wise join can not handle missing partition on the inner side +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b WHERE t2.a = 0 ORDER BY t2.b; +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 FULL JOIN prt2 t2 ON t1.a = t2.b WHERE coalesce(t1.b, 0) + coalesce(t2.a, 0) = 0 ORDER BY t1.a, t2.a; +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from prt2 t1 where not exists (select 1 from prt1 t2 WHERE t1.b = t2.a) and t1.a = 0 order by t1.a, t1.b, t1.c; + +-- Partition-wise join can not handle the case when one partition from one side +-- matches with multiple partitions on the other side +DROP TABLE prt2_p4; +DROP TABLE prt2_p5; +CREATE TABLE prt2_p4 PARTITION OF prt2 FOR VALUES FROM (600) TO (700); +CREATE TABLE prt2_p5 PARTITION OF prt2 FOR VALUES FROM (700) TO (1000); +INSERT INTO prt2 SELECT i % 25, i, to_char(i, 'FM0000') FROM generate_series(600, 999, 3) i; +ANALYZE prt2; + +-- inner join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 INNER JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a; + +-- left join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a; + +-- right join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b WHERE t2.a = 0 ORDER BY t2.a; + +-- full join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 FULL JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b + t2.a = 0 ORDER BY t1.a, t2.a; + +-- semi join +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from prt1 t1 where exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c; + +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from prt2 t1 where exists (select 1 from prt1 t2 WHERE t1.b = t2.a) and t1.a = 0 order by t1.a, t1.b, t1.c; + +-- anti join +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from prt1 t1 where not exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c; + +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from prt2 t1 where not exists (select 1 from prt1 t2 WHERE t1.b = t2.a) and t1.a = 0 order by t1.a, t1.b, t1.c; + -- -- partitioned by multiple columns -- @@ -193,28 +347,79 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_m WHERE prt1_m.c = 0) t1 -- -- tests for list partitioned tables. -- -CREATE TABLE plt1 (a int, b int, c text) PARTITION BY LIST(c); -CREATE TABLE plt1_p1 PARTITION OF plt1 FOR VALUES IN ('0000', '0003', '0004', '0010'); -CREATE TABLE plt1_p2 PARTITION OF plt1 FOR VALUES IN ('0001', '0005', '0002', '0009'); -CREATE TABLE plt1_p3 PARTITION OF plt1 FOR VALUES IN ('0006', '0007', '0008', '0011'); -INSERT INTO plt1 SELECT i, i, to_char(i/50, 'FM0000') FROM generate_series(0, 599, 2) i; -ANALYZE plt1; +\set part_mod 17 +\set cond_mod 47 +\set num_rows 500 + +CREATE TABLE plt1 (a int, b int, c varchar) PARTITION BY LIST(c); +CREATE TABLE plt1_p1 PARTITION OF plt1 FOR VALUES IN ('0001','0002','0003'); +CREATE TABLE plt1_p2 PARTITION OF plt1 FOR VALUES IN ('0004','0005','0006'); +CREATE TABLE plt1_p3 PARTITION OF plt1 FOR VALUES IN ('0008','0009'); +CREATE TABLE plt1_p4 PARTITION OF plt1 FOR VALUES IN ('0000','0010'); +INSERT INTO plt1 SELECT i, i % :cond_mod, to_char(i % :part_mod, 'FM0000') FROM generate_series(0, :num_rows) i WHERE i % :part_mod NOT IN (7, 11, 12, 13, 14, 15, 16); +ANALYSE plt1; + +-- plt2 have missing starting 0001, additional 0007, missing ending 0010 +-- and additional 0011 and 0012 bounds +CREATE TABLE plt2 (a int, b int, c varchar) PARTITION BY LIST(c); +CREATE TABLE plt2_p1 PARTITION OF plt2 FOR VALUES IN ('0002','0003'); +CREATE TABLE plt2_p2 PARTITION OF plt2 FOR VALUES IN ('0004','0005','0006'); +CREATE TABLE plt2_p3 PARTITION OF plt2 FOR VALUES IN ('0007','0008','0009'); +CREATE TABLE plt2_p4 PARTITION OF plt2 FOR VALUES IN ('0000','0011','0012'); +INSERT INTO plt2 SELECT i, i % :cond_mod, to_char(i % :part_mod, 'FM0000') FROM generate_series(0, :num_rows) i WHERE i % :part_mod NOT IN (1, 10, 13, 14, 15, 16); +ANALYSE plt2; + +-- Partition-wise-join is possible with some partition bounds overlap +-- with each other completely and some partialy for inner,left,right, +-- full, semi and anti joins -CREATE TABLE plt2 (a int, b int, c text) PARTITION BY LIST(c); -CREATE TABLE plt2_p1 PARTITION OF plt2 FOR VALUES IN ('0000', '0003', '0004', '0010'); -CREATE TABLE plt2_p2 PARTITION OF plt2 FOR VALUES IN ('0001', '0005', '0002', '0009'); -CREATE TABLE plt2_p3 PARTITION OF plt2 FOR VALUES IN ('0006', '0007', '0008', '0011'); -INSERT INTO plt2 SELECT i, i, to_char(i/50, 'FM0000') FROM generate_series(0, 599, 3) i; -ANALYZE plt2; +-- inner join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a; +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a; + +-- left join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a; +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a; + +-- right join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t2.a; +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t1.a; + +-- full join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a; +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a; + +-- semi join +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; +select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; +select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + +-- anti join +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; +select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; +select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; -- -- list partitioned by expression -- CREATE TABLE plt1_e (a int, b int, c text) PARTITION BY LIST(ltrim(c, 'A')); -CREATE TABLE plt1_e_p1 PARTITION OF plt1_e FOR VALUES IN ('0000', '0003', '0004', '0010'); -CREATE TABLE plt1_e_p2 PARTITION OF plt1_e FOR VALUES IN ('0001', '0005', '0002', '0009'); -CREATE TABLE plt1_e_p3 PARTITION OF plt1_e FOR VALUES IN ('0006', '0007', '0008', '0011'); -INSERT INTO plt1_e SELECT i, i, 'A' || to_char(i/50, 'FM0000') FROM generate_series(0, 599, 2) i; +CREATE TABLE plt1_e_p1 PARTITION OF plt1_e FOR VALUES IN ('0002', '0003'); +CREATE TABLE plt1_e_p2 PARTITION OF plt1_e FOR VALUES IN ('0004', '0005', '0006'); +CREATE TABLE plt1_e_p3 PARTITION OF plt1_e FOR VALUES IN ('0008', '0009'); +CREATE TABLE plt1_e_p4 PARTITION OF plt1_e FOR VALUES IN ('0000'); +INSERT INTO plt1_e SELECT i, i % :cond_mod, to_char(i % :part_mod, 'FM0000') FROM generate_series(0, :num_rows) i WHERE i % :part_mod NOT IN (1, 7, 10, 11, 12, 13, 14, 15, 16); ANALYZE plt1_e; -- test partition matching with N-way join @@ -222,6 +427,175 @@ EXPLAIN (COSTS OFF) SELECT avg(t1.a), avg(t2.b), avg(t3.a + t3.b), t1.c, t2.c, t3.c FROM plt1 t1, plt2 t2, plt1_e t3 WHERE t1.b = t2.b AND t1.c = t2.c AND ltrim(t3.c, 'A') = t1.c GROUP BY t1.c, t2.c, t3.c ORDER BY t1.c, t2.c, t3.c; SELECT avg(t1.a), avg(t2.b), avg(t3.a + t3.b), t1.c, t2.c, t3.c FROM plt1 t1, plt2 t2, plt1_e t3 WHERE t1.b = t2.b AND t1.c = t2.c AND ltrim(t3.c, 'A') = t1.c GROUP BY t1.c, t2.c, t3.c ORDER BY t1.c, t2.c, t3.c; +-- Add an extra partition to plt2 , Partition-wise join is possible with +-- partitions on inner side are allowed +CREATE TABLE plt2_p5 PARTITION OF plt2 FOR VALUES IN ('0013','0014'); +INSERT INTO plt2 SELECT i, i % :cond_mod, to_char(i % :part_mod, 'FM0000') FROM generate_series(0, :num_rows) i WHERE i % :part_mod IN (13, 14); +ANALYZE plt2; + +-- inner join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a; +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a; + +-- left join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a; +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a; + +-- right join, partition-wise join can not handle extra partition on the outer +-- side +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t2.a; +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t1.a; + +-- full join, partition-wise join can not handle extra partition on the outer +-- side +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a; +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a; + +-- semi join +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; +select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; +select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + +-- anti join +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; +select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; +select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + +-- Partition-wise join can not handle the case when one partition from one side +-- matches with multiple partitions on the other side +DROP TABLE plt2_p5; +CREATE TABLE plt2_p5 PARTITION OF plt2 FOR VALUES IN ('0001','0013','0014'); +INSERT INTO plt2 SELECT i, i % :cond_mod, to_char(i % :part_mod, 'FM0000') FROM generate_series(0, :num_rows) i WHERE i % :part_mod IN (1, 13, 14); +ANALYZE plt2; + +-- inner join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a; + +-- left join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a; + +-- right join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t2.a; + +-- full join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a; + +-- semi join +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + +-- anti join +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + +-- partition have a NULL on one side, Partition-wise join is possible with +-- NULL when NULL comparision is not strict i.e. NULL=NULL allowed +-- in this case NULL will be treated as addition partition bounds. +DROP TABLE plt2_p5; +DROP TABLE plt2_p4; +CREATE TABLE plt2_p4 PARTITION OF plt2 FOR VALUES IN ('0000',NULL,'0012'); +INSERT INTO plt2 SELECT i, i % :cond_mod, case when i % :part_mod = 11 then NULL else to_char(i % :part_mod, 'FM0000') end FROM generate_series(0, :num_rows) i WHERE i % :part_mod IN (0,11,12); +ANALYZE plt2; + +-- inner join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a; +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a; + +-- left join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a; +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a; + +-- right join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t2.a; +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t1.a; + +-- full join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a; +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a; + +-- semi join +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; +select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; +select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + +-- anti join +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; +select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; +select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + +-- partition have a NULL on both side with different partition bounds w.r.t other side +-- NULL when NULL comparision is not strict i.e. NULL=NULL allowed +-- Partition-wise join can not handle the case when one partition from one side +-- matches with multiple partitions on the other side +DROP TABLE plt1_p3; +CREATE TABLE plt1_p3 PARTITION OF plt1 FOR VALUES IN (NULL,'0008','0009'); +INSERT INTO plt1 SELECT i, i % :cond_mod, case when i % :part_mod = 7 then NULL else to_char(i % :part_mod, 'FM0000') end FROM generate_series(0, :num_rows) i WHERE i % :part_mod IN (7,8,9); +ANALYZE plt1; + +-- inner join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a; + +-- left join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a; + +-- right join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t2.a; + +-- full join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a; + +-- semi join +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + +-- anti join +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + -- joins where one of the relations is proven empty EXPLAIN (COSTS OFF) SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.a = 1 AND t1.a = 2; @@ -267,27 +641,18 @@ EXPLAIN (COSTS OFF) SELECT avg(t1.a), avg(t2.b), avg(t3.a + t3.b), t1.c, t2.c, t3.c FROM pht1 t1, pht2 t2, pht1_e t3 WHERE t1.b = t2.b AND t1.c = t2.c AND ltrim(t3.c, 'A') = t1.c GROUP BY t1.c, t2.c, t3.c ORDER BY t1.c, t2.c, t3.c; SELECT avg(t1.a), avg(t2.b), avg(t3.a + t3.b), t1.c, t2.c, t3.c FROM pht1 t1, pht2 t2, pht1_e t3 WHERE t1.b = t2.b AND t1.c = t2.c AND ltrim(t3.c, 'A') = t1.c GROUP BY t1.c, t2.c, t3.c ORDER BY t1.c, t2.c, t3.c; --- test default partition behavior for range -ALTER TABLE prt1 DETACH PARTITION prt1_p3; -ALTER TABLE prt1 ATTACH PARTITION prt1_p3 DEFAULT; -ANALYZE prt1; -ALTER TABLE prt2 DETACH PARTITION prt2_p3; -ALTER TABLE prt2 ATTACH PARTITION prt2_p3 DEFAULT; -ANALYZE prt2; - -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.b = 0 ORDER BY t1.a, t2.b; - --- test default partition behavior for list +-- test default partition behavior for list, should not use partition-wise join +-- since default partition from one side matches multiple partitions on the +-- other ALTER TABLE plt1 DETACH PARTITION plt1_p3; ALTER TABLE plt1 ATTACH PARTITION plt1_p3 DEFAULT; ANALYZE plt1; ALTER TABLE plt2 DETACH PARTITION plt2_p3; ALTER TABLE plt2 ATTACH PARTITION plt2_p3 DEFAULT; ANALYZE plt2; - EXPLAIN (COSTS OFF) SELECT avg(t1.a), avg(t2.b), t1.c, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.a % 25 = 0 GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c; + -- -- multiple levels of partitioning -- -- 2.19.2 --------------010102020204050006020806-- ^ permalink raw reply [nested|flat] 8+ messages in thread
* [PATCH 3/3] Tests for 0:1, 1:1 and 1:0 partition matching @ 2019-01-31 11:31 Etsuro Fujita <[email protected]> 0 siblings, 0 replies; 8+ messages in thread From: Etsuro Fujita @ 2019-01-31 11:31 UTC (permalink / raw) Rajkumar Raghuvanshi and Ashutosh Bapat. --- src/test/regress/expected/partition_join.out | 4131 +++++++++++++++--- src/test/regress/sql/partition_join.sql | 427 +- 2 files changed, 3855 insertions(+), 703 deletions(-) diff --git a/src/test/regress/expected/partition_join.out b/src/test/regress/expected/partition_join.out index c55de5d476..f19611c853 100644 --- a/src/test/regress/expected/partition_join.out +++ b/src/test/regress/expected/partition_join.out @@ -8,59 +8,86 @@ SET enable_partitionwise_join to true; -- partitioned by a single column -- CREATE TABLE prt1 (a int, b int, c varchar) PARTITION BY RANGE(a); +CREATE TABLE prt1_p0 PARTITION OF prt1 FOR VALUES FROM (MINVALUE) TO (0); CREATE TABLE prt1_p1 PARTITION OF prt1 FOR VALUES FROM (0) TO (250); CREATE TABLE prt1_p3 PARTITION OF prt1 FOR VALUES FROM (500) TO (600); CREATE TABLE prt1_p2 PARTITION OF prt1 FOR VALUES FROM (250) TO (500); -INSERT INTO prt1 SELECT i, i % 25, to_char(i, 'FM0000') FROM generate_series(0, 599) i WHERE i % 2 = 0; +CREATE TABLE prt1_p4 PARTITION OF prt1 FOR VALUES FROM (600) TO (800); +INSERT INTO prt1 SELECT i, i % 25, to_char(i, 'FM0000') FROM generate_series(-250, 799) i WHERE i % 2 = 0; +CREATE INDEX iprt1_p0_a on prt1_p0(a); CREATE INDEX iprt1_p1_a on prt1_p1(a); CREATE INDEX iprt1_p2_a on prt1_p2(a); CREATE INDEX iprt1_p3_a on prt1_p3(a); +CREATE INDEX iprt1_p4_a on prt1_p4(a); ANALYZE prt1; +-- prt2 have missing starting MINVALUE to -250 range and +-- extra bounds from 800 to MAXVALUE CREATE TABLE prt2 (a int, b int, c varchar) PARTITION BY RANGE(b); +CREATE TABLE prt2_p0 PARTITION OF prt2 FOR VALUES FROM (-250) TO (0); CREATE TABLE prt2_p1 PARTITION OF prt2 FOR VALUES FROM (0) TO (250); CREATE TABLE prt2_p2 PARTITION OF prt2 FOR VALUES FROM (250) TO (500); CREATE TABLE prt2_p3 PARTITION OF prt2 FOR VALUES FROM (500) TO (600); -INSERT INTO prt2 SELECT i % 25, i, to_char(i, 'FM0000') FROM generate_series(0, 599) i WHERE i % 3 = 0; +CREATE TABLE prt2_p4 PARTITION OF prt2 FOR VALUES FROM (600) TO (MAXVALUE); +INSERT INTO prt2 SELECT i % 25, i, to_char(i, 'FM0000') FROM generate_series(-250, 799) i WHERE i % 3 = 0; +CREATE INDEX iprt2_p0_b on prt2_p0(b); CREATE INDEX iprt2_p1_b on prt2_p1(b); CREATE INDEX iprt2_p2_b on prt2_p2(b); CREATE INDEX iprt2_p3_b on prt2_p3(b); +CREATE INDEX iprt2_p4_b on prt2_p4(b); ANALYZE prt2; +-- Partition-wise-join is possible with some partition bounds overlap +-- with each other completely and some partialy for inner,left,right, +-- full, semi and anti joins -- inner join EXPLAIN (COSTS OFF) SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.b = 0 ORDER BY t1.a, t2.b; - QUERY PLAN --------------------------------------------------- + QUERY PLAN +--------------------------------------------------------------- Sort Sort Key: t1.a -> Append -> Hash Join Hash Cond: (t2.b = t1.a) - -> Seq Scan on prt2_p1 t2 + -> Seq Scan on prt2_p0 t2 -> Hash - -> Seq Scan on prt1_p1 t1 + -> Seq Scan on prt1_p0 t1 Filter: (b = 0) -> Hash Join Hash Cond: (t2_1.b = t1_1.a) - -> Seq Scan on prt2_p2 t2_1 + -> Seq Scan on prt2_p1 t2_1 -> Hash - -> Seq Scan on prt1_p2 t1_1 + -> Seq Scan on prt1_p1 t1_1 Filter: (b = 0) -> Hash Join Hash Cond: (t2_2.b = t1_2.a) - -> Seq Scan on prt2_p3 t2_2 + -> Seq Scan on prt2_p2 t2_2 -> Hash - -> Seq Scan on prt1_p3 t1_2 + -> Seq Scan on prt1_p2 t1_2 Filter: (b = 0) -(21 rows) + -> Nested Loop + -> Seq Scan on prt1_p3 t1_3 + Filter: (b = 0) + -> Index Scan using iprt2_p3_b on prt2_p3 t2_3 + Index Cond: (b = t1_3.a) + -> Hash Join + Hash Cond: (t2_4.b = t1_4.a) + -> Seq Scan on prt2_p4 t2_4 + -> Hash + -> Seq Scan on prt1_p4 t1_4 + Filter: (b = 0) +(32 rows) SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.b = 0 ORDER BY t1.a, t2.b; - a | c | b | c ------+------+-----+------ - 0 | 0000 | 0 | 0000 - 150 | 0150 | 150 | 0150 - 300 | 0300 | 300 | 0300 - 450 | 0450 | 450 | 0450 -(4 rows) + a | c | b | c +------+-------+------+------- + -150 | -0150 | -150 | -0150 + 0 | 0000 | 0 | 0000 + 150 | 0150 | 150 | 0150 + 300 | 0300 | 300 | 0300 + 450 | 0450 | 450 | 0450 + 600 | 0600 | 600 | 0600 + 750 | 0750 | 750 | 0750 +(7 rows) -- left outer join, with whole-row reference; partitionwise join does not apply EXPLAIN (COSTS OFF) @@ -72,35 +99,50 @@ SELECT t1, t2 FROM prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER -> Hash Right Join Hash Cond: (t2.b = t1.a) -> Append - -> Seq Scan on prt2_p1 t2 - -> Seq Scan on prt2_p2 t2_1 - -> Seq Scan on prt2_p3 t2_2 + -> Seq Scan on prt2_p0 t2 + -> Seq Scan on prt2_p1 t2_1 + -> Seq Scan on prt2_p2 t2_2 + -> Seq Scan on prt2_p3 t2_3 + -> Seq Scan on prt2_p4 t2_4 -> Hash -> Append - -> Seq Scan on prt1_p1 t1 + -> Seq Scan on prt1_p0 t1 Filter: (b = 0) - -> Seq Scan on prt1_p2 t1_1 + -> Seq Scan on prt1_p1 t1_1 Filter: (b = 0) - -> Seq Scan on prt1_p3 t1_2 + -> Seq Scan on prt1_p2 t1_2 Filter: (b = 0) -(16 rows) + -> Seq Scan on prt1_p3 t1_3 + Filter: (b = 0) + -> Seq Scan on prt1_p4 t1_4 + Filter: (b = 0) +(22 rows) SELECT t1, t2 FROM prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a, t2.b; - t1 | t2 ---------------+-------------- - (0,0,0000) | (0,0,0000) - (50,0,0050) | - (100,0,0100) | - (150,0,0150) | (0,150,0150) - (200,0,0200) | - (250,0,0250) | - (300,0,0300) | (0,300,0300) - (350,0,0350) | - (400,0,0400) | - (450,0,0450) | (0,450,0450) - (500,0,0500) | - (550,0,0550) | -(12 rows) + t1 | t2 +----------------+---------------- + (-250,0,-0250) | + (-200,0,-0200) | + (-150,0,-0150) | (0,-150,-0150) + (-100,0,-0100) | + (-50,0,-0050) | + (0,0,0000) | (0,0,0000) + (50,0,0050) | + (100,0,0100) | + (150,0,0150) | (0,150,0150) + (200,0,0200) | + (250,0,0250) | + (300,0,0300) | (0,300,0300) + (350,0,0350) | + (400,0,0400) | + (450,0,0450) | (0,450,0450) + (500,0,0500) | + (550,0,0550) | + (600,0,0600) | (0,600,0600) + (650,0,0650) | + (700,0,0700) | + (750,0,0750) | (0,750,0750) +(21 rows) -- right outer join EXPLAIN (COSTS OFF) @@ -112,35 +154,53 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b WHE -> Append -> Hash Right Join Hash Cond: (t1.a = t2.b) - -> Seq Scan on prt1_p1 t1 + -> Seq Scan on prt1_p0 t1 -> Hash - -> Seq Scan on prt2_p1 t2 + -> Seq Scan on prt2_p0 t2 Filter: (a = 0) -> Hash Right Join Hash Cond: (t1_1.a = t2_1.b) - -> Seq Scan on prt1_p2 t1_1 + -> Seq Scan on prt1_p1 t1_1 + -> Hash + -> Seq Scan on prt2_p1 t2_1 + Filter: (a = 0) + -> Hash Right Join + Hash Cond: (t1_2.a = t2_2.b) + -> Seq Scan on prt1_p2 t1_2 -> Hash - -> Seq Scan on prt2_p2 t2_1 + -> Seq Scan on prt2_p2 t2_2 Filter: (a = 0) -> Nested Loop Left Join - -> Seq Scan on prt2_p3 t2_2 + -> Seq Scan on prt2_p3 t2_3 Filter: (a = 0) - -> Index Scan using iprt1_p3_a on prt1_p3 t1_2 - Index Cond: (a = t2_2.b) -(20 rows) + -> Index Scan using iprt1_p3_a on prt1_p3 t1_3 + Index Cond: (a = t2_3.b) + -> Hash Right Join + Hash Cond: (t1_4.a = t2_4.b) + -> Seq Scan on prt1_p4 t1_4 + -> Hash + -> Seq Scan on prt2_p4 t2_4 + Filter: (a = 0) +(32 rows) SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b WHERE t2.a = 0 ORDER BY t1.a, t2.b; - a | c | b | c ------+------+-----+------ - 0 | 0000 | 0 | 0000 - 150 | 0150 | 150 | 0150 - 300 | 0300 | 300 | 0300 - 450 | 0450 | 450 | 0450 - | | 75 | 0075 - | | 225 | 0225 - | | 375 | 0375 - | | 525 | 0525 -(8 rows) + a | c | b | c +------+-------+------+------- + -150 | -0150 | -150 | -0150 + 0 | 0000 | 0 | 0000 + 150 | 0150 | 150 | 0150 + 300 | 0300 | 300 | 0300 + 450 | 0450 | 450 | 0450 + 600 | 0600 | 600 | 0600 + 750 | 0750 | 750 | 0750 + | | -225 | -0225 + | | -75 | -0075 + | | 75 | 0075 + | | 225 | 0225 + | | 375 | 0375 + | | 525 | 0525 + | | 675 | 0675 +(14 rows) -- full outer join, with placeholder vars EXPLAIN (COSTS OFF) @@ -148,8 +208,16 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT 50 phv, * FROM prt1 WHERE prt1.b = 0) QUERY PLAN ------------------------------------------------------------------ Sort - Sort Key: prt1_p1.a, prt2_p1.b + Sort Key: prt1_p0.a, prt2_p0.b -> Append + -> Hash Full Join + Hash Cond: (prt1_p0.a = prt2_p0.b) + Filter: (((50) = prt1_p0.a) OR ((75) = prt2_p0.b)) + -> Seq Scan on prt1_p0 + Filter: (b = 0) + -> Hash + -> Seq Scan on prt2_p0 + Filter: (a = 0) -> Hash Full Join Hash Cond: (prt1_p1.a = prt2_p1.b) Filter: (((50) = prt1_p1.a) OR ((75) = prt2_p1.b)) @@ -174,7 +242,15 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT 50 phv, * FROM prt1 WHERE prt1.b = 0) -> Hash -> Seq Scan on prt2_p3 Filter: (a = 0) -(27 rows) + -> Hash Full Join + Hash Cond: (prt1_p4.a = prt2_p4.b) + Filter: (((50) = prt1_p4.a) OR ((75) = prt2_p4.b)) + -> Seq Scan on prt1_p4 + Filter: (b = 0) + -> Hash + -> Seq Scan on prt2_p4 + Filter: (a = 0) +(43 rows) SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT 50 phv, * FROM prt1 WHERE prt1.b = 0) t1 FULL JOIN (SELECT 75 phv, * FROM prt2 WHERE prt2.a = 0) t2 ON (t1.a = t2.b) WHERE t1.phv = t1.a OR t2.phv = t2.b ORDER BY t1.a, t2.b; a | c | b | c @@ -211,8 +287,15 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a < 450) t1 LEFT JO QUERY PLAN ----------------------------------------------------------- Sort - Sort Key: prt1_p1.a, b + Sort Key: prt1_p0.a, b -> Append + -> Hash Left Join + Hash Cond: (prt1_p0.a = b) + -> Seq Scan on prt1_p0 + Filter: ((a < 450) AND (b = 0)) + -> Hash + -> Result + One-Time Filter: false -> Hash Left Join Hash Cond: (prt1_p1.a = b) -> Seq Scan on prt1_p1 @@ -227,29 +310,42 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a < 450) t1 LEFT JO -> Hash -> Seq Scan on prt1_p2 Filter: ((a < 450) AND (b = 0)) -(17 rows) +(24 rows) SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a < 450) t1 LEFT JOIN (SELECT * FROM prt2 WHERE b > 250) t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a, t2.b; - a | c | b | c ------+------+-----+------ - 0 | 0000 | | - 50 | 0050 | | - 100 | 0100 | | - 150 | 0150 | | - 200 | 0200 | | - 250 | 0250 | | - 300 | 0300 | 300 | 0300 - 350 | 0350 | | - 400 | 0400 | | -(9 rows) + a | c | b | c +------+-------+-----+------ + -250 | -0250 | | + -200 | -0200 | | + -150 | -0150 | | + -100 | -0100 | | + -50 | -0050 | | + 0 | 0000 | | + 50 | 0050 | | + 100 | 0100 | | + 150 | 0150 | | + 200 | 0200 | | + 250 | 0250 | | + 300 | 0300 | 300 | 0300 + 350 | 0350 | | + 400 | 0400 | | +(14 rows) EXPLAIN (COSTS OFF) SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a < 450) t1 FULL JOIN (SELECT * FROM prt2 WHERE b > 250) t2 ON t1.a = t2.b WHERE t1.b = 0 OR t2.a = 0 ORDER BY t1.a, t2.b; QUERY PLAN ------------------------------------------------------------ Sort - Sort Key: prt1_p1.a, b + Sort Key: prt1_p0.a, b -> Append + -> Hash Full Join + Hash Cond: (prt1_p0.a = b) + Filter: ((prt1_p0.b = 0) OR (a = 0)) + -> Seq Scan on prt1_p0 + Filter: (a < 450) + -> Hash + -> Result + One-Time Filter: false -> Hash Full Join Hash Cond: (prt1_p1.a = b) Filter: ((prt1_p1.b = 0) OR (a = 0)) @@ -274,64 +370,153 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a < 450) t1 FULL JO -> Hash -> Result One-Time Filter: false -(27 rows) + -> Hash Full Join + Hash Cond: (prt2_p4.b = a) + Filter: ((b = 0) OR (prt2_p4.a = 0)) + -> Seq Scan on prt2_p4 + Filter: (b > 250) + -> Hash + -> Result + One-Time Filter: false +(43 rows) SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a < 450) t1 FULL JOIN (SELECT * FROM prt2 WHERE b > 250) t2 ON t1.a = t2.b WHERE t1.b = 0 OR t2.a = 0 ORDER BY t1.a, t2.b; - a | c | b | c ------+------+-----+------ - 0 | 0000 | | - 50 | 0050 | | - 100 | 0100 | | - 150 | 0150 | | - 200 | 0200 | | - 250 | 0250 | | - 300 | 0300 | 300 | 0300 - 350 | 0350 | | - 400 | 0400 | | - | | 375 | 0375 - | | 450 | 0450 - | | 525 | 0525 -(12 rows) + a | c | b | c +------+-------+-----+------ + -250 | -0250 | | + -200 | -0200 | | + -150 | -0150 | | + -100 | -0100 | | + -50 | -0050 | | + 0 | 0000 | | + 50 | 0050 | | + 100 | 0100 | | + 150 | 0150 | | + 200 | 0200 | | + 250 | 0250 | | + 300 | 0300 | 300 | 0300 + 350 | 0350 | | + 400 | 0400 | | + | | 375 | 0375 + | | 450 | 0450 + | | 525 | 0525 + | | 600 | 0600 + | | 675 | 0675 + | | 750 | 0750 +(20 rows) -- Semi-join EXPLAIN (COSTS OFF) SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t2.b FROM prt2 t2 WHERE t2.a = 0) AND t1.b = 0 ORDER BY t1.a; - QUERY PLAN --------------------------------------------------- + QUERY PLAN +--------------------------------------------------------------- Sort Sort Key: t1.a -> Append -> Hash Semi Join Hash Cond: (t1.a = t2.b) - -> Seq Scan on prt1_p1 t1 + -> Seq Scan on prt1_p0 t1 Filter: (b = 0) -> Hash - -> Seq Scan on prt2_p1 t2 + -> Seq Scan on prt2_p0 t2 Filter: (a = 0) -> Hash Semi Join Hash Cond: (t1_1.a = t2_1.b) - -> Seq Scan on prt1_p2 t1_1 + -> Seq Scan on prt1_p1 t1_1 Filter: (b = 0) -> Hash - -> Seq Scan on prt2_p2 t2_1 + -> Seq Scan on prt2_p1 t2_1 Filter: (a = 0) - -> Nested Loop Semi Join - Join Filter: (t1_2.a = t2_2.b) - -> Seq Scan on prt1_p3 t1_2 + -> Hash Semi Join + Hash Cond: (t1_2.a = t2_2.b) + -> Seq Scan on prt1_p2 t1_2 Filter: (b = 0) - -> Materialize - -> Seq Scan on prt2_p3 t2_2 + -> Hash + -> Seq Scan on prt2_p2 t2_2 Filter: (a = 0) -(24 rows) + -> Nested Loop + -> HashAggregate + Group Key: t2_3.b + -> Seq Scan on prt2_p3 t2_3 + Filter: (a = 0) + -> Index Scan using iprt1_p3_a on prt1_p3 t1_3 + Index Cond: (a = t2_3.b) + Filter: (b = 0) + -> Hash Semi Join + Hash Cond: (t1_4.a = t2_4.b) + -> Seq Scan on prt1_p4 t1_4 + Filter: (b = 0) + -> Hash + -> Seq Scan on prt2_p4 t2_4 + Filter: (a = 0) +(39 rows) SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t2.b FROM prt2 t2 WHERE t2.a = 0) AND t1.b = 0 ORDER BY t1.a; - a | b | c ------+---+------ - 0 | 0 | 0000 - 150 | 0 | 0150 - 300 | 0 | 0300 - 450 | 0 | 0450 -(4 rows) + a | b | c +------+---+------- + -150 | 0 | -0150 + 0 | 0 | 0000 + 150 | 0 | 0150 + 300 | 0 | 0300 + 450 | 0 | 0450 + 600 | 0 | 0600 + 750 | 0 | 0750 +(7 rows) + +EXPLAIN (COSTS OFF) +SELECT t1.* FROM prt2 t1 WHERE t1.b IN (SELECT t2.a FROM prt1 t2 WHERE t2.b = 0) AND t1.a = 0 ORDER BY t1.b; + QUERY PLAN +-------------------------------------------------- + Sort + Sort Key: t1.b + -> Append + -> Hash Semi Join + Hash Cond: (t1.b = t2.a) + -> Seq Scan on prt2_p0 t1 + Filter: (a = 0) + -> Hash + -> Seq Scan on prt1_p0 t2 + Filter: (b = 0) + -> Hash Semi Join + Hash Cond: (t1_1.b = t2_1.a) + -> Seq Scan on prt2_p1 t1_1 + Filter: (a = 0) + -> Hash + -> Seq Scan on prt1_p1 t2_1 + Filter: (b = 0) + -> Hash Semi Join + Hash Cond: (t1_2.b = t2_2.a) + -> Seq Scan on prt2_p2 t1_2 + Filter: (a = 0) + -> Hash + -> Seq Scan on prt1_p2 t2_2 + Filter: (b = 0) + -> Nested Loop Semi Join + Join Filter: (t1_3.b = t2_3.a) + -> Seq Scan on prt2_p3 t1_3 + Filter: (a = 0) + -> Seq Scan on prt1_p3 t2_3 + Filter: (b = 0) + -> Hash Semi Join + Hash Cond: (t1_4.b = t2_4.a) + -> Seq Scan on prt2_p4 t1_4 + Filter: (a = 0) + -> Hash + -> Seq Scan on prt1_p4 t2_4 + Filter: (b = 0) +(37 rows) + +SELECT t1.* FROM prt2 t1 WHERE t1.b IN (SELECT t2.a FROM prt1 t2 WHERE t2.b = 0) AND t1.a = 0 ORDER BY t1.b; + a | b | c +---+------+------- + 0 | -150 | -0150 + 0 | 0 | 0000 + 0 | 150 | 0150 + 0 | 300 | 0300 + 0 | 450 | 0450 + 0 | 600 | 0600 + 0 | 750 | 0750 +(7 rows) -- Anti-join with aggregates EXPLAIN (COSTS OFF) @@ -342,27 +527,82 @@ SELECT sum(t1.a), avg(t1.a), sum(t1.b), avg(t1.b) FROM prt1 t1 WHERE NOT EXISTS -> Append -> Hash Anti Join Hash Cond: (t1.a = t2.b) - -> Seq Scan on prt1_p1 t1 + -> Seq Scan on prt1_p0 t1 -> Hash - -> Seq Scan on prt2_p1 t2 + -> Seq Scan on prt2_p0 t2 -> Hash Anti Join Hash Cond: (t1_1.a = t2_1.b) - -> Seq Scan on prt1_p2 t1_1 + -> Seq Scan on prt1_p1 t1_1 -> Hash - -> Seq Scan on prt2_p2 t2_1 + -> Seq Scan on prt2_p1 t2_1 -> Hash Anti Join Hash Cond: (t1_2.a = t2_2.b) - -> Seq Scan on prt1_p3 t1_2 + -> Seq Scan on prt1_p2 t1_2 -> Hash - -> Seq Scan on prt2_p3 t2_2 -(17 rows) + -> Seq Scan on prt2_p2 t2_2 + -> Hash Anti Join + Hash Cond: (t1_3.a = t2_3.b) + -> Seq Scan on prt1_p3 t1_3 + -> Hash + -> Seq Scan on prt2_p3 t2_3 + -> Hash Anti Join + Hash Cond: (t1_4.a = t2_4.b) + -> Seq Scan on prt1_p4 t1_4 + -> Hash + -> Seq Scan on prt2_p4 t2_4 +(27 rows) SELECT sum(t1.a), avg(t1.a), sum(t1.b), avg(t1.b) FROM prt1 t1 WHERE NOT EXISTS (SELECT 1 FROM prt2 t2 WHERE t1.a = t2.b); - sum | avg | sum | avg --------+----------------------+------+--------------------- - 60000 | 300.0000000000000000 | 2400 | 12.0000000000000000 + sum | avg | sum | avg +-------+----------------------+------+-------------------- + 95550 | 273.0000000000000000 | 2200 | 6.2857142857142857 (1 row) +EXPLAIN (COSTS OFF) +SELECT t1.b, t1.c FROM prt2 t1 WHERE NOT EXISTS (SELECT 1 FROM prt1 t2 WHERE t1.b = t2.a) and t1.a = 0; + QUERY PLAN +-------------------------------------------------------------- + Append + -> Nested Loop Anti Join + -> Seq Scan on prt2_p0 t1 + Filter: (a = 0) + -> Index Only Scan using iprt1_p0_a on prt1_p0 t2 + Index Cond: (a = t1.b) + -> Hash Anti Join + Hash Cond: (t1_1.b = t2_1.a) + -> Seq Scan on prt2_p1 t1_1 + Filter: (a = 0) + -> Hash + -> Seq Scan on prt1_p1 t2_1 + -> Nested Loop Anti Join + -> Seq Scan on prt2_p2 t1_2 + Filter: (a = 0) + -> Index Only Scan using iprt1_p2_a on prt1_p2 t2_2 + Index Cond: (a = t1_2.b) + -> Nested Loop Anti Join + -> Seq Scan on prt2_p3 t1_3 + Filter: (a = 0) + -> Index Only Scan using iprt1_p3_a on prt1_p3 t2_3 + Index Cond: (a = t1_3.b) + -> Nested Loop Anti Join + -> Seq Scan on prt2_p4 t1_4 + Filter: (a = 0) + -> Index Only Scan using iprt1_p4_a on prt1_p4 t2_4 + Index Cond: (a = t1_4.b) +(27 rows) + +SELECT t1.b, t1.c FROM prt2 t1 WHERE NOT EXISTS (SELECT 1 FROM prt1 t2 WHERE t1.b = t2.a) and t1.a = 0; + b | c +------+------- + -225 | -0225 + -75 | -0075 + 75 | 0075 + 225 | 0225 + 375 | 0375 + 525 | 0525 + 675 | 0675 +(7 rows) + -- lateral reference EXPLAIN (COSTS OFF) SELECT * FROM prt1 t1 LEFT JOIN LATERAL @@ -374,49 +614,74 @@ SELECT * FROM prt1 t1 LEFT JOIN LATERAL Sort Key: t1.a -> Append -> Nested Loop Left Join - -> Seq Scan on prt1_p1 t1 + -> Seq Scan on prt1_p0 t1 Filter: (b = 0) -> Nested Loop - -> Index Only Scan using iprt1_p1_a on prt1_p1 t2 + -> Index Only Scan using iprt1_p0_a on prt1_p0 t2 Index Cond: (a = t1.a) - -> Index Scan using iprt2_p1_b on prt2_p1 t3 + -> Index Scan using iprt2_p0_b on prt2_p0 t3 Index Cond: (b = t2.a) -> Nested Loop Left Join - -> Seq Scan on prt1_p2 t1_1 + -> Seq Scan on prt1_p1 t1_1 Filter: (b = 0) -> Nested Loop - -> Index Only Scan using iprt1_p2_a on prt1_p2 t2_1 + -> Index Only Scan using iprt1_p1_a on prt1_p1 t2_1 Index Cond: (a = t1_1.a) - -> Index Scan using iprt2_p2_b on prt2_p2 t3_1 + -> Index Scan using iprt2_p1_b on prt2_p1 t3_1 Index Cond: (b = t2_1.a) -> Nested Loop Left Join - -> Seq Scan on prt1_p3 t1_2 + -> Seq Scan on prt1_p2 t1_2 Filter: (b = 0) -> Nested Loop - -> Index Only Scan using iprt1_p3_a on prt1_p3 t2_2 + -> Index Only Scan using iprt1_p2_a on prt1_p2 t2_2 Index Cond: (a = t1_2.a) - -> Index Scan using iprt2_p3_b on prt2_p3 t3_2 + -> Index Scan using iprt2_p2_b on prt2_p2 t3_2 Index Cond: (b = t2_2.a) -(27 rows) + -> Nested Loop Left Join + -> Seq Scan on prt1_p3 t1_3 + Filter: (b = 0) + -> Nested Loop + -> Index Only Scan using iprt1_p3_a on prt1_p3 t2_3 + Index Cond: (a = t1_3.a) + -> Index Scan using iprt2_p3_b on prt2_p3 t3_3 + Index Cond: (b = t2_3.a) + -> Nested Loop Left Join + -> Seq Scan on prt1_p4 t1_4 + Filter: (b = 0) + -> Nested Loop + -> Index Only Scan using iprt1_p4_a on prt1_p4 t2_4 + Index Cond: (a = t1_4.a) + -> Index Scan using iprt2_p4_b on prt2_p4 t3_4 + Index Cond: (b = t2_4.a) +(43 rows) SELECT * FROM prt1 t1 LEFT JOIN LATERAL (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.b) FROM prt1 t2 JOIN prt2 t3 ON (t2.a = t3.b)) ss ON t1.a = ss.t2a WHERE t1.b = 0 ORDER BY t1.a; - a | b | c | t2a | t3a | least ------+---+------+-----+-----+------- - 0 | 0 | 0000 | 0 | 0 | 0 - 50 | 0 | 0050 | | | - 100 | 0 | 0100 | | | - 150 | 0 | 0150 | 150 | 0 | 150 - 200 | 0 | 0200 | | | - 250 | 0 | 0250 | | | - 300 | 0 | 0300 | 300 | 0 | 300 - 350 | 0 | 0350 | | | - 400 | 0 | 0400 | | | - 450 | 0 | 0450 | 450 | 0 | 450 - 500 | 0 | 0500 | | | - 550 | 0 | 0550 | | | -(12 rows) + a | b | c | t2a | t3a | least +------+---+-------+------+-----+------- + -250 | 0 | -0250 | | | + -200 | 0 | -0200 | | | + -150 | 0 | -0150 | -150 | 0 | -150 + -100 | 0 | -0100 | | | + -50 | 0 | -0050 | | | + 0 | 0 | 0000 | 0 | 0 | 0 + 50 | 0 | 0050 | | | + 100 | 0 | 0100 | | | + 150 | 0 | 0150 | 150 | 0 | 150 + 200 | 0 | 0200 | | | + 250 | 0 | 0250 | | | + 300 | 0 | 0300 | 300 | 0 | 300 + 350 | 0 | 0350 | | | + 400 | 0 | 0400 | | | + 450 | 0 | 0450 | 450 | 0 | 450 + 500 | 0 | 0500 | | | + 550 | 0 | 0550 | | | + 600 | 0 | 0600 | 600 | 0 | 600 + 650 | 0 | 0650 | | | + 700 | 0 | 0700 | | | + 750 | 0 | 0750 | 750 | 0 | 750 +(21 rows) EXPLAIN (COSTS OFF) SELECT t1.a, ss.t2a, ss.t2c FROM prt1 t1 LEFT JOIN LATERAL @@ -430,64 +695,95 @@ SELECT t1.a, ss.t2a, ss.t2c FROM prt1 t1 LEFT JOIN LATERAL Hash Cond: ((t1.c)::text = (t2.c)::text) Filter: ((t1.b + COALESCE(t2.b, 0)) = 0) -> Append - -> Seq Scan on prt1_p1 t1 - -> Seq Scan on prt1_p2 t1_1 - -> Seq Scan on prt1_p3 t1_2 + -> Seq Scan on prt1_p0 t1 + -> Seq Scan on prt1_p1 t1_1 + -> Seq Scan on prt1_p2 t1_2 + -> Seq Scan on prt1_p3 t1_3 + -> Seq Scan on prt1_p4 t1_4 -> Hash -> Append -> Hash Join Hash Cond: (t2.a = t3.b) - -> Seq Scan on prt1_p1 t2 + -> Seq Scan on prt1_p0 t2 -> Hash - -> Seq Scan on prt2_p1 t3 + -> Seq Scan on prt2_p0 t3 -> Hash Join Hash Cond: (t2_1.a = t3_1.b) - -> Seq Scan on prt1_p2 t2_1 + -> Seq Scan on prt1_p1 t2_1 -> Hash - -> Seq Scan on prt2_p2 t3_1 + -> Seq Scan on prt2_p1 t3_1 -> Hash Join Hash Cond: (t2_2.a = t3_2.b) - -> Seq Scan on prt1_p3 t2_2 + -> Seq Scan on prt1_p2 t2_2 -> Hash - -> Seq Scan on prt2_p3 t3_2 -(26 rows) + -> Seq Scan on prt2_p2 t3_2 + -> Hash Join + Hash Cond: (t2_3.a = t3_3.b) + -> Seq Scan on prt1_p3 t2_3 + -> Hash + -> Seq Scan on prt2_p3 t3_3 + -> Hash Join + Hash Cond: (t2_4.a = t3_4.b) + -> Seq Scan on prt1_p4 t2_4 + -> Hash + -> Seq Scan on prt2_p4 t3_4 +(38 rows) SELECT t1.a, ss.t2a, ss.t2c FROM prt1 t1 LEFT JOIN LATERAL (SELECT t2.a AS t2a, t3.a AS t3a, t2.b t2b, t2.c t2c, least(t1.a,t2.a,t3.a) FROM prt1 t2 JOIN prt2 t3 ON (t2.a = t3.b)) ss ON t1.c = ss.t2c WHERE (t1.b + coalesce(ss.t2b, 0)) = 0 ORDER BY t1.a; - a | t2a | t2c ------+-----+------ - 0 | 0 | 0000 - 50 | | - 100 | | - 150 | 150 | 0150 - 200 | | - 250 | | - 300 | 300 | 0300 - 350 | | - 400 | | - 450 | 450 | 0450 - 500 | | - 550 | | -(12 rows) + a | t2a | t2c +------+------+------- + -250 | | + -200 | | + -150 | -150 | -0150 + -100 | | + -50 | | + 0 | 0 | 0000 + 50 | | + 100 | | + 150 | 150 | 0150 + 200 | | + 250 | | + 300 | 300 | 0300 + 350 | | + 400 | | + 450 | 450 | 0450 + 500 | | + 550 | | + 600 | 600 | 0600 + 650 | | + 700 | | + 750 | 750 | 0750 +(21 rows) -- -- partitioned by expression -- CREATE TABLE prt1_e (a int, b int, c int) PARTITION BY RANGE(((a + b)/2)); +CREATE TABLE prt1_e_p0 PARTITION OF prt1_e FOR VALUES FROM (MINVALUE) TO (0); CREATE TABLE prt1_e_p1 PARTITION OF prt1_e FOR VALUES FROM (0) TO (250); CREATE TABLE prt1_e_p2 PARTITION OF prt1_e FOR VALUES FROM (250) TO (500); CREATE TABLE prt1_e_p3 PARTITION OF prt1_e FOR VALUES FROM (500) TO (600); +CREATE TABLE prt1_e_p4 PARTITION OF prt1_e FOR VALUES FROM (600) TO (MAXVALUE); INSERT INTO prt1_e SELECT i, i, i % 25 FROM generate_series(0, 599, 2) i; +INSERT INTO prt1_e SELECT i, i, i % 25 FROM generate_series(-250, 0, 2) i; +INSERT INTO prt1_e SELECT i, i, i % 25 FROM generate_series(600, 799, 2) i; +CREATE INDEX iprt1_e_p0_ab2 on prt1_e_p1(((a+b)/2)); CREATE INDEX iprt1_e_p1_ab2 on prt1_e_p1(((a+b)/2)); CREATE INDEX iprt1_e_p2_ab2 on prt1_e_p2(((a+b)/2)); CREATE INDEX iprt1_e_p3_ab2 on prt1_e_p3(((a+b)/2)); +CREATE INDEX iprt1_e_p4_ab2 on prt1_e_p1(((a+b)/2)); ANALYZE prt1_e; CREATE TABLE prt2_e (a int, b int, c int) PARTITION BY RANGE(((b + a)/2)); +CREATE TABLE prt2_e_p0 PARTITION OF prt2_e FOR VALUES FROM (MINVALUE) TO (0); CREATE TABLE prt2_e_p1 PARTITION OF prt2_e FOR VALUES FROM (0) TO (250); CREATE TABLE prt2_e_p2 PARTITION OF prt2_e FOR VALUES FROM (250) TO (500); CREATE TABLE prt2_e_p3 PARTITION OF prt2_e FOR VALUES FROM (500) TO (600); +CREATE TABLE prt2_e_p4 PARTITION OF prt2_e FOR VALUES FROM (600) TO (MAXVALUE); INSERT INTO prt2_e SELECT i, i, i % 25 FROM generate_series(0, 599, 3) i; +INSERT INTO prt2_e SELECT i, i, i % 25 FROM generate_series(-250, 0, 3) i; +INSERT INTO prt2_e SELECT i, i, i % 25 FROM generate_series(600, 799, 3) i; ANALYZE prt2_e; EXPLAIN (COSTS OFF) SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_e t1, prt2_e t2 WHERE (t1.a + t1.b)/2 = (t2.b + t2.a)/2 AND t1.c = 0 ORDER BY t1.a, t2.b; @@ -498,32 +794,49 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_e t1, prt2_e t2 WHERE (t1.a + t1.b)/2 = -> Append -> Hash Join Hash Cond: (((t2.b + t2.a) / 2) = ((t1.a + t1.b) / 2)) - -> Seq Scan on prt2_e_p1 t2 + -> Seq Scan on prt2_e_p0 t2 -> Hash - -> Seq Scan on prt1_e_p1 t1 + -> Seq Scan on prt1_e_p0 t1 Filter: (c = 0) -> Hash Join - Hash Cond: (((t2_1.b + t2_1.a) / 2) = ((t1_1.a + t1_1.b) / 2)) - -> Seq Scan on prt2_e_p2 t2_1 + Hash Cond: (((t1_1.a + t1_1.b) / 2) = ((t2_1.b + t2_1.a) / 2)) + -> Seq Scan on prt1_e_p1 t1_1 + Filter: (c = 0) -> Hash - -> Seq Scan on prt1_e_p2 t1_1 - Filter: (c = 0) + -> Seq Scan on prt2_e_p1 t2_1 -> Hash Join Hash Cond: (((t2_2.b + t2_2.a) / 2) = ((t1_2.a + t1_2.b) / 2)) - -> Seq Scan on prt2_e_p3 t2_2 + -> Seq Scan on prt2_e_p2 t2_2 -> Hash - -> Seq Scan on prt1_e_p3 t1_2 + -> Seq Scan on prt1_e_p2 t1_2 Filter: (c = 0) -(21 rows) + -> Hash Join + Hash Cond: (((t2_3.b + t2_3.a) / 2) = ((t1_3.a + t1_3.b) / 2)) + -> Seq Scan on prt2_e_p3 t2_3 + -> Hash + -> Seq Scan on prt1_e_p3 t1_3 + Filter: (c = 0) + -> Hash Join + Hash Cond: (((t2_4.b + t2_4.a) / 2) = ((t1_4.a + t1_4.b) / 2)) + -> Seq Scan on prt2_e_p4 t2_4 + -> Hash + -> Seq Scan on prt1_e_p4 t1_4 + Filter: (c = 0) +(33 rows) SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_e t1, prt2_e t2 WHERE (t1.a + t1.b)/2 = (t2.b + t2.a)/2 AND t1.c = 0 ORDER BY t1.a, t2.b; - a | c | b | c ------+---+-----+--- - 0 | 0 | 0 | 0 - 150 | 0 | 150 | 0 - 300 | 0 | 300 | 0 - 450 | 0 | 450 | 0 -(4 rows) + a | c | b | c +------+---+------+--- + -250 | 0 | -250 | 0 + -100 | 0 | -100 | 0 + 0 | 0 | 0 | 0 + 0 | 0 | 0 | 0 + 150 | 0 | 150 | 0 + 300 | 0 | 300 | 0 + 450 | 0 | 450 | 0 + 600 | 0 | 600 | 0 + 750 | 0 | 750 | 0 +(9 rows) -- -- N-way join @@ -536,154 +849,232 @@ SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM prt1 t1, prt2 t2, prt1_e t Sort Key: t1.a -> Append -> Nested Loop - Join Filter: (t1.a = ((t3.a + t3.b) / 2)) + Join Filter: (t1.a = t2.b) -> Hash Join - Hash Cond: (t2.b = t1.a) - -> Seq Scan on prt2_p1 t2 + Hash Cond: (((t3.a + t3.b) / 2) = t1.a) + -> Seq Scan on prt1_e_p0 t3 -> Hash - -> Seq Scan on prt1_p1 t1 + -> Seq Scan on prt1_p0 t1 Filter: (b = 0) - -> Index Scan using iprt1_e_p1_ab2 on prt1_e_p1 t3 - Index Cond: (((a + b) / 2) = t2.b) + -> Index Scan using iprt2_p0_b on prt2_p0 t2 + Index Cond: (b = ((t3.a + t3.b) / 2)) -> Nested Loop Join Filter: (t1_1.a = ((t3_1.a + t3_1.b) / 2)) -> Hash Join Hash Cond: (t2_1.b = t1_1.a) - -> Seq Scan on prt2_p2 t2_1 + -> Seq Scan on prt2_p1 t2_1 -> Hash - -> Seq Scan on prt1_p2 t1_1 + -> Seq Scan on prt1_p1 t1_1 Filter: (b = 0) - -> Index Scan using iprt1_e_p2_ab2 on prt1_e_p2 t3_1 + -> Index Scan using iprt1_e_p4_ab2 on prt1_e_p1 t3_1 Index Cond: (((a + b) / 2) = t2_1.b) -> Nested Loop Join Filter: (t1_2.a = ((t3_2.a + t3_2.b) / 2)) -> Hash Join Hash Cond: (t2_2.b = t1_2.a) - -> Seq Scan on prt2_p3 t2_2 + -> Seq Scan on prt2_p2 t2_2 -> Hash - -> Seq Scan on prt1_p3 t1_2 + -> Seq Scan on prt1_p2 t1_2 Filter: (b = 0) - -> Index Scan using iprt1_e_p3_ab2 on prt1_e_p3 t3_2 + -> Index Scan using iprt1_e_p2_ab2 on prt1_e_p2 t3_2 Index Cond: (((a + b) / 2) = t2_2.b) -(33 rows) + -> Nested Loop + Join Filter: (t1_3.a = ((t3_3.a + t3_3.b) / 2)) + -> Nested Loop + -> Seq Scan on prt1_p3 t1_3 + Filter: (b = 0) + -> Index Scan using iprt2_p3_b on prt2_p3 t2_3 + Index Cond: (b = t1_3.a) + -> Index Scan using iprt1_e_p3_ab2 on prt1_e_p3 t3_3 + Index Cond: (((a + b) / 2) = t2_3.b) + -> Nested Loop + Join Filter: (t1_4.a = t2_4.b) + -> Hash Join + Hash Cond: (((t3_4.a + t3_4.b) / 2) = t1_4.a) + -> Seq Scan on prt1_e_p4 t3_4 + -> Hash + -> Seq Scan on prt1_p4 t1_4 + Filter: (b = 0) + -> Index Scan using iprt2_p4_b on prt2_p4 t2_4 + Index Cond: (b = ((t3_4.a + t3_4.b) / 2)) +(52 rows) SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM prt1 t1, prt2 t2, prt1_e t3 WHERE t1.a = t2.b AND t1.a = (t3.a + t3.b)/2 AND t1.b = 0 ORDER BY t1.a, t2.b; - a | c | b | c | ?column? | c ------+------+-----+------+----------+--- - 0 | 0000 | 0 | 0000 | 0 | 0 - 150 | 0150 | 150 | 0150 | 300 | 0 - 300 | 0300 | 300 | 0300 | 600 | 0 - 450 | 0450 | 450 | 0450 | 900 | 0 -(4 rows) + a | c | b | c | ?column? | c +------+-------+------+-------+----------+--- + -150 | -0150 | -150 | -0150 | -300 | 0 + 0 | 0000 | 0 | 0000 | 0 | 0 + 0 | 0000 | 0 | 0000 | 0 | 0 + 150 | 0150 | 150 | 0150 | 300 | 0 + 300 | 0300 | 300 | 0300 | 600 | 0 + 450 | 0450 | 450 | 0450 | 900 | 0 + 600 | 0600 | 600 | 0600 | 1200 | 0 + 750 | 0750 | 750 | 0750 | 1500 | 0 +(8 rows) EXPLAIN (COSTS OFF) SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b) LEFT JOIN prt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t1.b = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; - QUERY PLAN --------------------------------------------------------------- + QUERY PLAN +--------------------------------------------------------------------- Sort Sort Key: t1.a, t2.b, ((t3.a + t3.b)) -> Append -> Hash Right Join Hash Cond: (((t3.a + t3.b) / 2) = t1.a) - -> Seq Scan on prt1_e_p1 t3 + -> Seq Scan on prt1_e_p0 t3 -> Hash -> Hash Right Join Hash Cond: (t2.b = t1.a) - -> Seq Scan on prt2_p1 t2 + -> Seq Scan on prt2_p0 t2 -> Hash - -> Seq Scan on prt1_p1 t1 + -> Seq Scan on prt1_p0 t1 Filter: (b = 0) -> Hash Right Join Hash Cond: (((t3_1.a + t3_1.b) / 2) = t1_1.a) - -> Seq Scan on prt1_e_p2 t3_1 + -> Seq Scan on prt1_e_p1 t3_1 -> Hash -> Hash Right Join Hash Cond: (t2_1.b = t1_1.a) - -> Seq Scan on prt2_p2 t2_1 + -> Seq Scan on prt2_p1 t2_1 -> Hash - -> Seq Scan on prt1_p2 t1_1 + -> Seq Scan on prt1_p1 t1_1 Filter: (b = 0) -> Hash Right Join Hash Cond: (((t3_2.a + t3_2.b) / 2) = t1_2.a) - -> Seq Scan on prt1_e_p3 t3_2 + -> Seq Scan on prt1_e_p2 t3_2 -> Hash -> Hash Right Join Hash Cond: (t2_2.b = t1_2.a) - -> Seq Scan on prt2_p3 t2_2 + -> Seq Scan on prt2_p2 t2_2 -> Hash - -> Seq Scan on prt1_p3 t1_2 + -> Seq Scan on prt1_p2 t1_2 Filter: (b = 0) -(33 rows) + -> Nested Loop Left Join + -> Nested Loop Left Join + -> Seq Scan on prt1_p3 t1_3 + Filter: (b = 0) + -> Index Scan using iprt2_p3_b on prt2_p3 t2_3 + Index Cond: (t1_3.a = b) + -> Index Scan using iprt1_e_p3_ab2 on prt1_e_p3 t3_3 + Index Cond: (t1_3.a = ((a + b) / 2)) + -> Hash Right Join + Hash Cond: (((t3_4.a + t3_4.b) / 2) = t1_4.a) + -> Seq Scan on prt1_e_p4 t3_4 + -> Hash + -> Hash Right Join + Hash Cond: (t2_4.b = t1_4.a) + -> Seq Scan on prt2_p4 t2_4 + -> Hash + -> Seq Scan on prt1_p4 t1_4 + Filter: (b = 0) +(51 rows) SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b) LEFT JOIN prt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t1.b = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; - a | c | b | c | ?column? | c ------+------+-----+------+----------+--- - 0 | 0000 | 0 | 0000 | 0 | 0 - 50 | 0050 | | | 100 | 0 - 100 | 0100 | | | 200 | 0 - 150 | 0150 | 150 | 0150 | 300 | 0 - 200 | 0200 | | | 400 | 0 - 250 | 0250 | | | 500 | 0 - 300 | 0300 | 300 | 0300 | 600 | 0 - 350 | 0350 | | | 700 | 0 - 400 | 0400 | | | 800 | 0 - 450 | 0450 | 450 | 0450 | 900 | 0 - 500 | 0500 | | | 1000 | 0 - 550 | 0550 | | | 1100 | 0 -(12 rows) + a | c | b | c | ?column? | c +------+-------+------+-------+----------+--- + -250 | -0250 | | | -500 | 0 + -200 | -0200 | | | -400 | 0 + -150 | -0150 | -150 | -0150 | -300 | 0 + -100 | -0100 | | | -200 | 0 + -50 | -0050 | | | -100 | 0 + 0 | 0000 | 0 | 0000 | 0 | 0 + 0 | 0000 | 0 | 0000 | 0 | 0 + 50 | 0050 | | | 100 | 0 + 100 | 0100 | | | 200 | 0 + 150 | 0150 | 150 | 0150 | 300 | 0 + 200 | 0200 | | | 400 | 0 + 250 | 0250 | | | 500 | 0 + 300 | 0300 | 300 | 0300 | 600 | 0 + 350 | 0350 | | | 700 | 0 + 400 | 0400 | | | 800 | 0 + 450 | 0450 | 450 | 0450 | 900 | 0 + 500 | 0500 | | | 1000 | 0 + 550 | 0550 | | | 1100 | 0 + 600 | 0600 | 600 | 0600 | 1200 | 0 + 650 | 0650 | | | 1300 | 0 + 700 | 0700 | | | 1400 | 0 + 750 | 0750 | 750 | 0750 | 1500 | 0 +(22 rows) EXPLAIN (COSTS OFF) SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t3.c = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; - QUERY PLAN -------------------------------------------------------------------- + QUERY PLAN +--------------------------------------------------------------------- Sort Sort Key: t1.a, t2.b, ((t3.a + t3.b)) -> Append -> Nested Loop Left Join -> Hash Right Join Hash Cond: (t1.a = ((t3.a + t3.b) / 2)) - -> Seq Scan on prt1_p1 t1 + -> Seq Scan on prt1_p0 t1 -> Hash - -> Seq Scan on prt1_e_p1 t3 + -> Seq Scan on prt1_e_p0 t3 Filter: (c = 0) - -> Index Scan using iprt2_p1_b on prt2_p1 t2 + -> Index Scan using iprt2_p0_b on prt2_p0 t2 Index Cond: (t1.a = b) -> Nested Loop Left Join -> Hash Right Join Hash Cond: (t1_1.a = ((t3_1.a + t3_1.b) / 2)) - -> Seq Scan on prt1_p2 t1_1 + -> Seq Scan on prt1_p1 t1_1 -> Hash - -> Seq Scan on prt1_e_p2 t3_1 + -> Seq Scan on prt1_e_p1 t3_1 Filter: (c = 0) - -> Index Scan using iprt2_p2_b on prt2_p2 t2_1 + -> Index Scan using iprt2_p1_b on prt2_p1 t2_1 Index Cond: (t1_1.a = b) -> Nested Loop Left Join -> Hash Right Join Hash Cond: (t1_2.a = ((t3_2.a + t3_2.b) / 2)) - -> Seq Scan on prt1_p3 t1_2 + -> Seq Scan on prt1_p2 t1_2 -> Hash - -> Seq Scan on prt1_e_p3 t3_2 + -> Seq Scan on prt1_e_p2 t3_2 Filter: (c = 0) - -> Index Scan using iprt2_p3_b on prt2_p3 t2_2 + -> Index Scan using iprt2_p2_b on prt2_p2 t2_2 Index Cond: (t1_2.a = b) -(30 rows) + -> Nested Loop Left Join + -> Nested Loop Left Join + -> Seq Scan on prt1_e_p3 t3_3 + Filter: (c = 0) + -> Index Scan using iprt1_p3_a on prt1_p3 t1_3 + Index Cond: (a = ((t3_3.a + t3_3.b) / 2)) + -> Index Scan using iprt2_p3_b on prt2_p3 t2_3 + Index Cond: (t1_3.a = b) + -> Nested Loop Left Join + -> Hash Right Join + Hash Cond: (t1_4.a = ((t3_4.a + t3_4.b) / 2)) + -> Seq Scan on prt1_p4 t1_4 + -> Hash + -> Seq Scan on prt1_e_p4 t3_4 + Filter: (c = 0) + -> Index Scan using iprt2_p4_b on prt2_p4 t2_4 + Index Cond: (t1_4.a = b) +(47 rows) SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t3.c = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; - a | c | b | c | ?column? | c ------+------+-----+------+----------+--- - 0 | 0000 | 0 | 0000 | 0 | 0 - 50 | 0050 | | | 100 | 0 - 100 | 0100 | | | 200 | 0 - 150 | 0150 | 150 | 0150 | 300 | 0 - 200 | 0200 | | | 400 | 0 - 250 | 0250 | | | 500 | 0 - 300 | 0300 | 300 | 0300 | 600 | 0 - 350 | 0350 | | | 700 | 0 - 400 | 0400 | | | 800 | 0 - 450 | 0450 | 450 | 0450 | 900 | 0 - 500 | 0500 | | | 1000 | 0 - 550 | 0550 | | | 1100 | 0 -(12 rows) + a | c | b | c | ?column? | c +------+-------+------+-------+----------+--- + -250 | -0250 | | | -500 | 0 + -200 | -0200 | | | -400 | 0 + -150 | -0150 | -150 | -0150 | -300 | 0 + -100 | -0100 | | | -200 | 0 + -50 | -0050 | | | -100 | 0 + 0 | 0000 | 0 | 0000 | 0 | 0 + 0 | 0000 | 0 | 0000 | 0 | 0 + 50 | 0050 | | | 100 | 0 + 100 | 0100 | | | 200 | 0 + 150 | 0150 | 150 | 0150 | 300 | 0 + 200 | 0200 | | | 400 | 0 + 250 | 0250 | | | 500 | 0 + 300 | 0300 | 300 | 0300 | 600 | 0 + 350 | 0350 | | | 700 | 0 + 400 | 0400 | | | 800 | 0 + 450 | 0450 | 450 | 0450 | 900 | 0 + 500 | 0500 | | | 1000 | 0 + 550 | 0550 | | | 1100 | 0 + 600 | 0600 | 600 | 0600 | 1200 | 0 + 650 | 0650 | | | 1300 | 0 + 700 | 0700 | | | 1400 | 0 + 750 | 0750 | 750 | 0750 | 1500 | 0 +(22 rows) -- Cases with non-nullable expressions in subquery results; -- make sure these go to null as expected @@ -692,21 +1083,34 @@ SELECT t1.a, t1.phv, t2.b, t2.phv, t3.a + t3.b, t3.phv FROM ((SELECT 50 phv, * F QUERY PLAN ---------------------------------------------------------------------------------------------------------------- Sort - Sort Key: prt1_p1.a, prt2_p1.b, ((prt1_e_p1.a + prt1_e_p1.b)) + Sort Key: prt1_p0.a, prt2_p0.b, ((prt1_e_p0.a + prt1_e_p0.b)) -> Append -> Hash Full Join - Hash Cond: (prt1_p1.a = ((prt1_e_p1.a + prt1_e_p1.b) / 2)) - Filter: ((prt1_p1.a = (50)) OR (prt2_p1.b = (75)) OR (((prt1_e_p1.a + prt1_e_p1.b) / 2) = (50))) + Hash Cond: (prt1_p0.a = ((prt1_e_p0.a + prt1_e_p0.b) / 2)) + Filter: ((prt1_p0.a = (50)) OR (prt2_p0.b = (75)) OR (((prt1_e_p0.a + prt1_e_p0.b) / 2) = (50))) -> Hash Full Join - Hash Cond: (prt1_p1.a = prt2_p1.b) - -> Seq Scan on prt1_p1 + Hash Cond: (prt1_p0.a = prt2_p0.b) + -> Seq Scan on prt1_p0 Filter: (b = 0) -> Hash - -> Seq Scan on prt2_p1 + -> Seq Scan on prt2_p0 Filter: (a = 0) -> Hash - -> Seq Scan on prt1_e_p1 + -> Seq Scan on prt1_e_p0 Filter: (c = 0) + -> Hash Full Join + Hash Cond: (((prt1_e_p1.a + prt1_e_p1.b) / 2) = prt1_p1.a) + Filter: ((prt1_p1.a = (50)) OR (prt2_p1.b = (75)) OR (((prt1_e_p1.a + prt1_e_p1.b) / 2) = (50))) + -> Seq Scan on prt1_e_p1 + Filter: (c = 0) + -> Hash + -> Hash Full Join + Hash Cond: (prt1_p1.a = prt2_p1.b) + -> Seq Scan on prt1_p1 + Filter: (b = 0) + -> Hash + -> Seq Scan on prt2_p1 + Filter: (a = 0) -> Hash Full Join Hash Cond: (prt1_p2.a = ((prt1_e_p2.a + prt1_e_p2.b) / 2)) Filter: ((prt1_p2.a = (50)) OR (prt2_p2.b = (75)) OR (((prt1_e_p2.a + prt1_e_p2.b) / 2) = (50))) @@ -733,7 +1137,20 @@ SELECT t1.a, t1.phv, t2.b, t2.phv, t3.a + t3.b, t3.phv FROM ((SELECT 50 phv, * F -> Hash -> Seq Scan on prt1_e_p3 Filter: (c = 0) -(42 rows) + -> Hash Full Join + Hash Cond: (prt1_p4.a = ((prt1_e_p4.a + prt1_e_p4.b) / 2)) + Filter: ((prt1_p4.a = (50)) OR (prt2_p4.b = (75)) OR (((prt1_e_p4.a + prt1_e_p4.b) / 2) = (50))) + -> Hash Full Join + Hash Cond: (prt1_p4.a = prt2_p4.b) + -> Seq Scan on prt1_p4 + Filter: (b = 0) + -> Hash + -> Seq Scan on prt2_p4 + Filter: (a = 0) + -> Hash + -> Seq Scan on prt1_e_p4 + Filter: (c = 0) +(68 rows) SELECT t1.a, t1.phv, t2.b, t2.phv, t3.a + t3.b, t3.phv FROM ((SELECT 50 phv, * FROM prt1 WHERE prt1.b = 0) t1 FULL JOIN (SELECT 75 phv, * FROM prt2 WHERE prt2.a = 0) t2 ON (t1.a = t2.b)) FULL JOIN (SELECT 50 phv, * FROM prt1_e WHERE prt1_e.c = 0) t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t1.a = t1.phv OR t2.b = t2.phv OR (t3.a + t3.b)/2 = t3.phv ORDER BY t1.a, t2.b, t3.a + t3.b; a | phv | b | phv | ?column? | phv @@ -751,172 +1168,260 @@ SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1, prt1_e t2 WHER Sort Key: t1.a -> Append -> Nested Loop - Join Filter: (t1.a = t1_3.b) + Join Filter: (t1.a = t1_5.b) -> HashAggregate - Group Key: t1_3.b + Group Key: t1_5.b -> Hash Join - Hash Cond: (((t2.a + t2.b) / 2) = t1_3.b) - -> Seq Scan on prt1_e_p1 t2 + Hash Cond: (((t2.a + t2.b) / 2) = t1_5.b) + -> Seq Scan on prt1_e_p0 t2 -> Hash - -> Seq Scan on prt2_p1 t1_3 + -> Seq Scan on prt2_p0 t1_5 Filter: (a = 0) - -> Index Scan using iprt1_p1_a on prt1_p1 t1 + -> Index Scan using iprt1_p0_a on prt1_p0 t1 Index Cond: (a = ((t2.a + t2.b) / 2)) Filter: (b = 0) -> Nested Loop - Join Filter: (t1_1.a = t1_4.b) + Join Filter: (t1_1.a = t1_6.b) -> HashAggregate - Group Key: t1_4.b + Group Key: t1_6.b -> Hash Join - Hash Cond: (((t2_1.a + t2_1.b) / 2) = t1_4.b) - -> Seq Scan on prt1_e_p2 t2_1 + Hash Cond: (((t2_1.a + t2_1.b) / 2) = t1_6.b) + -> Seq Scan on prt1_e_p1 t2_1 -> Hash - -> Seq Scan on prt2_p2 t1_4 + -> Seq Scan on prt2_p1 t1_6 Filter: (a = 0) - -> Index Scan using iprt1_p2_a on prt1_p2 t1_1 + -> Index Scan using iprt1_p1_a on prt1_p1 t1_1 Index Cond: (a = ((t2_1.a + t2_1.b) / 2)) Filter: (b = 0) -> Nested Loop - Join Filter: (t1_2.a = t1_5.b) + Join Filter: (t1_2.a = t1_7.b) -> HashAggregate - Group Key: t1_5.b + Group Key: t1_7.b -> Nested Loop - -> Seq Scan on prt2_p3 t1_5 + -> Seq Scan on prt2_p2 t1_7 Filter: (a = 0) - -> Index Scan using iprt1_e_p3_ab2 on prt1_e_p3 t2_2 - Index Cond: (((a + b) / 2) = t1_5.b) - -> Index Scan using iprt1_p3_a on prt1_p3 t1_2 + -> Index Scan using iprt1_e_p2_ab2 on prt1_e_p2 t2_2 + Index Cond: (((a + b) / 2) = t1_7.b) + -> Index Scan using iprt1_p2_a on prt1_p2 t1_2 Index Cond: (a = ((t2_2.a + t2_2.b) / 2)) Filter: (b = 0) -(41 rows) + -> Nested Loop + Join Filter: (t1_3.a = t1_8.b) + -> HashAggregate + Group Key: t1_8.b + -> Nested Loop + -> Seq Scan on prt2_p3 t1_8 + Filter: (a = 0) + -> Index Scan using iprt1_e_p3_ab2 on prt1_e_p3 t2_3 + Index Cond: (((a + b) / 2) = t1_8.b) + -> Index Scan using iprt1_p3_a on prt1_p3 t1_3 + Index Cond: (a = ((t2_3.a + t2_3.b) / 2)) + Filter: (b = 0) + -> Nested Loop + Join Filter: (t1_4.a = t1_9.b) + -> HashAggregate + Group Key: t1_9.b + -> Hash Join + Hash Cond: (((t2_4.a + t2_4.b) / 2) = t1_9.b) + -> Seq Scan on prt1_e_p4 t2_4 + -> Hash + -> Seq Scan on prt2_p4 t1_9 + Filter: (a = 0) + -> Index Scan using iprt1_p4_a on prt1_p4 t1_4 + Index Cond: (a = ((t2_4.a + t2_4.b) / 2)) + Filter: (b = 0) +(66 rows) SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1, prt1_e t2 WHERE t1.a = 0 AND t1.b = (t2.a + t2.b)/2) AND t1.b = 0 ORDER BY t1.a; - a | b | c ------+---+------ - 0 | 0 | 0000 - 150 | 0 | 0150 - 300 | 0 | 0300 - 450 | 0 | 0450 -(4 rows) + a | b | c +------+---+------- + -150 | 0 | -0150 + 0 | 0 | 0000 + 150 | 0 | 0150 + 300 | 0 | 0300 + 450 | 0 | 0450 + 600 | 0 | 0600 + 750 | 0 | 0750 +(7 rows) EXPLAIN (COSTS OFF) SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1 WHERE t1.b IN (SELECT (t1.a + t1.b)/2 FROM prt1_e t1 WHERE t1.c = 0)) AND t1.b = 0 ORDER BY t1.a; - QUERY PLAN -------------------------------------------------------------------------- + QUERY PLAN +--------------------------------------------------------------------------- Sort Sort Key: t1.a -> Append -> Nested Loop -> HashAggregate - Group Key: t1_3.b + Group Key: t1_5.b -> Hash Semi Join - Hash Cond: (t1_3.b = ((t1_6.a + t1_6.b) / 2)) - -> Seq Scan on prt2_p1 t1_3 + Hash Cond: (t1_5.b = ((t1_10.a + t1_10.b) / 2)) + -> Seq Scan on prt2_p0 t1_5 -> Hash - -> Seq Scan on prt1_e_p1 t1_6 + -> Seq Scan on prt1_e_p0 t1_10 Filter: (c = 0) - -> Index Scan using iprt1_p1_a on prt1_p1 t1 - Index Cond: (a = t1_3.b) + -> Index Scan using iprt1_p0_a on prt1_p0 t1 + Index Cond: (a = t1_5.b) Filter: (b = 0) -> Nested Loop -> HashAggregate - Group Key: t1_4.b + Group Key: t1_6.b -> Hash Semi Join - Hash Cond: (t1_4.b = ((t1_7.a + t1_7.b) / 2)) - -> Seq Scan on prt2_p2 t1_4 + Hash Cond: (t1_6.b = ((t1_11.a + t1_11.b) / 2)) + -> Seq Scan on prt2_p1 t1_6 -> Hash - -> Seq Scan on prt1_e_p2 t1_7 + -> Seq Scan on prt1_e_p1 t1_11 Filter: (c = 0) - -> Index Scan using iprt1_p2_a on prt1_p2 t1_1 - Index Cond: (a = t1_4.b) + -> Index Scan using iprt1_p1_a on prt1_p1 t1_1 + Index Cond: (a = t1_6.b) Filter: (b = 0) -> Nested Loop -> HashAggregate - Group Key: t1_5.b + Group Key: t1_7.b -> Hash Semi Join - Hash Cond: (t1_5.b = ((t1_8.a + t1_8.b) / 2)) - -> Seq Scan on prt2_p3 t1_5 + Hash Cond: (t1_7.b = ((t1_12.a + t1_12.b) / 2)) + -> Seq Scan on prt2_p2 t1_7 -> Hash - -> Seq Scan on prt1_e_p3 t1_8 + -> Seq Scan on prt1_e_p2 t1_12 Filter: (c = 0) - -> Index Scan using iprt1_p3_a on prt1_p3 t1_2 - Index Cond: (a = t1_5.b) + -> Index Scan using iprt1_p2_a on prt1_p2 t1_2 + Index Cond: (a = t1_7.b) Filter: (b = 0) -(39 rows) + -> Nested Loop + -> HashAggregate + Group Key: t1_8.b + -> Hash Semi Join + Hash Cond: (t1_8.b = ((t1_13.a + t1_13.b) / 2)) + -> Seq Scan on prt2_p3 t1_8 + -> Hash + -> Seq Scan on prt1_e_p3 t1_13 + Filter: (c = 0) + -> Index Scan using iprt1_p3_a on prt1_p3 t1_3 + Index Cond: (a = t1_8.b) + Filter: (b = 0) + -> Nested Loop + -> HashAggregate + Group Key: t1_9.b + -> Hash Semi Join + Hash Cond: (t1_9.b = ((t1_14.a + t1_14.b) / 2)) + -> Seq Scan on prt2_p4 t1_9 + -> Hash + -> Seq Scan on prt1_e_p4 t1_14 + Filter: (c = 0) + -> Index Scan using iprt1_p4_a on prt1_p4 t1_4 + Index Cond: (a = t1_9.b) + Filter: (b = 0) +(63 rows) SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1 WHERE t1.b IN (SELECT (t1.a + t1.b)/2 FROM prt1_e t1 WHERE t1.c = 0)) AND t1.b = 0 ORDER BY t1.a; - a | b | c ------+---+------ - 0 | 0 | 0000 - 150 | 0 | 0150 - 300 | 0 | 0300 - 450 | 0 | 0450 -(4 rows) + a | b | c +------+---+------- + -150 | 0 | -0150 + 0 | 0 | 0000 + 150 | 0 | 0150 + 300 | 0 | 0300 + 450 | 0 | 0450 + 600 | 0 | 0600 + 750 | 0 | 0750 +(7 rows) -- test merge joins SET enable_hashjoin TO off; SET enable_nestloop TO off; EXPLAIN (COSTS OFF) SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1 WHERE t1.b IN (SELECT (t1.a + t1.b)/2 FROM prt1_e t1 WHERE t1.c = 0)) AND t1.b = 0 ORDER BY t1.a; - QUERY PLAN ----------------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------ Merge Append Sort Key: t1.a -> Merge Semi Join - Merge Cond: (t1.a = t1_3.b) + Merge Cond: (t1.a = t1_5.b) -> Sort Sort Key: t1.a - -> Seq Scan on prt1_p1 t1 + -> Seq Scan on prt1_p0 t1 Filter: (b = 0) -> Merge Semi Join - Merge Cond: (t1_3.b = (((t1_6.a + t1_6.b) / 2))) + Merge Cond: (t1_5.b = (((t1_10.a + t1_10.b) / 2))) -> Sort - Sort Key: t1_3.b - -> Seq Scan on prt2_p1 t1_3 + Sort Key: t1_5.b + -> Seq Scan on prt2_p0 t1_5 -> Sort - Sort Key: (((t1_6.a + t1_6.b) / 2)) - -> Seq Scan on prt1_e_p1 t1_6 + Sort Key: (((t1_10.a + t1_10.b) / 2)) + -> Seq Scan on prt1_e_p0 t1_10 Filter: (c = 0) -> Merge Semi Join - Merge Cond: (t1_1.a = t1_4.b) + Merge Cond: (t1_1.a = t1_6.b) -> Sort Sort Key: t1_1.a - -> Seq Scan on prt1_p2 t1_1 + -> Seq Scan on prt1_p1 t1_1 Filter: (b = 0) -> Merge Semi Join - Merge Cond: (t1_4.b = (((t1_7.a + t1_7.b) / 2))) + Merge Cond: (t1_6.b = (((t1_11.a + t1_11.b) / 2))) -> Sort - Sort Key: t1_4.b - -> Seq Scan on prt2_p2 t1_4 + Sort Key: t1_6.b + -> Seq Scan on prt2_p1 t1_6 -> Sort - Sort Key: (((t1_7.a + t1_7.b) / 2)) - -> Seq Scan on prt1_e_p2 t1_7 + Sort Key: (((t1_11.a + t1_11.b) / 2)) + -> Seq Scan on prt1_e_p1 t1_11 Filter: (c = 0) -> Merge Semi Join - Merge Cond: (t1_2.a = t1_5.b) + Merge Cond: (t1_2.a = t1_7.b) -> Sort Sort Key: t1_2.a - -> Seq Scan on prt1_p3 t1_2 + -> Seq Scan on prt1_p2 t1_2 Filter: (b = 0) -> Merge Semi Join - Merge Cond: (t1_5.b = (((t1_8.a + t1_8.b) / 2))) + Merge Cond: (t1_7.b = (((t1_12.a + t1_12.b) / 2))) -> Sort - Sort Key: t1_5.b - -> Seq Scan on prt2_p3 t1_5 + Sort Key: t1_7.b + -> Seq Scan on prt2_p2 t1_7 -> Sort - Sort Key: (((t1_8.a + t1_8.b) / 2)) - -> Seq Scan on prt1_e_p3 t1_8 + Sort Key: (((t1_12.a + t1_12.b) / 2)) + -> Seq Scan on prt1_e_p2 t1_12 Filter: (c = 0) -(47 rows) + -> Merge Semi Join + Merge Cond: (t1_3.a = t1_8.b) + -> Sort + Sort Key: t1_3.a + -> Seq Scan on prt1_p3 t1_3 + Filter: (b = 0) + -> Merge Semi Join + Merge Cond: (t1_8.b = (((t1_13.a + t1_13.b) / 2))) + -> Sort + Sort Key: t1_8.b + -> Seq Scan on prt2_p3 t1_8 + -> Sort + Sort Key: (((t1_13.a + t1_13.b) / 2)) + -> Seq Scan on prt1_e_p3 t1_13 + Filter: (c = 0) + -> Merge Semi Join + Merge Cond: (t1_4.a = t1_9.b) + -> Sort + Sort Key: t1_4.a + -> Seq Scan on prt1_p4 t1_4 + Filter: (b = 0) + -> Merge Semi Join + Merge Cond: (t1_9.b = (((t1_14.a + t1_14.b) / 2))) + -> Sort + Sort Key: t1_9.b + -> Seq Scan on prt2_p4 t1_9 + -> Sort + Sort Key: (((t1_14.a + t1_14.b) / 2)) + -> Seq Scan on prt1_e_p4 t1_14 + Filter: (c = 0) +(77 rows) SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1 WHERE t1.b IN (SELECT (t1.a + t1.b)/2 FROM prt1_e t1 WHERE t1.c = 0)) AND t1.b = 0 ORDER BY t1.a; - a | b | c ------+---+------ - 0 | 0 | 0000 - 150 | 0 | 0150 - 300 | 0 | 0300 - 450 | 0 | 0450 -(4 rows) + a | b | c +------+---+------- + -150 | 0 | -0150 + 0 | 0 | 0000 + 150 | 0 | 0150 + 300 | 0 | 0300 + 450 | 0 | 0450 + 600 | 0 | 0600 + 750 | 0 | 0750 +(7 rows) EXPLAIN (COSTS OFF) SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t3.c = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; @@ -933,14 +1438,14 @@ SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 Merge Cond: ((((t3.a + t3.b) / 2)) = t1.a) -> Sort Sort Key: (((t3.a + t3.b) / 2)) - -> Seq Scan on prt1_e_p1 t3 + -> Seq Scan on prt1_e_p0 t3 Filter: (c = 0) -> Sort Sort Key: t1.a - -> Seq Scan on prt1_p1 t1 + -> Seq Scan on prt1_p0 t1 -> Sort Sort Key: t2.b - -> Seq Scan on prt2_p1 t2 + -> Seq Scan on prt2_p0 t2 -> Merge Left Join Merge Cond: (t1_1.a = t2_1.b) -> Sort @@ -949,14 +1454,14 @@ SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 Merge Cond: ((((t3_1.a + t3_1.b) / 2)) = t1_1.a) -> Sort Sort Key: (((t3_1.a + t3_1.b) / 2)) - -> Seq Scan on prt1_e_p2 t3_1 + -> Seq Scan on prt1_e_p1 t3_1 Filter: (c = 0) -> Sort Sort Key: t1_1.a - -> Seq Scan on prt1_p2 t1_1 + -> Seq Scan on prt1_p1 t1_1 -> Sort Sort Key: t2_1.b - -> Seq Scan on prt2_p2 t2_1 + -> Seq Scan on prt2_p1 t2_1 -> Merge Left Join Merge Cond: (t1_2.a = t2_2.b) -> Sort @@ -965,32 +1470,74 @@ SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 Merge Cond: ((((t3_2.a + t3_2.b) / 2)) = t1_2.a) -> Sort Sort Key: (((t3_2.a + t3_2.b) / 2)) - -> Seq Scan on prt1_e_p3 t3_2 + -> Seq Scan on prt1_e_p2 t3_2 Filter: (c = 0) -> Sort Sort Key: t1_2.a - -> Seq Scan on prt1_p3 t1_2 + -> Seq Scan on prt1_p2 t1_2 -> Sort Sort Key: t2_2.b - -> Seq Scan on prt2_p3 t2_2 -(51 rows) + -> Seq Scan on prt2_p2 t2_2 + -> Merge Left Join + Merge Cond: (t1_3.a = t2_3.b) + -> Sort + Sort Key: t1_3.a + -> Merge Left Join + Merge Cond: ((((t3_3.a + t3_3.b) / 2)) = t1_3.a) + -> Sort + Sort Key: (((t3_3.a + t3_3.b) / 2)) + -> Seq Scan on prt1_e_p3 t3_3 + Filter: (c = 0) + -> Sort + Sort Key: t1_3.a + -> Seq Scan on prt1_p3 t1_3 + -> Sort + Sort Key: t2_3.b + -> Seq Scan on prt2_p3 t2_3 + -> Merge Left Join + Merge Cond: (t1_4.a = t2_4.b) + -> Sort + Sort Key: t1_4.a + -> Merge Left Join + Merge Cond: ((((t3_4.a + t3_4.b) / 2)) = t1_4.a) + -> Sort + Sort Key: (((t3_4.a + t3_4.b) / 2)) + -> Seq Scan on prt1_e_p4 t3_4 + Filter: (c = 0) + -> Sort + Sort Key: t1_4.a + -> Seq Scan on prt1_p4 t1_4 + -> Sort + Sort Key: t2_4.b + -> Seq Scan on prt2_p4 t2_4 +(83 rows) SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t3.c = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; - a | c | b | c | ?column? | c ------+------+-----+------+----------+--- - 0 | 0000 | 0 | 0000 | 0 | 0 - 50 | 0050 | | | 100 | 0 - 100 | 0100 | | | 200 | 0 - 150 | 0150 | 150 | 0150 | 300 | 0 - 200 | 0200 | | | 400 | 0 - 250 | 0250 | | | 500 | 0 - 300 | 0300 | 300 | 0300 | 600 | 0 - 350 | 0350 | | | 700 | 0 - 400 | 0400 | | | 800 | 0 - 450 | 0450 | 450 | 0450 | 900 | 0 - 500 | 0500 | | | 1000 | 0 - 550 | 0550 | | | 1100 | 0 -(12 rows) + a | c | b | c | ?column? | c +------+-------+------+-------+----------+--- + -250 | -0250 | | | -500 | 0 + -200 | -0200 | | | -400 | 0 + -150 | -0150 | -150 | -0150 | -300 | 0 + -100 | -0100 | | | -200 | 0 + -50 | -0050 | | | -100 | 0 + 0 | 0000 | 0 | 0000 | 0 | 0 + 0 | 0000 | 0 | 0000 | 0 | 0 + 50 | 0050 | | | 100 | 0 + 100 | 0100 | | | 200 | 0 + 150 | 0150 | 150 | 0150 | 300 | 0 + 200 | 0200 | | | 400 | 0 + 250 | 0250 | | | 500 | 0 + 300 | 0300 | 300 | 0300 | 600 | 0 + 350 | 0350 | | | 700 | 0 + 400 | 0400 | | | 800 | 0 + 450 | 0450 | 450 | 0450 | 900 | 0 + 500 | 0500 | | | 1000 | 0 + 550 | 0550 | | | 1100 | 0 + 600 | 0600 | 600 | 0600 | 1200 | 0 + 650 | 0650 | | | 1300 | 0 + 700 | 0700 | | | 1400 | 0 + 750 | 0750 | 750 | 0750 | 1500 | 0 +(22 rows) -- MergeAppend on nullable column EXPLAIN (COSTS OFF) @@ -998,8 +1545,18 @@ SELECT t1.a, t2.b FROM (SELECT * FROM prt1 WHERE a < 450) t1 LEFT JOIN (SELECT * QUERY PLAN ----------------------------------------------------------- Sort - Sort Key: prt1_p1.a, b + Sort Key: prt1_p0.a, b -> Append + -> Merge Left Join + Merge Cond: (prt1_p0.a = b) + -> Sort + Sort Key: prt1_p0.a + -> Seq Scan on prt1_p0 + Filter: ((a < 450) AND (b = 0)) + -> Sort + Sort Key: b + -> Result + One-Time Filter: false -> Merge Left Join Merge Cond: (prt1_p1.a = b) -> Sort @@ -1020,21 +1577,26 @@ SELECT t1.a, t2.b FROM (SELECT * FROM prt1 WHERE a < 450) t1 LEFT JOIN (SELECT * Sort Key: prt2_p2.b -> Seq Scan on prt2_p2 Filter: (b > 250) -(23 rows) +(33 rows) SELECT t1.a, t2.b FROM (SELECT * FROM prt1 WHERE a < 450) t1 LEFT JOIN (SELECT * FROM prt2 WHERE b > 250) t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a, t2.b; - a | b ------+----- - 0 | - 50 | - 100 | - 150 | - 200 | - 250 | - 300 | 300 - 350 | - 400 | -(9 rows) + a | b +------+----- + -250 | + -200 | + -150 | + -100 | + -50 | + 0 | + 50 | + 100 | + 150 | + 200 | + 250 | + 300 | 300 + 350 | + 400 | +(14 rows) -- merge join when expression with whole-row reference needs to be sorted; -- partitionwise join does not apply @@ -1048,175 +1610,2412 @@ SELECT t1.a, t2.b FROM prt1 t1, prt2 t2 WHERE t1::text = t2::text AND t1.a = t2. Sort Key: t1.a, ((((t1.*)::prt1))::text) -> Result -> Append - -> Seq Scan on prt1_p1 t1 - -> Seq Scan on prt1_p2 t1_1 - -> Seq Scan on prt1_p3 t1_2 + -> Seq Scan on prt1_p0 t1 + -> Seq Scan on prt1_p1 t1_1 + -> Seq Scan on prt1_p2 t1_2 + -> Seq Scan on prt1_p3 t1_3 + -> Seq Scan on prt1_p4 t1_4 -> Sort Sort Key: t2.b, ((((t2.*)::prt2))::text) -> Result -> Append - -> Seq Scan on prt2_p1 t2 - -> Seq Scan on prt2_p2 t2_1 - -> Seq Scan on prt2_p3 t2_2 -(16 rows) + -> Seq Scan on prt2_p0 t2 + -> Seq Scan on prt2_p1 t2_1 + -> Seq Scan on prt2_p2 t2_2 + -> Seq Scan on prt2_p3 t2_3 + -> Seq Scan on prt2_p4 t2_4 +(20 rows) SELECT t1.a, t2.b FROM prt1 t1, prt2 t2 WHERE t1::text = t2::text AND t1.a = t2.b ORDER BY t1.a; - a | b -----+---- - 0 | 0 - 6 | 6 - 12 | 12 - 18 | 18 - 24 | 24 -(5 rows) + a | b +-----+----- + -24 | -24 + -18 | -18 + -12 | -12 + -6 | -6 + 0 | 0 + 6 | 6 + 12 | 12 + 18 | 18 + 24 | 24 +(9 rows) RESET enable_hashjoin; RESET enable_nestloop; --- --- partitioned by multiple columns --- -CREATE TABLE prt1_m (a int, b int, c int) PARTITION BY RANGE(a, ((a + b)/2)); -CREATE TABLE prt1_m_p1 PARTITION OF prt1_m FOR VALUES FROM (0, 0) TO (250, 250); -CREATE TABLE prt1_m_p2 PARTITION OF prt1_m FOR VALUES FROM (250, 250) TO (500, 500); -CREATE TABLE prt1_m_p3 PARTITION OF prt1_m FOR VALUES FROM (500, 500) TO (600, 600); -INSERT INTO prt1_m SELECT i, i, i % 25 FROM generate_series(0, 599, 2) i; -ANALYZE prt1_m; -CREATE TABLE prt2_m (a int, b int, c int) PARTITION BY RANGE(((b + a)/2), b); -CREATE TABLE prt2_m_p1 PARTITION OF prt2_m FOR VALUES FROM (0, 0) TO (250, 250); -CREATE TABLE prt2_m_p2 PARTITION OF prt2_m FOR VALUES FROM (250, 250) TO (500, 500); -CREATE TABLE prt2_m_p3 PARTITION OF prt2_m FOR VALUES FROM (500, 500) TO (600, 600); -INSERT INTO prt2_m SELECT i, i, i % 25 FROM generate_series(0, 599, 3) i; -ANALYZE prt2_m; +-- test default partition behavior for range, partition-wise join is not +-- possible since more than one partition on one side matches default partition +-- on the other side. Default partition from prt1 matches default partition and +-- prt2_p4 from prt2 and default partition from prt2 matches default partition +-- and prt1_p0 from prt1 +ALTER TABLE prt1 DETACH PARTITION prt1_p3; +ALTER TABLE prt1 ATTACH PARTITION prt1_p3 DEFAULT; +ANALYZE prt1; +ALTER TABLE prt2 DETACH PARTITION prt2_p3; +ALTER TABLE prt2 ATTACH PARTITION prt2_p3 DEFAULT; +ANALYZE prt2; EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_m WHERE prt1_m.c = 0) t1 FULL JOIN (SELECT * FROM prt2_m WHERE prt2_m.c = 0) t2 ON (t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2) ORDER BY t1.a, t2.b; - QUERY PLAN ------------------------------------------------------------------------------------------------------------------------------------- +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.b = 0 ORDER BY t1.a, t2.b; + QUERY PLAN +-------------------------------------------------- + Sort + Sort Key: t1.a + -> Hash Join + Hash Cond: (t2.b = t1.a) + -> Append + -> Seq Scan on prt2_p0 t2 + -> Seq Scan on prt2_p1 t2_1 + -> Seq Scan on prt2_p2 t2_2 + -> Seq Scan on prt2_p4 t2_3 + -> Seq Scan on prt2_p3 t2_4 + -> Hash + -> Append + -> Seq Scan on prt1_p0 t1 + Filter: (b = 0) + -> Seq Scan on prt1_p1 t1_1 + Filter: (b = 0) + -> Seq Scan on prt1_p2 t1_2 + Filter: (b = 0) + -> Seq Scan on prt1_p4 t1_3 + Filter: (b = 0) + -> Seq Scan on prt1_p3 t1_4 + Filter: (b = 0) +(22 rows) + +-- partition-wise join should be possible when we drop the first and last +-- partitions from both sides +ALTER TABLE prt1 DETACH PARTITION prt1_p0; +ALTER TABLE prt1 DETACH PARTITION prt1_p4; +ANALYZE prt1; +ALTER TABLE prt2 DETACH PARTITION prt2_p0; +ALTER TABLE prt2 DETACH PARTITION prt2_p4; +ANALYZE prt2; +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.b = 0 ORDER BY t1.a, t2.b; + QUERY PLAN +-------------------------------------------------- + Sort + Sort Key: t1.a + -> Append + -> Hash Join + Hash Cond: (t2.b = t1.a) + -> Seq Scan on prt2_p1 t2 + -> Hash + -> Seq Scan on prt1_p1 t1 + Filter: (b = 0) + -> Hash Join + Hash Cond: (t2_1.b = t1_1.a) + -> Seq Scan on prt2_p2 t2_1 + -> Hash + -> Seq Scan on prt1_p2 t1_1 + Filter: (b = 0) + -> Hash Join + Hash Cond: (t2_2.b = t1_2.a) + -> Seq Scan on prt2_p3 t2_2 + -> Hash + -> Seq Scan on prt1_p3 t1_2 + Filter: (b = 0) +(21 rows) + +-- restore the partitioned tables for rest of the tests +ALTER TABLE prt1 ATTACH PARTITION prt1_p0 FOR VALUES FROM (MINVALUE) TO (0); +ALTER TABLE prt1 ATTACH PARTITION prt1_p4 FOR VALUES FROM (600) TO (800); +ALTER TABLE prt1 DETACH PARTITION prt1_p3; +ALTER TABLE prt1 ATTACH PARTITION prt1_p3 FOR VALUES FROM (500) TO (600); +ANALYZE prt1; +ALTER TABLE prt2 ATTACH PARTITION prt2_p0 FOR VALUES FROM (-250) TO (0); +ALTER TABLE prt2 ATTACH PARTITION prt2_p4 FOR VALUES FROM (600) TO (MAXVALUE); +ALTER TABLE prt2 DETACH PARTITION prt2_p3; +ALTER TABLE prt2 ATTACH PARTITION prt2_p3 FOR VALUES FROM (500) TO (600); +ANALYZE prt2; +-- Add an extra partition to prt2 , Partition-wise join is possible with +-- extra partitions on inner side are allowed +DROP TABLE prt2_p4; +CREATE TABLE prt2_p4 PARTITION OF prt2 FOR VALUES FROM (600) TO (800); +CREATE TABLE prt2_p5 PARTITION OF prt2 FOR VALUES FROM (800) TO (1000); +INSERT INTO prt2 SELECT i % 25, i, to_char(i, 'FM0000') FROM generate_series(600, 999) i WHERE i % 3 = 0; +ANALYZE prt2; +-- inner join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 INNER JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a; + QUERY PLAN +--------------------------------------------------------------- + Sort + Sort Key: t1.a + -> Append + -> Hash Join + Hash Cond: (t2.b = t1.a) + -> Seq Scan on prt2_p0 t2 + -> Hash + -> Seq Scan on prt1_p0 t1 + Filter: (b = 0) + -> Hash Join + Hash Cond: (t2_1.b = t1_1.a) + -> Seq Scan on prt2_p1 t2_1 + -> Hash + -> Seq Scan on prt1_p1 t1_1 + Filter: (b = 0) + -> Hash Join + Hash Cond: (t2_2.b = t1_2.a) + -> Seq Scan on prt2_p2 t2_2 + -> Hash + -> Seq Scan on prt1_p2 t1_2 + Filter: (b = 0) + -> Nested Loop + -> Seq Scan on prt1_p3 t1_3 + Filter: (b = 0) + -> Index Scan using iprt2_p3_b on prt2_p3 t2_3 + Index Cond: (b = t1_3.a) + -> Hash Join + Hash Cond: (t2_4.b = t1_4.a) + -> Seq Scan on prt2_p4 t2_4 + -> Hash + -> Seq Scan on prt1_p4 t1_4 + Filter: (b = 0) +(32 rows) + +SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 INNER JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a; + a | c | a | c +------+-------+---+------- + -150 | -0150 | 0 | -0150 + 0 | 0000 | 0 | 0000 + 150 | 0150 | 0 | 0150 + 300 | 0300 | 0 | 0300 + 450 | 0450 | 0 | 0450 + 600 | 0600 | 0 | 0600 + 750 | 0750 | 0 | 0750 +(7 rows) + +-- left join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a; + QUERY PLAN +--------------------------------------------------------------- + Sort + Sort Key: t1.a + -> Append + -> Hash Right Join + Hash Cond: (t2.b = t1.a) + -> Seq Scan on prt2_p0 t2 + -> Hash + -> Seq Scan on prt1_p0 t1 + Filter: (b = 0) + -> Hash Right Join + Hash Cond: (t2_1.b = t1_1.a) + -> Seq Scan on prt2_p1 t2_1 + -> Hash + -> Seq Scan on prt1_p1 t1_1 + Filter: (b = 0) + -> Hash Right Join + Hash Cond: (t2_2.b = t1_2.a) + -> Seq Scan on prt2_p2 t2_2 + -> Hash + -> Seq Scan on prt1_p2 t1_2 + Filter: (b = 0) + -> Nested Loop Left Join + -> Seq Scan on prt1_p3 t1_3 + Filter: (b = 0) + -> Index Scan using iprt2_p3_b on prt2_p3 t2_3 + Index Cond: (t1_3.a = b) + -> Hash Right Join + Hash Cond: (t2_4.b = t1_4.a) + -> Seq Scan on prt2_p4 t2_4 + -> Hash + -> Seq Scan on prt1_p4 t1_4 + Filter: (b = 0) +(32 rows) + +SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a; + a | c | a | c +------+-------+---+------- + -250 | -0250 | | + -200 | -0200 | | + -150 | -0150 | 0 | -0150 + -100 | -0100 | | + -50 | -0050 | | + 0 | 0000 | 0 | 0000 + 50 | 0050 | | + 100 | 0100 | | + 150 | 0150 | 0 | 0150 + 200 | 0200 | | + 250 | 0250 | | + 300 | 0300 | 0 | 0300 + 350 | 0350 | | + 400 | 0400 | | + 450 | 0450 | 0 | 0450 + 500 | 0500 | | + 550 | 0550 | | + 600 | 0600 | 0 | 0600 + 650 | 0650 | | + 700 | 0700 | | + 750 | 0750 | 0 | 0750 +(21 rows) + +-- semi join +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from prt1 t1 where exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c; + QUERY PLAN +-------------------------------------------------------------------- + Sort + Sort Key: t1.a, t1.c + -> Append + -> Hash Semi Join + Hash Cond: (t1.a = t2.b) + -> Seq Scan on prt1_p0 t1 + Filter: (b = 0) + -> Hash + -> Seq Scan on prt2_p0 t2 + -> Hash Semi Join + Hash Cond: (t1_1.a = t2_1.b) + -> Seq Scan on prt1_p1 t1_1 + Filter: (b = 0) + -> Hash + -> Seq Scan on prt2_p1 t2_1 + -> Hash Semi Join + Hash Cond: (t1_2.a = t2_2.b) + -> Seq Scan on prt1_p2 t1_2 + Filter: (b = 0) + -> Hash + -> Seq Scan on prt2_p2 t2_2 + -> Nested Loop Semi Join + -> Seq Scan on prt1_p3 t1_3 + Filter: (b = 0) + -> Index Only Scan using iprt2_p3_b on prt2_p3 t2_3 + Index Cond: (b = t1_3.a) + -> Hash Semi Join + Hash Cond: (t1_4.a = t2_4.b) + -> Seq Scan on prt1_p4 t1_4 + Filter: (b = 0) + -> Hash + -> Seq Scan on prt2_p4 t2_4 +(32 rows) + +select t1.a, t1.b, t1.c from prt1 t1 where exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c; + a | b | c +------+---+------- + -150 | 0 | -0150 + 0 | 0 | 0000 + 150 | 0 | 0150 + 300 | 0 | 0300 + 450 | 0 | 0450 + 600 | 0 | 0600 + 750 | 0 | 0750 +(7 rows) + +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from prt2 t1 where exists (select 1 from prt1 t2 WHERE t1.b = t2.a) and t1.a = 0 order by t1.a, t1.b, t1.c; + QUERY PLAN +-------------------------------------------------------------------- + Sort + Sort Key: t1.b, t1.c + -> Append + -> Nested Loop Semi Join + -> Seq Scan on prt2_p0 t1 + Filter: (a = 0) + -> Index Only Scan using iprt1_p0_a on prt1_p0 t2 + Index Cond: (a = t1.b) + -> Nested Loop Semi Join + -> Seq Scan on prt2_p1 t1_1 + Filter: (a = 0) + -> Index Only Scan using iprt1_p1_a on prt1_p1 t2_1 + Index Cond: (a = t1_1.b) + -> Nested Loop Semi Join + -> Seq Scan on prt2_p2 t1_2 + Filter: (a = 0) + -> Index Only Scan using iprt1_p2_a on prt1_p2 t2_2 + Index Cond: (a = t1_2.b) + -> Nested Loop Semi Join + -> Seq Scan on prt2_p3 t1_3 + Filter: (a = 0) + -> Index Only Scan using iprt1_p3_a on prt1_p3 t2_3 + Index Cond: (a = t1_3.b) + -> Nested Loop Semi Join + -> Seq Scan on prt2_p4 t1_4 + Filter: (a = 0) + -> Index Only Scan using iprt1_p4_a on prt1_p4 t2_4 + Index Cond: (a = t1_4.b) +(28 rows) + +select t1.a, t1.b, t1.c from prt2 t1 where exists (select 1 from prt1 t2 WHERE t1.b = t2.a) and t1.a = 0 order by t1.a, t1.b, t1.c; + a | b | c +---+------+------- + 0 | -150 | -0150 + 0 | 0 | 0000 + 0 | 150 | 0150 + 0 | 300 | 0300 + 0 | 450 | 0450 + 0 | 600 | 0600 + 0 | 750 | 0750 +(7 rows) + +-- anti join +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from prt1 t1 where not exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c; + QUERY PLAN +-------------------------------------------------------------------- + Sort + Sort Key: t1.a, t1.c + -> Append + -> Hash Anti Join + Hash Cond: (t1.a = t2.b) + -> Seq Scan on prt1_p0 t1 + Filter: (b = 0) + -> Hash + -> Seq Scan on prt2_p0 t2 + -> Hash Anti Join + Hash Cond: (t1_1.a = t2_1.b) + -> Seq Scan on prt1_p1 t1_1 + Filter: (b = 0) + -> Hash + -> Seq Scan on prt2_p1 t2_1 + -> Hash Anti Join + Hash Cond: (t1_2.a = t2_2.b) + -> Seq Scan on prt1_p2 t1_2 + Filter: (b = 0) + -> Hash + -> Seq Scan on prt2_p2 t2_2 + -> Nested Loop Anti Join + -> Seq Scan on prt1_p3 t1_3 + Filter: (b = 0) + -> Index Only Scan using iprt2_p3_b on prt2_p3 t2_3 + Index Cond: (b = t1_3.a) + -> Hash Anti Join + Hash Cond: (t1_4.a = t2_4.b) + -> Seq Scan on prt1_p4 t1_4 + Filter: (b = 0) + -> Hash + -> Seq Scan on prt2_p4 t2_4 +(32 rows) + +select t1.a, t1.b, t1.c from prt1 t1 where not exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c; + a | b | c +------+---+------- + -250 | 0 | -0250 + -200 | 0 | -0200 + -100 | 0 | -0100 + -50 | 0 | -0050 + 50 | 0 | 0050 + 100 | 0 | 0100 + 200 | 0 | 0200 + 250 | 0 | 0250 + 350 | 0 | 0350 + 400 | 0 | 0400 + 500 | 0 | 0500 + 550 | 0 | 0550 + 650 | 0 | 0650 + 700 | 0 | 0700 +(14 rows) + +-- 3-way join when not every pair of joining relation can use partition-wise +-- join +EXPLAIN (COSTS OFF) +SELECT t1.a, t2.a, t3.c FROM prt1 t1 RIGHT JOIN prt2 t2 ON (t1.a = t2.b) INNER JOIN prt1 t3 ON (t2.b = t3.a) WHERE t2.a = 0 ORDER BY t1.a, t2.a, t3.c; + QUERY PLAN +--------------------------------------------------------------------- + Sort + Sort Key: t1.a, t3.c + -> Append + -> Nested Loop Left Join + -> Nested Loop + -> Seq Scan on prt2_p0 t2 + Filter: (a = 0) + -> Index Scan using iprt1_p0_a on prt1_p0 t3 + Index Cond: (a = t2.b) + -> Index Only Scan using iprt1_p0_a on prt1_p0 t1 + Index Cond: (a = t2.b) + -> Hash Right Join + Hash Cond: (t1_1.a = t2_1.b) + -> Seq Scan on prt1_p1 t1_1 + -> Hash + -> Hash Join + Hash Cond: (t3_1.a = t2_1.b) + -> Seq Scan on prt1_p1 t3_1 + -> Hash + -> Seq Scan on prt2_p1 t2_1 + Filter: (a = 0) + -> Nested Loop Left Join + -> Nested Loop + -> Seq Scan on prt2_p2 t2_2 + Filter: (a = 0) + -> Index Scan using iprt1_p2_a on prt1_p2 t3_2 + Index Cond: (a = t2_2.b) + -> Index Only Scan using iprt1_p2_a on prt1_p2 t1_2 + Index Cond: (a = t2_2.b) + -> Nested Loop Left Join + -> Nested Loop + -> Seq Scan on prt2_p3 t2_3 + Filter: (a = 0) + -> Index Scan using iprt1_p3_a on prt1_p3 t3_3 + Index Cond: (a = t2_3.b) + -> Index Only Scan using iprt1_p3_a on prt1_p3 t1_3 + Index Cond: (a = t2_3.b) + -> Hash Right Join + Hash Cond: (t1_4.a = t2_4.b) + -> Seq Scan on prt1_p4 t1_4 + -> Hash + -> Hash Join + Hash Cond: (t3_4.a = t2_4.b) + -> Seq Scan on prt1_p4 t3_4 + -> Hash + -> Seq Scan on prt2_p4 t2_4 + Filter: (a = 0) +(47 rows) + +SELECT t1.a, t2.a, t3.c FROM prt1 t1 RIGHT JOIN prt2 t2 ON (t1.a = t2.b) INNER JOIN prt1 t3 ON (t2.b = t3.a) WHERE t2.a = 0 ORDER BY t1.a, t2.a, t3.c; + a | a | c +------+---+------- + -150 | 0 | -0150 + 0 | 0 | 0000 + 150 | 0 | 0150 + 300 | 0 | 0300 + 450 | 0 | 0450 + 600 | 0 | 0600 + 750 | 0 | 0750 +(7 rows) + +-- partition-wise join can not handle missing partition on the inner side +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b WHERE t2.a = 0 ORDER BY t2.b; + QUERY PLAN +-------------------------------------------------- + Sort + Sort Key: t2.b + -> Hash Right Join + Hash Cond: (t1.a = t2.b) + -> Append + -> Seq Scan on prt1_p0 t1 + -> Seq Scan on prt1_p1 t1_1 + -> Seq Scan on prt1_p2 t1_2 + -> Seq Scan on prt1_p3 t1_3 + -> Seq Scan on prt1_p4 t1_4 + -> Hash + -> Append + -> Seq Scan on prt2_p0 t2 + Filter: (a = 0) + -> Seq Scan on prt2_p1 t2_1 + Filter: (a = 0) + -> Seq Scan on prt2_p2 t2_2 + Filter: (a = 0) + -> Seq Scan on prt2_p3 t2_3 + Filter: (a = 0) + -> Seq Scan on prt2_p4 t2_4 + Filter: (a = 0) + -> Seq Scan on prt2_p5 t2_5 + Filter: (a = 0) +(24 rows) + +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 FULL JOIN prt2 t2 ON t1.a = t2.b WHERE coalesce(t1.b, 0) + coalesce(t2.a, 0) = 0 ORDER BY t1.a, t2.a; + QUERY PLAN +--------------------------------------------------------------- + Sort + Sort Key: t1.a, t2.a + -> Hash Full Join + Hash Cond: (t1.a = t2.b) + Filter: ((COALESCE(t1.b, 0) + COALESCE(t2.a, 0)) = 0) + -> Append + -> Seq Scan on prt1_p0 t1 + -> Seq Scan on prt1_p1 t1_1 + -> Seq Scan on prt1_p2 t1_2 + -> Seq Scan on prt1_p3 t1_3 + -> Seq Scan on prt1_p4 t1_4 + -> Hash + -> Append + -> Seq Scan on prt2_p0 t2 + -> Seq Scan on prt2_p1 t2_1 + -> Seq Scan on prt2_p2 t2_2 + -> Seq Scan on prt2_p3 t2_3 + -> Seq Scan on prt2_p4 t2_4 + -> Seq Scan on prt2_p5 t2_5 +(19 rows) + +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from prt2 t1 where not exists (select 1 from prt1 t2 WHERE t1.b = t2.a) and t1.a = 0 order by t1.a, t1.b, t1.c; + QUERY PLAN +-------------------------------------------------- + Sort + Sort Key: t1.b, t1.c + -> Hash Anti Join + Hash Cond: (t1.b = t2.a) + -> Append + -> Seq Scan on prt2_p0 t1 + Filter: (a = 0) + -> Seq Scan on prt2_p1 t1_1 + Filter: (a = 0) + -> Seq Scan on prt2_p2 t1_2 + Filter: (a = 0) + -> Seq Scan on prt2_p3 t1_3 + Filter: (a = 0) + -> Seq Scan on prt2_p4 t1_4 + Filter: (a = 0) + -> Seq Scan on prt2_p5 t1_5 + Filter: (a = 0) + -> Hash + -> Append + -> Seq Scan on prt1_p0 t2 + -> Seq Scan on prt1_p1 t2_1 + -> Seq Scan on prt1_p2 t2_2 + -> Seq Scan on prt1_p3 t2_3 + -> Seq Scan on prt1_p4 t2_4 +(24 rows) + +-- Partition-wise join can not handle the case when one partition from one side +-- matches with multiple partitions on the other side +DROP TABLE prt2_p4; +DROP TABLE prt2_p5; +CREATE TABLE prt2_p4 PARTITION OF prt2 FOR VALUES FROM (600) TO (700); +CREATE TABLE prt2_p5 PARTITION OF prt2 FOR VALUES FROM (700) TO (1000); +INSERT INTO prt2 SELECT i % 25, i, to_char(i, 'FM0000') FROM generate_series(600, 999, 3) i; +ANALYZE prt2; +-- inner join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 INNER JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a; + QUERY PLAN +-------------------------------------------------- + Sort + Sort Key: t1.a + -> Hash Join + Hash Cond: (t2.b = t1.a) + -> Append + -> Seq Scan on prt2_p0 t2 + -> Seq Scan on prt2_p1 t2_1 + -> Seq Scan on prt2_p2 t2_2 + -> Seq Scan on prt2_p3 t2_3 + -> Seq Scan on prt2_p4 t2_4 + -> Seq Scan on prt2_p5 t2_5 + -> Hash + -> Append + -> Seq Scan on prt1_p0 t1 + Filter: (b = 0) + -> Seq Scan on prt1_p1 t1_1 + Filter: (b = 0) + -> Seq Scan on prt1_p2 t1_2 + Filter: (b = 0) + -> Seq Scan on prt1_p3 t1_3 + Filter: (b = 0) + -> Seq Scan on prt1_p4 t1_4 + Filter: (b = 0) +(23 rows) + +-- left join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a; + QUERY PLAN +-------------------------------------------------- + Sort + Sort Key: t1.a + -> Hash Right Join + Hash Cond: (t2.b = t1.a) + -> Append + -> Seq Scan on prt2_p0 t2 + -> Seq Scan on prt2_p1 t2_1 + -> Seq Scan on prt2_p2 t2_2 + -> Seq Scan on prt2_p3 t2_3 + -> Seq Scan on prt2_p4 t2_4 + -> Seq Scan on prt2_p5 t2_5 + -> Hash + -> Append + -> Seq Scan on prt1_p0 t1 + Filter: (b = 0) + -> Seq Scan on prt1_p1 t1_1 + Filter: (b = 0) + -> Seq Scan on prt1_p2 t1_2 + Filter: (b = 0) + -> Seq Scan on prt1_p3 t1_3 + Filter: (b = 0) + -> Seq Scan on prt1_p4 t1_4 + Filter: (b = 0) +(23 rows) + +-- right join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b WHERE t2.a = 0 ORDER BY t2.a; + QUERY PLAN +-------------------------------------------- + Hash Right Join + Hash Cond: (t1.a = t2.b) + -> Append + -> Seq Scan on prt1_p0 t1 + -> Seq Scan on prt1_p1 t1_1 + -> Seq Scan on prt1_p2 t1_2 + -> Seq Scan on prt1_p3 t1_3 + -> Seq Scan on prt1_p4 t1_4 + -> Hash + -> Append + -> Seq Scan on prt2_p0 t2 + Filter: (a = 0) + -> Seq Scan on prt2_p1 t2_1 + Filter: (a = 0) + -> Seq Scan on prt2_p2 t2_2 + Filter: (a = 0) + -> Seq Scan on prt2_p3 t2_3 + Filter: (a = 0) + -> Seq Scan on prt2_p4 t2_4 + Filter: (a = 0) + -> Seq Scan on prt2_p5 t2_5 + Filter: (a = 0) +(22 rows) + +-- full join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 FULL JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b + t2.a = 0 ORDER BY t1.a, t2.a; + QUERY PLAN +-------------------------------------------------- + Sort + Sort Key: t1.a, t2.a + -> Hash Join + Hash Cond: (t1.a = t2.b) + Join Filter: ((t1.b + t2.a) = 0) + -> Append + -> Seq Scan on prt1_p0 t1 + -> Seq Scan on prt1_p1 t1_1 + -> Seq Scan on prt1_p2 t1_2 + -> Seq Scan on prt1_p3 t1_3 + -> Seq Scan on prt1_p4 t1_4 + -> Hash + -> Append + -> Seq Scan on prt2_p0 t2 + -> Seq Scan on prt2_p1 t2_1 + -> Seq Scan on prt2_p2 t2_2 + -> Seq Scan on prt2_p3 t2_3 + -> Seq Scan on prt2_p4 t2_4 + -> Seq Scan on prt2_p5 t2_5 +(19 rows) + +-- semi join +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from prt1 t1 where exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c; + QUERY PLAN +-------------------------------------------------- + Sort + Sort Key: t1.a, t1.c + -> Hash Semi Join + Hash Cond: (t1.a = t2.b) + -> Append + -> Seq Scan on prt1_p0 t1 + Filter: (b = 0) + -> Seq Scan on prt1_p1 t1_1 + Filter: (b = 0) + -> Seq Scan on prt1_p2 t1_2 + Filter: (b = 0) + -> Seq Scan on prt1_p3 t1_3 + Filter: (b = 0) + -> Seq Scan on prt1_p4 t1_4 + Filter: (b = 0) + -> Hash + -> Append + -> Seq Scan on prt2_p0 t2 + -> Seq Scan on prt2_p1 t2_1 + -> Seq Scan on prt2_p2 t2_2 + -> Seq Scan on prt2_p3 t2_3 + -> Seq Scan on prt2_p4 t2_4 + -> Seq Scan on prt2_p5 t2_5 +(23 rows) + +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from prt2 t1 where exists (select 1 from prt1 t2 WHERE t1.b = t2.a) and t1.a = 0 order by t1.a, t1.b, t1.c; + QUERY PLAN +-------------------------------------------------- + Sort + Sort Key: t1.b, t1.c + -> Hash Semi Join + Hash Cond: (t1.b = t2.a) + -> Append + -> Seq Scan on prt2_p0 t1 + Filter: (a = 0) + -> Seq Scan on prt2_p1 t1_1 + Filter: (a = 0) + -> Seq Scan on prt2_p2 t1_2 + Filter: (a = 0) + -> Seq Scan on prt2_p3 t1_3 + Filter: (a = 0) + -> Seq Scan on prt2_p4 t1_4 + Filter: (a = 0) + -> Seq Scan on prt2_p5 t1_5 + Filter: (a = 0) + -> Hash + -> Append + -> Seq Scan on prt1_p0 t2 + -> Seq Scan on prt1_p1 t2_1 + -> Seq Scan on prt1_p2 t2_2 + -> Seq Scan on prt1_p3 t2_3 + -> Seq Scan on prt1_p4 t2_4 +(24 rows) + +-- anti join +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from prt1 t1 where not exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c; + QUERY PLAN +-------------------------------------------------- + Sort + Sort Key: t1.a, t1.c + -> Hash Anti Join + Hash Cond: (t1.a = t2.b) + -> Append + -> Seq Scan on prt1_p0 t1 + Filter: (b = 0) + -> Seq Scan on prt1_p1 t1_1 + Filter: (b = 0) + -> Seq Scan on prt1_p2 t1_2 + Filter: (b = 0) + -> Seq Scan on prt1_p3 t1_3 + Filter: (b = 0) + -> Seq Scan on prt1_p4 t1_4 + Filter: (b = 0) + -> Hash + -> Append + -> Seq Scan on prt2_p0 t2 + -> Seq Scan on prt2_p1 t2_1 + -> Seq Scan on prt2_p2 t2_2 + -> Seq Scan on prt2_p3 t2_3 + -> Seq Scan on prt2_p4 t2_4 + -> Seq Scan on prt2_p5 t2_5 +(23 rows) + +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from prt2 t1 where not exists (select 1 from prt1 t2 WHERE t1.b = t2.a) and t1.a = 0 order by t1.a, t1.b, t1.c; + QUERY PLAN +-------------------------------------------------- + Sort + Sort Key: t1.b, t1.c + -> Hash Anti Join + Hash Cond: (t1.b = t2.a) + -> Append + -> Seq Scan on prt2_p0 t1 + Filter: (a = 0) + -> Seq Scan on prt2_p1 t1_1 + Filter: (a = 0) + -> Seq Scan on prt2_p2 t1_2 + Filter: (a = 0) + -> Seq Scan on prt2_p3 t1_3 + Filter: (a = 0) + -> Seq Scan on prt2_p4 t1_4 + Filter: (a = 0) + -> Seq Scan on prt2_p5 t1_5 + Filter: (a = 0) + -> Hash + -> Append + -> Seq Scan on prt1_p0 t2 + -> Seq Scan on prt1_p1 t2_1 + -> Seq Scan on prt1_p2 t2_2 + -> Seq Scan on prt1_p3 t2_3 + -> Seq Scan on prt1_p4 t2_4 +(24 rows) + +-- +-- partitioned by multiple columns +-- +CREATE TABLE prt1_m (a int, b int, c int) PARTITION BY RANGE(a, ((a + b)/2)); +CREATE TABLE prt1_m_p1 PARTITION OF prt1_m FOR VALUES FROM (0, 0) TO (250, 250); +CREATE TABLE prt1_m_p2 PARTITION OF prt1_m FOR VALUES FROM (250, 250) TO (500, 500); +CREATE TABLE prt1_m_p3 PARTITION OF prt1_m FOR VALUES FROM (500, 500) TO (600, 600); +INSERT INTO prt1_m SELECT i, i, i % 25 FROM generate_series(0, 599, 2) i; +ANALYZE prt1_m; +CREATE TABLE prt2_m (a int, b int, c int) PARTITION BY RANGE(((b + a)/2), b); +CREATE TABLE prt2_m_p1 PARTITION OF prt2_m FOR VALUES FROM (0, 0) TO (250, 250); +CREATE TABLE prt2_m_p2 PARTITION OF prt2_m FOR VALUES FROM (250, 250) TO (500, 500); +CREATE TABLE prt2_m_p3 PARTITION OF prt2_m FOR VALUES FROM (500, 500) TO (600, 600); +INSERT INTO prt2_m SELECT i, i, i % 25 FROM generate_series(0, 599, 3) i; +ANALYZE prt2_m; +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_m WHERE prt1_m.c = 0) t1 FULL JOIN (SELECT * FROM prt2_m WHERE prt2_m.c = 0) t2 ON (t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2) ORDER BY t1.a, t2.b; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------ + Sort + Sort Key: prt1_m_p1.a, prt2_m_p1.b + -> Append + -> Hash Full Join + Hash Cond: ((prt1_m_p1.a = ((prt2_m_p1.b + prt2_m_p1.a) / 2)) AND (((prt1_m_p1.a + prt1_m_p1.b) / 2) = prt2_m_p1.b)) + -> Seq Scan on prt1_m_p1 + Filter: (c = 0) + -> Hash + -> Seq Scan on prt2_m_p1 + Filter: (c = 0) + -> Hash Full Join + Hash Cond: ((prt1_m_p2.a = ((prt2_m_p2.b + prt2_m_p2.a) / 2)) AND (((prt1_m_p2.a + prt1_m_p2.b) / 2) = prt2_m_p2.b)) + -> Seq Scan on prt1_m_p2 + Filter: (c = 0) + -> Hash + -> Seq Scan on prt2_m_p2 + Filter: (c = 0) + -> Hash Full Join + Hash Cond: ((prt1_m_p3.a = ((prt2_m_p3.b + prt2_m_p3.a) / 2)) AND (((prt1_m_p3.a + prt1_m_p3.b) / 2) = prt2_m_p3.b)) + -> Seq Scan on prt1_m_p3 + Filter: (c = 0) + -> Hash + -> Seq Scan on prt2_m_p3 + Filter: (c = 0) +(24 rows) + +SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_m WHERE prt1_m.c = 0) t1 FULL JOIN (SELECT * FROM prt2_m WHERE prt2_m.c = 0) t2 ON (t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2) ORDER BY t1.a, t2.b; + a | c | b | c +-----+---+-----+--- + 0 | 0 | 0 | 0 + 50 | 0 | | + 100 | 0 | | + 150 | 0 | 150 | 0 + 200 | 0 | | + 250 | 0 | | + 300 | 0 | 300 | 0 + 350 | 0 | | + 400 | 0 | | + 450 | 0 | 450 | 0 + 500 | 0 | | + 550 | 0 | | + | | 75 | 0 + | | 225 | 0 + | | 375 | 0 + | | 525 | 0 +(16 rows) + +-- +-- tests for list partitioned tables. +-- +\set part_mod 17 +\set cond_mod 47 +\set num_rows 500 +CREATE TABLE plt1 (a int, b int, c varchar) PARTITION BY LIST(c); +CREATE TABLE plt1_p1 PARTITION OF plt1 FOR VALUES IN ('0001','0002','0003'); +CREATE TABLE plt1_p2 PARTITION OF plt1 FOR VALUES IN ('0004','0005','0006'); +CREATE TABLE plt1_p3 PARTITION OF plt1 FOR VALUES IN ('0008','0009'); +CREATE TABLE plt1_p4 PARTITION OF plt1 FOR VALUES IN ('0000','0010'); +INSERT INTO plt1 SELECT i, i % :cond_mod, to_char(i % :part_mod, 'FM0000') FROM generate_series(0, :num_rows) i WHERE i % :part_mod NOT IN (7, 11, 12, 13, 14, 15, 16); +ANALYSE plt1; +-- plt2 have missing starting 0001, additional 0007, missing ending 0010 +-- and additional 0011 and 0012 bounds +CREATE TABLE plt2 (a int, b int, c varchar) PARTITION BY LIST(c); +CREATE TABLE plt2_p1 PARTITION OF plt2 FOR VALUES IN ('0002','0003'); +CREATE TABLE plt2_p2 PARTITION OF plt2 FOR VALUES IN ('0004','0005','0006'); +CREATE TABLE plt2_p3 PARTITION OF plt2 FOR VALUES IN ('0007','0008','0009'); +CREATE TABLE plt2_p4 PARTITION OF plt2 FOR VALUES IN ('0000','0011','0012'); +INSERT INTO plt2 SELECT i, i % :cond_mod, to_char(i % :part_mod, 'FM0000') FROM generate_series(0, :num_rows) i WHERE i % :part_mod NOT IN (1, 10, 13, 14, 15, 16); +ANALYSE plt2; +-- Partition-wise-join is possible with some partition bounds overlap +-- with each other completely and some partialy for inner,left,right, +-- full, semi and anti joins +-- inner join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a; + QUERY PLAN +------------------------------------------------------------ + Sort + Sort Key: t1.a + -> Append + -> Hash Join + Hash Cond: ((t1.c)::text = (t2.c)::text) + Join Filter: ((t1.b + t2.b) = 0) + -> Seq Scan on plt1_p4 t1 + -> Hash + -> Seq Scan on plt2_p4 t2 + -> Hash Join + Hash Cond: ((t2_1.c)::text = (t1_1.c)::text) + Join Filter: ((t1_1.b + t2_1.b) = 0) + -> Seq Scan on plt2_p1 t2_1 + -> Hash + -> Seq Scan on plt1_p1 t1_1 + -> Hash Join + Hash Cond: ((t1_2.c)::text = (t2_2.c)::text) + Join Filter: ((t1_2.b + t2_2.b) = 0) + -> Seq Scan on plt1_p2 t1_2 + -> Hash + -> Seq Scan on plt2_p2 t2_2 + -> Hash Join + Hash Cond: ((t1_3.c)::text = (t2_3.c)::text) + Join Filter: ((t1_3.b + t2_3.b) = 0) + -> Seq Scan on plt1_p3 t1_3 + -> Hash + -> Seq Scan on plt2_p3 t2_3 +(27 rows) + +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a; + a | c | a | c +-----+------+-----+------ + 0 | 0000 | 0 | 0000 + 94 | 0009 | 94 | 0009 + 141 | 0005 | 141 | 0005 + 329 | 0006 | 329 | 0006 + 376 | 0002 | 376 | 0002 +(5 rows) + +-- left join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a; + QUERY PLAN +------------------------------------------------------------ + Sort + Sort Key: t1.a + -> Append + -> Hash Left Join + Hash Cond: ((t1.c)::text = (t2.c)::text) + Filter: ((t1.b + COALESCE(t2.b, 0)) = 0) + -> Seq Scan on plt1_p4 t1 + -> Hash + -> Seq Scan on plt2_p4 t2 + -> Hash Right Join + Hash Cond: ((t2_1.c)::text = (t1_1.c)::text) + Filter: ((t1_1.b + COALESCE(t2_1.b, 0)) = 0) + -> Seq Scan on plt2_p1 t2_1 + -> Hash + -> Seq Scan on plt1_p1 t1_1 + -> Hash Left Join + Hash Cond: ((t1_2.c)::text = (t2_2.c)::text) + Filter: ((t1_2.b + COALESCE(t2_2.b, 0)) = 0) + -> Seq Scan on plt1_p2 t1_2 + -> Hash + -> Seq Scan on plt2_p2 t2_2 + -> Hash Left Join + Hash Cond: ((t1_3.c)::text = (t2_3.c)::text) + Filter: ((t1_3.b + COALESCE(t2_3.b, 0)) = 0) + -> Seq Scan on plt1_p3 t1_3 + -> Hash + -> Seq Scan on plt2_p3 t2_3 +(27 rows) + +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a; + a | c | a | c +-----+------+-----+------ + 0 | 0000 | 0 | 0000 + 94 | 0009 | 94 | 0009 + 141 | 0005 | 141 | 0005 + 188 | 0001 | | + 282 | 0010 | | + 329 | 0006 | 329 | 0006 + 376 | 0002 | 376 | 0002 +(7 rows) + +-- right join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t2.a; + QUERY PLAN +------------------------------------------------------------ + Sort + Sort Key: t2.a + -> Append + -> Hash Right Join + Hash Cond: ((t1.c)::text = (t2.c)::text) + Filter: ((COALESCE(t1.b, 0) + t2.b) = 0) + -> Seq Scan on plt1_p4 t1 + -> Hash + -> Seq Scan on plt2_p4 t2 + -> Hash Left Join + Hash Cond: ((t2_1.c)::text = (t1_1.c)::text) + Filter: ((COALESCE(t1_1.b, 0) + t2_1.b) = 0) + -> Seq Scan on plt2_p1 t2_1 + -> Hash + -> Seq Scan on plt1_p1 t1_1 + -> Hash Left Join + Hash Cond: ((t2_2.c)::text = (t1_2.c)::text) + Filter: ((COALESCE(t1_2.b, 0) + t2_2.b) = 0) + -> Seq Scan on plt2_p2 t2_2 + -> Hash + -> Seq Scan on plt1_p2 t1_2 + -> Hash Right Join + Hash Cond: ((t1_3.c)::text = (t2_3.c)::text) + Filter: ((COALESCE(t1_3.b, 0) + t2_3.b) = 0) + -> Seq Scan on plt1_p3 t1_3 + -> Hash + -> Seq Scan on plt2_p3 t2_3 +(27 rows) + +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t1.a; + a | c | a | c +-----+------+-----+------ + 0 | 0000 | 0 | 0000 + 94 | 0009 | 94 | 0009 + 141 | 0005 | 141 | 0005 + 329 | 0006 | 329 | 0006 + 376 | 0002 | 376 | 0002 + | | 470 | 0011 +(6 rows) + +-- full join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a; + QUERY PLAN +------------------------------------------------------------------------- + Sort + Sort Key: t1.a, t2.a + -> Append + -> Hash Full Join + Hash Cond: ((t1.c)::text = (t2.c)::text) + Filter: ((COALESCE(t1.b, 0) + COALESCE(t2.b, 0)) = 0) + -> Seq Scan on plt1_p4 t1 + -> Hash + -> Seq Scan on plt2_p4 t2 + -> Hash Full Join + Hash Cond: ((t2_1.c)::text = (t1_1.c)::text) + Filter: ((COALESCE(t1_1.b, 0) + COALESCE(t2_1.b, 0)) = 0) + -> Seq Scan on plt2_p1 t2_1 + -> Hash + -> Seq Scan on plt1_p1 t1_1 + -> Hash Full Join + Hash Cond: ((t1_2.c)::text = (t2_2.c)::text) + Filter: ((COALESCE(t1_2.b, 0) + COALESCE(t2_2.b, 0)) = 0) + -> Seq Scan on plt1_p2 t1_2 + -> Hash + -> Seq Scan on plt2_p2 t2_2 + -> Hash Full Join + Hash Cond: ((t1_3.c)::text = (t2_3.c)::text) + Filter: ((COALESCE(t1_3.b, 0) + COALESCE(t2_3.b, 0)) = 0) + -> Seq Scan on plt1_p3 t1_3 + -> Hash + -> Seq Scan on plt2_p3 t2_3 +(27 rows) + +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a; + a | c | a | c +-----+------+-----+------ + 0 | 0000 | 0 | 0000 + 94 | 0009 | 94 | 0009 + 141 | 0005 | 141 | 0005 + 188 | 0001 | | + 282 | 0010 | | + 329 | 0006 | 329 | 0006 + 376 | 0002 | 376 | 0002 + | | 470 | 0011 +(8 rows) + +-- semi join +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + QUERY PLAN +-------------------------------------------------------------- + Sort + Sort Key: t1.a, t1.c + -> Append + -> Hash Join + Hash Cond: ((t2.c)::text = (t1.c)::text) + -> HashAggregate + Group Key: (t2.c)::text + -> Seq Scan on plt2_p4 t2 + -> Hash + -> Seq Scan on plt1_p4 t1 + Filter: (b = 0) + -> Nested Loop + Join Filter: ((t1_1.c)::text = (t2_1.c)::text) + -> HashAggregate + Group Key: (t2_1.c)::text + -> Seq Scan on plt2_p1 t2_1 + -> Materialize + -> Seq Scan on plt1_p1 t1_1 + Filter: (b = 0) + -> Nested Loop Semi Join + Join Filter: ((t1_2.c)::text = (t2_2.c)::text) + -> Seq Scan on plt1_p2 t1_2 + Filter: (b = 0) + -> Seq Scan on plt2_p2 t2_2 + -> Nested Loop Semi Join + Join Filter: ((t1_3.c)::text = (t2_3.c)::text) + -> Seq Scan on plt1_p3 t1_3 + Filter: (b = 0) + -> Seq Scan on plt2_p3 t2_3 +(29 rows) + +select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + a | b | c +-----+---+------ + 0 | 0 | 0000 + 94 | 0 | 0009 + 141 | 0 | 0005 + 329 | 0 | 0006 + 376 | 0 | 0002 +(5 rows) + +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + QUERY PLAN +-------------------------------------------------------------- + Sort + Sort Key: t1.a, t1.c + -> Append + -> Nested Loop + Join Filter: ((t1.c)::text = (t2.c)::text) + -> HashAggregate + Group Key: (t2.c)::text + -> Seq Scan on plt1_p4 t2 + -> Materialize + -> Seq Scan on plt2_p4 t1 + Filter: (b = 0) + -> Nested Loop Semi Join + Join Filter: ((t1_1.c)::text = (t2_1.c)::text) + -> Seq Scan on plt2_p1 t1_1 + Filter: (b = 0) + -> Seq Scan on plt1_p1 t2_1 + -> Nested Loop Semi Join + Join Filter: ((t1_2.c)::text = (t2_2.c)::text) + -> Seq Scan on plt2_p2 t1_2 + Filter: (b = 0) + -> Seq Scan on plt1_p2 t2_2 + -> Nested Loop Semi Join + Join Filter: ((t1_3.c)::text = (t2_3.c)::text) + -> Seq Scan on plt2_p3 t1_3 + Filter: (b = 0) + -> Seq Scan on plt1_p3 t2_3 +(26 rows) + +select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + a | b | c +-----+---+------ + 0 | 0 | 0000 + 94 | 0 | 0009 + 141 | 0 | 0005 + 329 | 0 | 0006 + 376 | 0 | 0002 +(5 rows) + +-- anti join +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + QUERY PLAN +-------------------------------------------------------------- + Sort + Sort Key: t1.a, t1.c + -> Append + -> Nested Loop Anti Join + Join Filter: ((t1.c)::text = (t2.c)::text) + -> Seq Scan on plt1_p4 t1 + Filter: (b = 0) + -> Seq Scan on plt2_p4 t2 + -> Hash Anti Join + Hash Cond: ((t1_1.c)::text = (t2_1.c)::text) + -> Seq Scan on plt1_p1 t1_1 + Filter: (b = 0) + -> Hash + -> Seq Scan on plt2_p1 t2_1 + -> Nested Loop Anti Join + Join Filter: ((t1_2.c)::text = (t2_2.c)::text) + -> Seq Scan on plt1_p2 t1_2 + Filter: (b = 0) + -> Seq Scan on plt2_p2 t2_2 + -> Nested Loop Anti Join + Join Filter: ((t1_3.c)::text = (t2_3.c)::text) + -> Seq Scan on plt1_p3 t1_3 + Filter: (b = 0) + -> Seq Scan on plt2_p3 t2_3 +(24 rows) + +select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + a | b | c +-----+---+------ + 188 | 0 | 0001 + 282 | 0 | 0010 +(2 rows) + +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + QUERY PLAN +-------------------------------------------------------------- + Sort + Sort Key: t1.a, t1.c + -> Append + -> Hash Anti Join + Hash Cond: ((t1.c)::text = (t2.c)::text) + -> Seq Scan on plt2_p4 t1 + Filter: (b = 0) + -> Hash + -> Seq Scan on plt1_p4 t2 + -> Nested Loop Anti Join + Join Filter: ((t1_1.c)::text = (t2_1.c)::text) + -> Seq Scan on plt2_p1 t1_1 + Filter: (b = 0) + -> Seq Scan on plt1_p1 t2_1 + -> Nested Loop Anti Join + Join Filter: ((t1_2.c)::text = (t2_2.c)::text) + -> Seq Scan on plt2_p2 t1_2 + Filter: (b = 0) + -> Seq Scan on plt1_p2 t2_2 + -> Nested Loop Anti Join + Join Filter: ((t1_3.c)::text = (t2_3.c)::text) + -> Seq Scan on plt2_p3 t1_3 + Filter: (b = 0) + -> Seq Scan on plt1_p3 t2_3 +(24 rows) + +select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + a | b | c +-----+---+------ + 470 | 0 | 0011 +(1 row) + +-- +-- list partitioned by expression +-- +CREATE TABLE plt1_e (a int, b int, c text) PARTITION BY LIST(ltrim(c, 'A')); +CREATE TABLE plt1_e_p1 PARTITION OF plt1_e FOR VALUES IN ('0002', '0003'); +CREATE TABLE plt1_e_p2 PARTITION OF plt1_e FOR VALUES IN ('0004', '0005', '0006'); +CREATE TABLE plt1_e_p3 PARTITION OF plt1_e FOR VALUES IN ('0008', '0009'); +CREATE TABLE plt1_e_p4 PARTITION OF plt1_e FOR VALUES IN ('0000'); +INSERT INTO plt1_e SELECT i, i % :cond_mod, to_char(i % :part_mod, 'FM0000') FROM generate_series(0, :num_rows) i WHERE i % :part_mod NOT IN (1, 7, 10, 11, 12, 13, 14, 15, 16); +ANALYZE plt1_e; +-- test partition matching with N-way join +EXPLAIN (COSTS OFF) +SELECT avg(t1.a), avg(t2.b), avg(t3.a + t3.b), t1.c, t2.c, t3.c FROM plt1 t1, plt2 t2, plt1_e t3 WHERE t1.b = t2.b AND t1.c = t2.c AND ltrim(t3.c, 'A') = t1.c GROUP BY t1.c, t2.c, t3.c ORDER BY t1.c, t2.c, t3.c; + QUERY PLAN +------------------------------------------------------------------------------------------------ + GroupAggregate + Group Key: t1.c, t2.c, t3.c + -> Sort + Sort Key: t1.c, t3.c + -> Append + -> Hash Join + Hash Cond: ((t1.c)::text = ltrim(t3.c, 'A'::text)) + -> Hash Join + Hash Cond: ((t2.b = t1.b) AND ((t2.c)::text = (t1.c)::text)) + -> Seq Scan on plt2_p4 t2 + -> Hash + -> Seq Scan on plt1_p4 t1 + -> Hash + -> Seq Scan on plt1_e_p4 t3 + -> Hash Join + Hash Cond: ((t1_1.c)::text = ltrim(t3_1.c, 'A'::text)) + -> Hash Join + Hash Cond: ((t1_1.b = t2_1.b) AND ((t1_1.c)::text = (t2_1.c)::text)) + -> Seq Scan on plt1_p1 t1_1 + -> Hash + -> Seq Scan on plt2_p1 t2_1 + -> Hash + -> Seq Scan on plt1_e_p1 t3_1 + -> Hash Join + Hash Cond: ((t1_2.c)::text = ltrim(t3_2.c, 'A'::text)) + -> Hash Join + Hash Cond: ((t1_2.b = t2_2.b) AND ((t1_2.c)::text = (t2_2.c)::text)) + -> Seq Scan on plt1_p2 t1_2 + -> Hash + -> Seq Scan on plt2_p2 t2_2 + -> Hash + -> Seq Scan on plt1_e_p2 t3_2 + -> Hash Join + Hash Cond: ((t1_3.c)::text = ltrim(t3_3.c, 'A'::text)) + -> Hash Join + Hash Cond: ((t2_3.b = t1_3.b) AND ((t2_3.c)::text = (t1_3.c)::text)) + -> Seq Scan on plt2_p3 t2_3 + -> Hash + -> Seq Scan on plt1_p3 t1_3 + -> Hash + -> Seq Scan on plt1_e_p3 t3_3 +(41 rows) + +SELECT avg(t1.a), avg(t2.b), avg(t3.a + t3.b), t1.c, t2.c, t3.c FROM plt1 t1, plt2 t2, plt1_e t3 WHERE t1.b = t2.b AND t1.c = t2.c AND ltrim(t3.c, 'A') = t1.c GROUP BY t1.c, t2.c, t3.c ORDER BY t1.c, t2.c, t3.c; + avg | avg | avg | c | c | c +----------------------+---------------------+----------------------+------+------+------ + 246.5000000000000000 | 22.4666666666666667 | 268.9666666666666667 | 0000 | 0000 | 0000 + 248.5000000000000000 | 21.3333333333333333 | 269.8333333333333333 | 0002 | 0002 | 0002 + 249.5000000000000000 | 22.3333333333333333 | 271.8333333333333333 | 0003 | 0003 | 0003 + 250.5000000000000000 | 23.3333333333333333 | 273.8333333333333333 | 0004 | 0004 | 0004 + 251.5000000000000000 | 22.7666666666666667 | 274.2666666666666667 | 0005 | 0005 | 0005 + 252.5000000000000000 | 22.2000000000000000 | 274.7000000000000000 | 0006 | 0006 | 0006 + 246.0000000000000000 | 23.9655172413793103 | 269.9655172413793103 | 0008 | 0008 | 0008 + 247.0000000000000000 | 23.3448275862068966 | 270.3448275862068966 | 0009 | 0009 | 0009 +(8 rows) + +-- Add an extra partition to plt2 , Partition-wise join is possible with +-- partitions on inner side are allowed +CREATE TABLE plt2_p5 PARTITION OF plt2 FOR VALUES IN ('0013','0014'); +INSERT INTO plt2 SELECT i, i % :cond_mod, to_char(i % :part_mod, 'FM0000') FROM generate_series(0, :num_rows) i WHERE i % :part_mod IN (13, 14); +ANALYZE plt2; +-- inner join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a; + QUERY PLAN +------------------------------------------------------------ + Sort + Sort Key: t1.a + -> Append + -> Hash Join + Hash Cond: ((t1.c)::text = (t2.c)::text) + Join Filter: ((t1.b + t2.b) = 0) + -> Seq Scan on plt1_p4 t1 + -> Hash + -> Seq Scan on plt2_p4 t2 + -> Hash Join + Hash Cond: ((t2_1.c)::text = (t1_1.c)::text) + Join Filter: ((t1_1.b + t2_1.b) = 0) + -> Seq Scan on plt2_p1 t2_1 + -> Hash + -> Seq Scan on plt1_p1 t1_1 + -> Hash Join + Hash Cond: ((t1_2.c)::text = (t2_2.c)::text) + Join Filter: ((t1_2.b + t2_2.b) = 0) + -> Seq Scan on plt1_p2 t1_2 + -> Hash + -> Seq Scan on plt2_p2 t2_2 + -> Hash Join + Hash Cond: ((t1_3.c)::text = (t2_3.c)::text) + Join Filter: ((t1_3.b + t2_3.b) = 0) + -> Seq Scan on plt1_p3 t1_3 + -> Hash + -> Seq Scan on plt2_p3 t2_3 +(27 rows) + +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a; + a | c | a | c +-----+------+-----+------ + 0 | 0000 | 0 | 0000 + 94 | 0009 | 94 | 0009 + 141 | 0005 | 141 | 0005 + 329 | 0006 | 329 | 0006 + 376 | 0002 | 376 | 0002 +(5 rows) + +-- left join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a; + QUERY PLAN +------------------------------------------------------------ + Sort + Sort Key: t1.a + -> Append + -> Hash Left Join + Hash Cond: ((t1.c)::text = (t2.c)::text) + Filter: ((t1.b + COALESCE(t2.b, 0)) = 0) + -> Seq Scan on plt1_p4 t1 + -> Hash + -> Seq Scan on plt2_p4 t2 + -> Hash Right Join + Hash Cond: ((t2_1.c)::text = (t1_1.c)::text) + Filter: ((t1_1.b + COALESCE(t2_1.b, 0)) = 0) + -> Seq Scan on plt2_p1 t2_1 + -> Hash + -> Seq Scan on plt1_p1 t1_1 + -> Hash Left Join + Hash Cond: ((t1_2.c)::text = (t2_2.c)::text) + Filter: ((t1_2.b + COALESCE(t2_2.b, 0)) = 0) + -> Seq Scan on plt1_p2 t1_2 + -> Hash + -> Seq Scan on plt2_p2 t2_2 + -> Hash Left Join + Hash Cond: ((t1_3.c)::text = (t2_3.c)::text) + Filter: ((t1_3.b + COALESCE(t2_3.b, 0)) = 0) + -> Seq Scan on plt1_p3 t1_3 + -> Hash + -> Seq Scan on plt2_p3 t2_3 +(27 rows) + +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a; + a | c | a | c +-----+------+-----+------ + 0 | 0000 | 0 | 0000 + 94 | 0009 | 94 | 0009 + 141 | 0005 | 141 | 0005 + 188 | 0001 | | + 282 | 0010 | | + 329 | 0006 | 329 | 0006 + 376 | 0002 | 376 | 0002 +(7 rows) + +-- right join, partition-wise join can not handle extra partition on the outer +-- side +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t2.a; + QUERY PLAN +-------------------------------------------------- + Sort + Sort Key: t2.a + -> Hash Right Join + Hash Cond: ((t1.c)::text = (t2.c)::text) + Filter: ((COALESCE(t1.b, 0) + t2.b) = 0) + -> Append + -> Seq Scan on plt1_p4 t1 + -> Seq Scan on plt1_p1 t1_1 + -> Seq Scan on plt1_p2 t1_2 + -> Seq Scan on plt1_p3 t1_3 + -> Hash + -> Append + -> Seq Scan on plt2_p4 t2 + -> Seq Scan on plt2_p1 t2_1 + -> Seq Scan on plt2_p2 t2_2 + -> Seq Scan on plt2_p3 t2_3 + -> Seq Scan on plt2_p5 t2_4 +(17 rows) + +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t1.a; + a | c | a | c +-----+------+-----+------ + 0 | 0000 | 0 | 0000 + 94 | 0009 | 94 | 0009 + 141 | 0005 | 141 | 0005 + 329 | 0006 | 329 | 0006 + 376 | 0002 | 376 | 0002 + | | 47 | 0013 + | | 470 | 0011 + | | 235 | 0014 +(8 rows) + +-- full join, partition-wise join can not handle extra partition on the outer +-- side +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a; + QUERY PLAN +--------------------------------------------------------------- + Sort + Sort Key: t1.a, t2.a + -> Hash Full Join + Hash Cond: ((t1.c)::text = (t2.c)::text) + Filter: ((COALESCE(t1.b, 0) + COALESCE(t2.b, 0)) = 0) + -> Append + -> Seq Scan on plt1_p4 t1 + -> Seq Scan on plt1_p1 t1_1 + -> Seq Scan on plt1_p2 t1_2 + -> Seq Scan on plt1_p3 t1_3 + -> Hash + -> Append + -> Seq Scan on plt2_p4 t2 + -> Seq Scan on plt2_p1 t2_1 + -> Seq Scan on plt2_p2 t2_2 + -> Seq Scan on plt2_p3 t2_3 + -> Seq Scan on plt2_p5 t2_4 +(17 rows) + +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a; + a | c | a | c +-----+------+-----+------ + 0 | 0000 | 0 | 0000 + 94 | 0009 | 94 | 0009 + 141 | 0005 | 141 | 0005 + 188 | 0001 | | + 282 | 0010 | | + 329 | 0006 | 329 | 0006 + 376 | 0002 | 376 | 0002 + | | 47 | 0013 + | | 235 | 0014 + | | 470 | 0011 +(10 rows) + +-- semi join +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + QUERY PLAN +-------------------------------------------------------------- + Sort + Sort Key: t1.a, t1.c + -> Append + -> Hash Join + Hash Cond: ((t2.c)::text = (t1.c)::text) + -> HashAggregate + Group Key: (t2.c)::text + -> Seq Scan on plt2_p4 t2 + -> Hash + -> Seq Scan on plt1_p4 t1 + Filter: (b = 0) + -> Nested Loop + Join Filter: ((t1_1.c)::text = (t2_1.c)::text) + -> HashAggregate + Group Key: (t2_1.c)::text + -> Seq Scan on plt2_p1 t2_1 + -> Materialize + -> Seq Scan on plt1_p1 t1_1 + Filter: (b = 0) + -> Nested Loop Semi Join + Join Filter: ((t1_2.c)::text = (t2_2.c)::text) + -> Seq Scan on plt1_p2 t1_2 + Filter: (b = 0) + -> Seq Scan on plt2_p2 t2_2 + -> Nested Loop Semi Join + Join Filter: ((t1_3.c)::text = (t2_3.c)::text) + -> Seq Scan on plt1_p3 t1_3 + Filter: (b = 0) + -> Seq Scan on plt2_p3 t2_3 +(29 rows) + +select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + a | b | c +-----+---+------ + 0 | 0 | 0000 + 94 | 0 | 0009 + 141 | 0 | 0005 + 329 | 0 | 0006 + 376 | 0 | 0002 +(5 rows) + +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + QUERY PLAN +-------------------------------------------------------------- + Sort + Sort Key: t1.a, t1.c + -> Append + -> Nested Loop + Join Filter: ((t1.c)::text = (t2.c)::text) + -> HashAggregate + Group Key: (t2.c)::text + -> Seq Scan on plt1_p4 t2 + -> Materialize + -> Seq Scan on plt2_p4 t1 + Filter: (b = 0) + -> Nested Loop Semi Join + Join Filter: ((t1_1.c)::text = (t2_1.c)::text) + -> Seq Scan on plt2_p1 t1_1 + Filter: (b = 0) + -> Seq Scan on plt1_p1 t2_1 + -> Nested Loop Semi Join + Join Filter: ((t1_2.c)::text = (t2_2.c)::text) + -> Seq Scan on plt2_p2 t1_2 + Filter: (b = 0) + -> Seq Scan on plt1_p2 t2_2 + -> Nested Loop Semi Join + Join Filter: ((t1_3.c)::text = (t2_3.c)::text) + -> Seq Scan on plt2_p3 t1_3 + Filter: (b = 0) + -> Seq Scan on plt1_p3 t2_3 +(26 rows) + +select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + a | b | c +-----+---+------ + 0 | 0 | 0000 + 94 | 0 | 0009 + 141 | 0 | 0005 + 329 | 0 | 0006 + 376 | 0 | 0002 +(5 rows) + +-- anti join +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + QUERY PLAN +-------------------------------------------------------------- + Sort + Sort Key: t1.a, t1.c + -> Append + -> Nested Loop Anti Join + Join Filter: ((t1.c)::text = (t2.c)::text) + -> Seq Scan on plt1_p4 t1 + Filter: (b = 0) + -> Seq Scan on plt2_p4 t2 + -> Hash Anti Join + Hash Cond: ((t1_1.c)::text = (t2_1.c)::text) + -> Seq Scan on plt1_p1 t1_1 + Filter: (b = 0) + -> Hash + -> Seq Scan on plt2_p1 t2_1 + -> Nested Loop Anti Join + Join Filter: ((t1_2.c)::text = (t2_2.c)::text) + -> Seq Scan on plt1_p2 t1_2 + Filter: (b = 0) + -> Seq Scan on plt2_p2 t2_2 + -> Nested Loop Anti Join + Join Filter: ((t1_3.c)::text = (t2_3.c)::text) + -> Seq Scan on plt1_p3 t1_3 + Filter: (b = 0) + -> Seq Scan on plt2_p3 t2_3 +(24 rows) + +select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + a | b | c +-----+---+------ + 188 | 0 | 0001 + 282 | 0 | 0010 +(2 rows) + +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + QUERY PLAN +-------------------------------------------------- + Sort + Sort Key: t1.a, t1.c + -> Hash Anti Join + Hash Cond: ((t1.c)::text = (t2.c)::text) + -> Append + -> Seq Scan on plt2_p4 t1 + Filter: (b = 0) + -> Seq Scan on plt2_p1 t1_1 + Filter: (b = 0) + -> Seq Scan on plt2_p2 t1_2 + Filter: (b = 0) + -> Seq Scan on plt2_p3 t1_3 + Filter: (b = 0) + -> Seq Scan on plt2_p5 t1_4 + Filter: (b = 0) + -> Hash + -> Append + -> Seq Scan on plt1_p4 t2 + -> Seq Scan on plt1_p1 t2_1 + -> Seq Scan on plt1_p2 t2_2 + -> Seq Scan on plt1_p3 t2_3 +(21 rows) + +select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + a | b | c +-----+---+------ + 47 | 0 | 0013 + 235 | 0 | 0014 + 470 | 0 | 0011 +(3 rows) + +-- Partition-wise join can not handle the case when one partition from one side +-- matches with multiple partitions on the other side +DROP TABLE plt2_p5; +CREATE TABLE plt2_p5 PARTITION OF plt2 FOR VALUES IN ('0001','0013','0014'); +INSERT INTO plt2 SELECT i, i % :cond_mod, to_char(i % :part_mod, 'FM0000') FROM generate_series(0, :num_rows) i WHERE i % :part_mod IN (1, 13, 14); +ANALYZE plt2; +-- inner join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a; + QUERY PLAN +-------------------------------------------------- + Sort + Sort Key: t1.a + -> Hash Join + Hash Cond: ((t1.c)::text = (t2.c)::text) + Join Filter: ((t1.b + t2.b) = 0) + -> Append + -> Seq Scan on plt1_p4 t1 + -> Seq Scan on plt1_p1 t1_1 + -> Seq Scan on plt1_p2 t1_2 + -> Seq Scan on plt1_p3 t1_3 + -> Hash + -> Append + -> Seq Scan on plt2_p4 t2 + -> Seq Scan on plt2_p5 t2_1 + -> Seq Scan on plt2_p1 t2_2 + -> Seq Scan on plt2_p2 t2_3 + -> Seq Scan on plt2_p3 t2_4 +(17 rows) + +-- left join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a; + QUERY PLAN +-------------------------------------------------- + Sort + Sort Key: t1.a + -> Hash Left Join + Hash Cond: ((t1.c)::text = (t2.c)::text) + Filter: ((t1.b + COALESCE(t2.b, 0)) = 0) + -> Append + -> Seq Scan on plt1_p4 t1 + -> Seq Scan on plt1_p1 t1_1 + -> Seq Scan on plt1_p2 t1_2 + -> Seq Scan on plt1_p3 t1_3 + -> Hash + -> Append + -> Seq Scan on plt2_p4 t2 + -> Seq Scan on plt2_p5 t2_1 + -> Seq Scan on plt2_p1 t2_2 + -> Seq Scan on plt2_p2 t2_3 + -> Seq Scan on plt2_p3 t2_4 +(17 rows) + +-- right join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t2.a; + QUERY PLAN +-------------------------------------------------- + Sort + Sort Key: t2.a + -> Hash Right Join + Hash Cond: ((t1.c)::text = (t2.c)::text) + Filter: ((COALESCE(t1.b, 0) + t2.b) = 0) + -> Append + -> Seq Scan on plt1_p4 t1 + -> Seq Scan on plt1_p1 t1_1 + -> Seq Scan on plt1_p2 t1_2 + -> Seq Scan on plt1_p3 t1_3 + -> Hash + -> Append + -> Seq Scan on plt2_p4 t2 + -> Seq Scan on plt2_p5 t2_1 + -> Seq Scan on plt2_p1 t2_2 + -> Seq Scan on plt2_p2 t2_3 + -> Seq Scan on plt2_p3 t2_4 +(17 rows) + +-- full join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a; + QUERY PLAN +--------------------------------------------------------------- + Sort + Sort Key: t1.a, t2.a + -> Hash Full Join + Hash Cond: ((t1.c)::text = (t2.c)::text) + Filter: ((COALESCE(t1.b, 0) + COALESCE(t2.b, 0)) = 0) + -> Append + -> Seq Scan on plt1_p4 t1 + -> Seq Scan on plt1_p1 t1_1 + -> Seq Scan on plt1_p2 t1_2 + -> Seq Scan on plt1_p3 t1_3 + -> Hash + -> Append + -> Seq Scan on plt2_p4 t2 + -> Seq Scan on plt2_p5 t2_1 + -> Seq Scan on plt2_p1 t2_2 + -> Seq Scan on plt2_p2 t2_3 + -> Seq Scan on plt2_p3 t2_4 +(17 rows) + +-- semi join +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + QUERY PLAN +-------------------------------------------------------------- + Sort + Sort Key: t1.a, t1.c + -> Hash Join + Hash Cond: ((t1.c)::text = (t2.c)::text) + -> Append + -> Seq Scan on plt1_p4 t1 + Filter: (b = 0) + -> Seq Scan on plt1_p1 t1_1 + Filter: (b = 0) + -> Seq Scan on plt1_p2 t1_2 + Filter: (b = 0) + -> Seq Scan on plt1_p3 t1_3 + Filter: (b = 0) + -> Hash + -> HashAggregate + Group Key: (t2.c)::text + -> Result + -> Append + -> Seq Scan on plt2_p4 t2 + -> Seq Scan on plt2_p5 t2_1 + -> Seq Scan on plt2_p1 t2_2 + -> Seq Scan on plt2_p2 t2_3 + -> Seq Scan on plt2_p3 t2_4 +(23 rows) + +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + QUERY PLAN +-------------------------------------------------------------- + Sort + Sort Key: t1.a, t1.c + -> Hash Join + Hash Cond: ((t1.c)::text = (t2.c)::text) + -> Append + -> Seq Scan on plt2_p4 t1 + Filter: (b = 0) + -> Seq Scan on plt2_p5 t1_1 + Filter: (b = 0) + -> Seq Scan on plt2_p1 t1_2 + Filter: (b = 0) + -> Seq Scan on plt2_p2 t1_3 + Filter: (b = 0) + -> Seq Scan on plt2_p3 t1_4 + Filter: (b = 0) + -> Hash + -> HashAggregate + Group Key: (t2.c)::text + -> Result + -> Append + -> Seq Scan on plt1_p4 t2 + -> Seq Scan on plt1_p1 t2_1 + -> Seq Scan on plt1_p2 t2_2 + -> Seq Scan on plt1_p3 t2_3 +(24 rows) + +-- anti join +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + QUERY PLAN +---------------------------------------------------- + Sort + Sort Key: t1.a, t1.c + -> Nested Loop Anti Join + Join Filter: ((t1.c)::text = (t2.c)::text) + -> Append + -> Seq Scan on plt1_p4 t1 + Filter: (b = 0) + -> Seq Scan on plt1_p1 t1_1 + Filter: (b = 0) + -> Seq Scan on plt1_p2 t1_2 + Filter: (b = 0) + -> Seq Scan on plt1_p3 t1_3 + Filter: (b = 0) + -> Materialize + -> Append + -> Seq Scan on plt2_p4 t2 + -> Seq Scan on plt2_p5 t2_1 + -> Seq Scan on plt2_p1 t2_2 + -> Seq Scan on plt2_p2 t2_3 + -> Seq Scan on plt2_p3 t2_4 +(20 rows) + +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + QUERY PLAN +-------------------------------------------------- + Sort + Sort Key: t1.a, t1.c + -> Hash Anti Join + Hash Cond: ((t1.c)::text = (t2.c)::text) + -> Append + -> Seq Scan on plt2_p4 t1 + Filter: (b = 0) + -> Seq Scan on plt2_p5 t1_1 + Filter: (b = 0) + -> Seq Scan on plt2_p1 t1_2 + Filter: (b = 0) + -> Seq Scan on plt2_p2 t1_3 + Filter: (b = 0) + -> Seq Scan on plt2_p3 t1_4 + Filter: (b = 0) + -> Hash + -> Append + -> Seq Scan on plt1_p4 t2 + -> Seq Scan on plt1_p1 t2_1 + -> Seq Scan on plt1_p2 t2_2 + -> Seq Scan on plt1_p3 t2_3 +(21 rows) + +-- partition have a NULL on one side, Partition-wise join is possible with +-- NULL when NULL comparision is not strict i.e. NULL=NULL allowed +-- in this case NULL will be treated as addition partition bounds. +DROP TABLE plt2_p5; +DROP TABLE plt2_p4; +CREATE TABLE plt2_p4 PARTITION OF plt2 FOR VALUES IN ('0000',NULL,'0012'); +INSERT INTO plt2 SELECT i, i % :cond_mod, case when i % :part_mod = 11 then NULL else to_char(i % :part_mod, 'FM0000') end FROM generate_series(0, :num_rows) i WHERE i % :part_mod IN (0,11,12); +ANALYZE plt2; +-- inner join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a; + QUERY PLAN +------------------------------------------------------------ + Sort + Sort Key: t1.a + -> Append + -> Hash Join + Hash Cond: ((t2.c)::text = (t1.c)::text) + Join Filter: ((t1.b + t2.b) = 0) + -> Seq Scan on plt2_p4 t2 + -> Hash + -> Seq Scan on plt1_p4 t1 + -> Hash Join + Hash Cond: ((t2_1.c)::text = (t1_1.c)::text) + Join Filter: ((t1_1.b + t2_1.b) = 0) + -> Seq Scan on plt2_p1 t2_1 + -> Hash + -> Seq Scan on plt1_p1 t1_1 + -> Hash Join + Hash Cond: ((t1_2.c)::text = (t2_2.c)::text) + Join Filter: ((t1_2.b + t2_2.b) = 0) + -> Seq Scan on plt1_p2 t1_2 + -> Hash + -> Seq Scan on plt2_p2 t2_2 + -> Hash Join + Hash Cond: ((t1_3.c)::text = (t2_3.c)::text) + Join Filter: ((t1_3.b + t2_3.b) = 0) + -> Seq Scan on plt1_p3 t1_3 + -> Hash + -> Seq Scan on plt2_p3 t2_3 +(27 rows) + +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a; + a | c | a | c +-----+------+-----+------ + 0 | 0000 | 0 | 0000 + 94 | 0009 | 94 | 0009 + 141 | 0005 | 141 | 0005 + 329 | 0006 | 329 | 0006 + 376 | 0002 | 376 | 0002 +(5 rows) + +-- left join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a; + QUERY PLAN +------------------------------------------------------------ + Sort + Sort Key: t1.a + -> Append + -> Hash Right Join + Hash Cond: ((t2.c)::text = (t1.c)::text) + Filter: ((t1.b + COALESCE(t2.b, 0)) = 0) + -> Seq Scan on plt2_p4 t2 + -> Hash + -> Seq Scan on plt1_p4 t1 + -> Hash Right Join + Hash Cond: ((t2_1.c)::text = (t1_1.c)::text) + Filter: ((t1_1.b + COALESCE(t2_1.b, 0)) = 0) + -> Seq Scan on plt2_p1 t2_1 + -> Hash + -> Seq Scan on plt1_p1 t1_1 + -> Hash Left Join + Hash Cond: ((t1_2.c)::text = (t2_2.c)::text) + Filter: ((t1_2.b + COALESCE(t2_2.b, 0)) = 0) + -> Seq Scan on plt1_p2 t1_2 + -> Hash + -> Seq Scan on plt2_p2 t2_2 + -> Hash Left Join + Hash Cond: ((t1_3.c)::text = (t2_3.c)::text) + Filter: ((t1_3.b + COALESCE(t2_3.b, 0)) = 0) + -> Seq Scan on plt1_p3 t1_3 + -> Hash + -> Seq Scan on plt2_p3 t2_3 +(27 rows) + +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a; + a | c | a | c +-----+------+-----+------ + 0 | 0000 | 0 | 0000 + 94 | 0009 | 94 | 0009 + 141 | 0005 | 141 | 0005 + 188 | 0001 | | + 282 | 0010 | | + 329 | 0006 | 329 | 0006 + 376 | 0002 | 376 | 0002 +(7 rows) + +-- right join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t2.a; + QUERY PLAN +------------------------------------------------------------ + Sort + Sort Key: t2.a + -> Append + -> Hash Left Join + Hash Cond: ((t2.c)::text = (t1.c)::text) + Filter: ((COALESCE(t1.b, 0) + t2.b) = 0) + -> Seq Scan on plt2_p4 t2 + -> Hash + -> Seq Scan on plt1_p4 t1 + -> Hash Left Join + Hash Cond: ((t2_1.c)::text = (t1_1.c)::text) + Filter: ((COALESCE(t1_1.b, 0) + t2_1.b) = 0) + -> Seq Scan on plt2_p1 t2_1 + -> Hash + -> Seq Scan on plt1_p1 t1_1 + -> Hash Left Join + Hash Cond: ((t2_2.c)::text = (t1_2.c)::text) + Filter: ((COALESCE(t1_2.b, 0) + t2_2.b) = 0) + -> Seq Scan on plt2_p2 t2_2 + -> Hash + -> Seq Scan on plt1_p2 t1_2 + -> Hash Right Join + Hash Cond: ((t1_3.c)::text = (t2_3.c)::text) + Filter: ((COALESCE(t1_3.b, 0) + t2_3.b) = 0) + -> Seq Scan on plt1_p3 t1_3 + -> Hash + -> Seq Scan on plt2_p3 t2_3 +(27 rows) + +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t1.a; + a | c | a | c +-----+------+-----+------ + 0 | 0000 | 0 | 0000 + 94 | 0009 | 94 | 0009 + 141 | 0005 | 141 | 0005 + 329 | 0006 | 329 | 0006 + 376 | 0002 | 376 | 0002 + | | 470 | +(6 rows) + +-- full join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a; + QUERY PLAN +------------------------------------------------------------------------- + Sort + Sort Key: t1.a, t2.a + -> Append + -> Hash Full Join + Hash Cond: ((t2.c)::text = (t1.c)::text) + Filter: ((COALESCE(t1.b, 0) + COALESCE(t2.b, 0)) = 0) + -> Seq Scan on plt2_p4 t2 + -> Hash + -> Seq Scan on plt1_p4 t1 + -> Hash Full Join + Hash Cond: ((t2_1.c)::text = (t1_1.c)::text) + Filter: ((COALESCE(t1_1.b, 0) + COALESCE(t2_1.b, 0)) = 0) + -> Seq Scan on plt2_p1 t2_1 + -> Hash + -> Seq Scan on plt1_p1 t1_1 + -> Hash Full Join + Hash Cond: ((t1_2.c)::text = (t2_2.c)::text) + Filter: ((COALESCE(t1_2.b, 0) + COALESCE(t2_2.b, 0)) = 0) + -> Seq Scan on plt1_p2 t1_2 + -> Hash + -> Seq Scan on plt2_p2 t2_2 + -> Hash Full Join + Hash Cond: ((t1_3.c)::text = (t2_3.c)::text) + Filter: ((COALESCE(t1_3.b, 0) + COALESCE(t2_3.b, 0)) = 0) + -> Seq Scan on plt1_p3 t1_3 + -> Hash + -> Seq Scan on plt2_p3 t2_3 +(27 rows) + +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a; + a | c | a | c +-----+------+-----+------ + 0 | 0000 | 0 | 0000 + 94 | 0009 | 94 | 0009 + 141 | 0005 | 141 | 0005 + 188 | 0001 | | + 282 | 0010 | | + 329 | 0006 | 329 | 0006 + 376 | 0002 | 376 | 0002 + | | 470 | +(8 rows) + +-- semi join +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + QUERY PLAN +-------------------------------------------------------------- + Sort + Sort Key: t1.a, t1.c + -> Append + -> Nested Loop + Join Filter: ((t1.c)::text = (t2.c)::text) + -> HashAggregate + Group Key: (t2.c)::text + -> Seq Scan on plt2_p4 t2 + -> Materialize + -> Seq Scan on plt1_p4 t1 + Filter: (b = 0) + -> Nested Loop + Join Filter: ((t1_1.c)::text = (t2_1.c)::text) + -> HashAggregate + Group Key: (t2_1.c)::text + -> Seq Scan on plt2_p1 t2_1 + -> Materialize + -> Seq Scan on plt1_p1 t1_1 + Filter: (b = 0) + -> Nested Loop Semi Join + Join Filter: ((t1_2.c)::text = (t2_2.c)::text) + -> Seq Scan on plt1_p2 t1_2 + Filter: (b = 0) + -> Seq Scan on plt2_p2 t2_2 + -> Nested Loop Semi Join + Join Filter: ((t1_3.c)::text = (t2_3.c)::text) + -> Seq Scan on plt1_p3 t1_3 + Filter: (b = 0) + -> Seq Scan on plt2_p3 t2_3 +(29 rows) + +select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + a | b | c +-----+---+------ + 0 | 0 | 0000 + 94 | 0 | 0009 + 141 | 0 | 0005 + 329 | 0 | 0006 + 376 | 0 | 0002 +(5 rows) + +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + QUERY PLAN +-------------------------------------------------------------- + Sort + Sort Key: t1.a, t1.c + -> Append + -> Nested Loop + Join Filter: ((t1.c)::text = (t2.c)::text) + -> HashAggregate + Group Key: (t2.c)::text + -> Seq Scan on plt1_p4 t2 + -> Materialize + -> Seq Scan on plt2_p4 t1 + Filter: (b = 0) + -> Nested Loop Semi Join + Join Filter: ((t1_1.c)::text = (t2_1.c)::text) + -> Seq Scan on plt2_p1 t1_1 + Filter: (b = 0) + -> Seq Scan on plt1_p1 t2_1 + -> Nested Loop Semi Join + Join Filter: ((t1_2.c)::text = (t2_2.c)::text) + -> Seq Scan on plt2_p2 t1_2 + Filter: (b = 0) + -> Seq Scan on plt1_p2 t2_2 + -> Nested Loop Semi Join + Join Filter: ((t1_3.c)::text = (t2_3.c)::text) + -> Seq Scan on plt2_p3 t1_3 + Filter: (b = 0) + -> Seq Scan on plt1_p3 t2_3 +(26 rows) + +select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + a | b | c +-----+---+------ + 0 | 0 | 0000 + 94 | 0 | 0009 + 141 | 0 | 0005 + 329 | 0 | 0006 + 376 | 0 | 0002 +(5 rows) + +-- anti join +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + QUERY PLAN +-------------------------------------------------------------- + Sort + Sort Key: t1.a, t1.c + -> Append + -> Nested Loop Anti Join + Join Filter: ((t1.c)::text = (t2.c)::text) + -> Seq Scan on plt1_p4 t1 + Filter: (b = 0) + -> Seq Scan on plt2_p4 t2 + -> Hash Anti Join + Hash Cond: ((t1_1.c)::text = (t2_1.c)::text) + -> Seq Scan on plt1_p1 t1_1 + Filter: (b = 0) + -> Hash + -> Seq Scan on plt2_p1 t2_1 + -> Nested Loop Anti Join + Join Filter: ((t1_2.c)::text = (t2_2.c)::text) + -> Seq Scan on plt1_p2 t1_2 + Filter: (b = 0) + -> Seq Scan on plt2_p2 t2_2 + -> Nested Loop Anti Join + Join Filter: ((t1_3.c)::text = (t2_3.c)::text) + -> Seq Scan on plt1_p3 t1_3 + Filter: (b = 0) + -> Seq Scan on plt2_p3 t2_3 +(24 rows) + +select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + a | b | c +-----+---+------ + 188 | 0 | 0001 + 282 | 0 | 0010 +(2 rows) + +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + QUERY PLAN +-------------------------------------------------------------- + Sort + Sort Key: t1.a, t1.c + -> Append + -> Hash Anti Join + Hash Cond: ((t1.c)::text = (t2.c)::text) + -> Seq Scan on plt2_p4 t1 + Filter: (b = 0) + -> Hash + -> Seq Scan on plt1_p4 t2 + -> Nested Loop Anti Join + Join Filter: ((t1_1.c)::text = (t2_1.c)::text) + -> Seq Scan on plt2_p1 t1_1 + Filter: (b = 0) + -> Seq Scan on plt1_p1 t2_1 + -> Nested Loop Anti Join + Join Filter: ((t1_2.c)::text = (t2_2.c)::text) + -> Seq Scan on plt2_p2 t1_2 + Filter: (b = 0) + -> Seq Scan on plt1_p2 t2_2 + -> Nested Loop Anti Join + Join Filter: ((t1_3.c)::text = (t2_3.c)::text) + -> Seq Scan on plt2_p3 t1_3 + Filter: (b = 0) + -> Seq Scan on plt1_p3 t2_3 +(24 rows) + +select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + a | b | c +-----+---+--- + 470 | 0 | +(1 row) + +-- partition have a NULL on both side with different partition bounds w.r.t other side +-- NULL when NULL comparision is not strict i.e. NULL=NULL allowed +-- Partition-wise join can not handle the case when one partition from one side +-- matches with multiple partitions on the other side +DROP TABLE plt1_p3; +CREATE TABLE plt1_p3 PARTITION OF plt1 FOR VALUES IN (NULL,'0008','0009'); +INSERT INTO plt1 SELECT i, i % :cond_mod, case when i % :part_mod = 7 then NULL else to_char(i % :part_mod, 'FM0000') end FROM generate_series(0, :num_rows) i WHERE i % :part_mod IN (7,8,9); +ANALYZE plt1; +-- inner join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a; + QUERY PLAN +-------------------------------------------------- + Sort + Sort Key: t1.a + -> Hash Join + Hash Cond: ((t2.c)::text = (t1.c)::text) + Join Filter: ((t1.b + t2.b) = 0) + -> Append + -> Seq Scan on plt2_p4 t2 + -> Seq Scan on plt2_p1 t2_1 + -> Seq Scan on plt2_p2 t2_2 + -> Seq Scan on plt2_p3 t2_3 + -> Hash + -> Append + -> Seq Scan on plt1_p4 t1 + -> Seq Scan on plt1_p1 t1_1 + -> Seq Scan on plt1_p2 t1_2 + -> Seq Scan on plt1_p3 t1_3 +(16 rows) + +-- left join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a; + QUERY PLAN +-------------------------------------------------- + Sort + Sort Key: t1.a + -> Hash Right Join + Hash Cond: ((t2.c)::text = (t1.c)::text) + Filter: ((t1.b + COALESCE(t2.b, 0)) = 0) + -> Append + -> Seq Scan on plt2_p4 t2 + -> Seq Scan on plt2_p1 t2_1 + -> Seq Scan on plt2_p2 t2_2 + -> Seq Scan on plt2_p3 t2_3 + -> Hash + -> Append + -> Seq Scan on plt1_p4 t1 + -> Seq Scan on plt1_p1 t1_1 + -> Seq Scan on plt1_p2 t1_2 + -> Seq Scan on plt1_p3 t1_3 +(16 rows) + +-- right join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t2.a; + QUERY PLAN +-------------------------------------------------- Sort - Sort Key: prt1_m_p1.a, prt2_m_p1.b - -> Append - -> Hash Full Join - Hash Cond: ((prt1_m_p1.a = ((prt2_m_p1.b + prt2_m_p1.a) / 2)) AND (((prt1_m_p1.a + prt1_m_p1.b) / 2) = prt2_m_p1.b)) - -> Seq Scan on prt1_m_p1 - Filter: (c = 0) - -> Hash - -> Seq Scan on prt2_m_p1 - Filter: (c = 0) - -> Hash Full Join - Hash Cond: ((prt1_m_p2.a = ((prt2_m_p2.b + prt2_m_p2.a) / 2)) AND (((prt1_m_p2.a + prt1_m_p2.b) / 2) = prt2_m_p2.b)) - -> Seq Scan on prt1_m_p2 - Filter: (c = 0) - -> Hash - -> Seq Scan on prt2_m_p2 - Filter: (c = 0) - -> Hash Full Join - Hash Cond: ((prt1_m_p3.a = ((prt2_m_p3.b + prt2_m_p3.a) / 2)) AND (((prt1_m_p3.a + prt1_m_p3.b) / 2) = prt2_m_p3.b)) - -> Seq Scan on prt1_m_p3 - Filter: (c = 0) - -> Hash - -> Seq Scan on prt2_m_p3 - Filter: (c = 0) -(24 rows) + Sort Key: t2.a + -> Hash Left Join + Hash Cond: ((t2.c)::text = (t1.c)::text) + Filter: ((COALESCE(t1.b, 0) + t2.b) = 0) + -> Append + -> Seq Scan on plt2_p4 t2 + -> Seq Scan on plt2_p1 t2_1 + -> Seq Scan on plt2_p2 t2_2 + -> Seq Scan on plt2_p3 t2_3 + -> Hash + -> Append + -> Seq Scan on plt1_p4 t1 + -> Seq Scan on plt1_p1 t1_1 + -> Seq Scan on plt1_p2 t1_2 + -> Seq Scan on plt1_p3 t1_3 +(16 rows) -SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_m WHERE prt1_m.c = 0) t1 FULL JOIN (SELECT * FROM prt2_m WHERE prt2_m.c = 0) t2 ON (t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2) ORDER BY t1.a, t2.b; - a | c | b | c ------+---+-----+--- - 0 | 0 | 0 | 0 - 50 | 0 | | - 100 | 0 | | - 150 | 0 | 150 | 0 - 200 | 0 | | - 250 | 0 | | - 300 | 0 | 300 | 0 - 350 | 0 | | - 400 | 0 | | - 450 | 0 | 450 | 0 - 500 | 0 | | - 550 | 0 | | - | | 75 | 0 - | | 225 | 0 - | | 375 | 0 - | | 525 | 0 +-- full join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a; + QUERY PLAN +--------------------------------------------------------------- + Sort + Sort Key: t1.a, t2.a + -> Hash Full Join + Hash Cond: ((t2.c)::text = (t1.c)::text) + Filter: ((COALESCE(t1.b, 0) + COALESCE(t2.b, 0)) = 0) + -> Append + -> Seq Scan on plt2_p4 t2 + -> Seq Scan on plt2_p1 t2_1 + -> Seq Scan on plt2_p2 t2_2 + -> Seq Scan on plt2_p3 t2_3 + -> Hash + -> Append + -> Seq Scan on plt1_p4 t1 + -> Seq Scan on plt1_p1 t1_1 + -> Seq Scan on plt1_p2 t1_2 + -> Seq Scan on plt1_p3 t1_3 (16 rows) --- --- tests for list partitioned tables. --- -CREATE TABLE plt1 (a int, b int, c text) PARTITION BY LIST(c); -CREATE TABLE plt1_p1 PARTITION OF plt1 FOR VALUES IN ('0000', '0003', '0004', '0010'); -CREATE TABLE plt1_p2 PARTITION OF plt1 FOR VALUES IN ('0001', '0005', '0002', '0009'); -CREATE TABLE plt1_p3 PARTITION OF plt1 FOR VALUES IN ('0006', '0007', '0008', '0011'); -INSERT INTO plt1 SELECT i, i, to_char(i/50, 'FM0000') FROM generate_series(0, 599, 2) i; -ANALYZE plt1; -CREATE TABLE plt2 (a int, b int, c text) PARTITION BY LIST(c); -CREATE TABLE plt2_p1 PARTITION OF plt2 FOR VALUES IN ('0000', '0003', '0004', '0010'); -CREATE TABLE plt2_p2 PARTITION OF plt2 FOR VALUES IN ('0001', '0005', '0002', '0009'); -CREATE TABLE plt2_p3 PARTITION OF plt2 FOR VALUES IN ('0006', '0007', '0008', '0011'); -INSERT INTO plt2 SELECT i, i, to_char(i/50, 'FM0000') FROM generate_series(0, 599, 3) i; -ANALYZE plt2; --- --- list partitioned by expression --- -CREATE TABLE plt1_e (a int, b int, c text) PARTITION BY LIST(ltrim(c, 'A')); -CREATE TABLE plt1_e_p1 PARTITION OF plt1_e FOR VALUES IN ('0000', '0003', '0004', '0010'); -CREATE TABLE plt1_e_p2 PARTITION OF plt1_e FOR VALUES IN ('0001', '0005', '0002', '0009'); -CREATE TABLE plt1_e_p3 PARTITION OF plt1_e FOR VALUES IN ('0006', '0007', '0008', '0011'); -INSERT INTO plt1_e SELECT i, i, 'A' || to_char(i/50, 'FM0000') FROM generate_series(0, 599, 2) i; -ANALYZE plt1_e; --- test partition matching with N-way join +-- semi join EXPLAIN (COSTS OFF) -SELECT avg(t1.a), avg(t2.b), avg(t3.a + t3.b), t1.c, t2.c, t3.c FROM plt1 t1, plt2 t2, plt1_e t3 WHERE t1.b = t2.b AND t1.c = t2.c AND ltrim(t3.c, 'A') = t1.c GROUP BY t1.c, t2.c, t3.c ORDER BY t1.c, t2.c, t3.c; - QUERY PLAN --------------------------------------------------------------------------------- - GroupAggregate - Group Key: t1.c, t2.c, t3.c - -> Sort - Sort Key: t1.c, t3.c +select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + QUERY PLAN +-------------------------------------------------------------- + Sort + Sort Key: t1.a, t1.c + -> Hash Join + Hash Cond: ((t1.c)::text = (t2.c)::text) -> Append - -> Hash Join - Hash Cond: (t1.c = ltrim(t3.c, 'A'::text)) - -> Hash Join - Hash Cond: ((t1.b = t2.b) AND (t1.c = t2.c)) - -> Seq Scan on plt1_p1 t1 - -> Hash - -> Seq Scan on plt2_p1 t2 - -> Hash - -> Seq Scan on plt1_e_p1 t3 - -> Hash Join - Hash Cond: (t1_1.c = ltrim(t3_1.c, 'A'::text)) - -> Hash Join - Hash Cond: ((t1_1.b = t2_1.b) AND (t1_1.c = t2_1.c)) - -> Seq Scan on plt1_p2 t1_1 - -> Hash - -> Seq Scan on plt2_p2 t2_1 - -> Hash - -> Seq Scan on plt1_e_p2 t3_1 - -> Hash Join - Hash Cond: (t1_2.c = ltrim(t3_2.c, 'A'::text)) - -> Hash Join - Hash Cond: ((t1_2.b = t2_2.b) AND (t1_2.c = t2_2.c)) - -> Seq Scan on plt1_p3 t1_2 - -> Hash - -> Seq Scan on plt2_p3 t2_2 - -> Hash - -> Seq Scan on plt1_e_p3 t3_2 -(32 rows) + -> Seq Scan on plt1_p4 t1 + Filter: (b = 0) + -> Seq Scan on plt1_p1 t1_1 + Filter: (b = 0) + -> Seq Scan on plt1_p2 t1_2 + Filter: (b = 0) + -> Seq Scan on plt1_p3 t1_3 + Filter: (b = 0) + -> Hash + -> HashAggregate + Group Key: (t2.c)::text + -> Result + -> Append + -> Seq Scan on plt2_p4 t2 + -> Seq Scan on plt2_p1 t2_1 + -> Seq Scan on plt2_p2 t2_2 + -> Seq Scan on plt2_p3 t2_3 +(22 rows) -SELECT avg(t1.a), avg(t2.b), avg(t3.a + t3.b), t1.c, t2.c, t3.c FROM plt1 t1, plt2 t2, plt1_e t3 WHERE t1.b = t2.b AND t1.c = t2.c AND ltrim(t3.c, 'A') = t1.c GROUP BY t1.c, t2.c, t3.c ORDER BY t1.c, t2.c, t3.c; - avg | avg | avg | c | c | c -----------------------+----------------------+-----------------------+------+------+------- - 24.0000000000000000 | 24.0000000000000000 | 48.0000000000000000 | 0000 | 0000 | A0000 - 75.0000000000000000 | 75.0000000000000000 | 148.0000000000000000 | 0001 | 0001 | A0001 - 123.0000000000000000 | 123.0000000000000000 | 248.0000000000000000 | 0002 | 0002 | A0002 - 174.0000000000000000 | 174.0000000000000000 | 348.0000000000000000 | 0003 | 0003 | A0003 - 225.0000000000000000 | 225.0000000000000000 | 448.0000000000000000 | 0004 | 0004 | A0004 - 273.0000000000000000 | 273.0000000000000000 | 548.0000000000000000 | 0005 | 0005 | A0005 - 324.0000000000000000 | 324.0000000000000000 | 648.0000000000000000 | 0006 | 0006 | A0006 - 375.0000000000000000 | 375.0000000000000000 | 748.0000000000000000 | 0007 | 0007 | A0007 - 423.0000000000000000 | 423.0000000000000000 | 848.0000000000000000 | 0008 | 0008 | A0008 - 474.0000000000000000 | 474.0000000000000000 | 948.0000000000000000 | 0009 | 0009 | A0009 - 525.0000000000000000 | 525.0000000000000000 | 1048.0000000000000000 | 0010 | 0010 | A0010 - 573.0000000000000000 | 573.0000000000000000 | 1148.0000000000000000 | 0011 | 0011 | A0011 -(12 rows) +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + QUERY PLAN +-------------------------------------------------------------- + Sort + Sort Key: t1.a, t1.c + -> Hash Join + Hash Cond: ((t1.c)::text = (t2.c)::text) + -> Append + -> Seq Scan on plt2_p4 t1 + Filter: (b = 0) + -> Seq Scan on plt2_p1 t1_1 + Filter: (b = 0) + -> Seq Scan on plt2_p2 t1_2 + Filter: (b = 0) + -> Seq Scan on plt2_p3 t1_3 + Filter: (b = 0) + -> Hash + -> HashAggregate + Group Key: (t2.c)::text + -> Result + -> Append + -> Seq Scan on plt1_p4 t2 + -> Seq Scan on plt1_p1 t2_1 + -> Seq Scan on plt1_p2 t2_2 + -> Seq Scan on plt1_p3 t2_3 +(22 rows) + +-- anti join +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + QUERY PLAN +-------------------------------------------------- + Sort + Sort Key: t1.a, t1.c + -> Hash Anti Join + Hash Cond: ((t1.c)::text = (t2.c)::text) + -> Append + -> Seq Scan on plt1_p4 t1 + Filter: (b = 0) + -> Seq Scan on plt1_p1 t1_1 + Filter: (b = 0) + -> Seq Scan on plt1_p2 t1_2 + Filter: (b = 0) + -> Seq Scan on plt1_p3 t1_3 + Filter: (b = 0) + -> Hash + -> Append + -> Seq Scan on plt2_p4 t2 + -> Seq Scan on plt2_p1 t2_1 + -> Seq Scan on plt2_p2 t2_2 + -> Seq Scan on plt2_p3 t2_3 +(19 rows) + +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + QUERY PLAN +-------------------------------------------------- + Sort + Sort Key: t1.a, t1.c + -> Hash Anti Join + Hash Cond: ((t1.c)::text = (t2.c)::text) + -> Append + -> Seq Scan on plt2_p4 t1 + Filter: (b = 0) + -> Seq Scan on plt2_p1 t1_1 + Filter: (b = 0) + -> Seq Scan on plt2_p2 t1_2 + Filter: (b = 0) + -> Seq Scan on plt2_p3 t1_3 + Filter: (b = 0) + -> Hash + -> Append + -> Seq Scan on plt1_p4 t2 + -> Seq Scan on plt1_p1 t2_1 + -> Seq Scan on plt1_p2 t2_2 + -> Seq Scan on plt1_p3 t2_3 +(19 rows) -- joins where one of the relations is proven empty EXPLAIN (COSTS OFF) @@ -1241,22 +4040,22 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a = 1 AND a = 2) t1 -------------------------------------------------- Hash Left Join Hash Cond: (t2.b = a) - -> Append - -> Hash Join - Hash Cond: (t3.a = t2.b) - -> Seq Scan on prt1_p1 t3 - -> Hash - -> Seq Scan on prt2_p1 t2 - -> Hash Join - Hash Cond: (t3_1.a = t2_1.b) - -> Seq Scan on prt1_p2 t3_1 - -> Hash - -> Seq Scan on prt2_p2 t2_1 - -> Hash Join - Hash Cond: (t3_2.a = t2_2.b) - -> Seq Scan on prt1_p3 t3_2 - -> Hash - -> Seq Scan on prt2_p3 t2_2 + -> Hash Join + Hash Cond: (t3.a = t2.b) + -> Append + -> Seq Scan on prt1_p0 t3 + -> Seq Scan on prt1_p1 t3_1 + -> Seq Scan on prt1_p2 t3_2 + -> Seq Scan on prt1_p3 t3_3 + -> Seq Scan on prt1_p4 t3_4 + -> Hash + -> Append + -> Seq Scan on prt2_p0 t2 + -> Seq Scan on prt2_p1 t2_1 + -> Seq Scan on prt2_p2 t2_2 + -> Seq Scan on prt2_p3 t2_3 + -> Seq Scan on prt2_p4 t2_4 + -> Seq Scan on prt2_p5 t2_5 -> Hash -> Result One-Time Filter: false @@ -1271,16 +4070,22 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a = 1 AND a = 2) t1 -> Hash Left Join Hash Cond: (t2.b = a) -> Append - -> Seq Scan on prt2_p1 t2 + -> Seq Scan on prt2_p0 t2 Filter: (a = 0) - -> Seq Scan on prt2_p2 t2_1 + -> Seq Scan on prt2_p1 t2_1 Filter: (a = 0) - -> Seq Scan on prt2_p3 t2_2 + -> Seq Scan on prt2_p2 t2_2 + Filter: (a = 0) + -> Seq Scan on prt2_p3 t2_3 + Filter: (a = 0) + -> Seq Scan on prt2_p4 t2_4 + Filter: (a = 0) + -> Seq Scan on prt2_p5 t2_5 Filter: (a = 0) -> Hash -> Result One-Time Filter: false -(14 rows) +(20 rows) -- -- tests for hash partitioned tables. @@ -1356,41 +4161,9 @@ SELECT avg(t1.a), avg(t2.b), avg(t3.a + t3.b), t1.c, t2.c, t3.c FROM pht1 t1, ph 273.0000000000000000 | 273.0000000000000000 | 548.0000000000000000 | 0005 | 0005 | A0005 (6 rows) --- test default partition behavior for range -ALTER TABLE prt1 DETACH PARTITION prt1_p3; -ALTER TABLE prt1 ATTACH PARTITION prt1_p3 DEFAULT; -ANALYZE prt1; -ALTER TABLE prt2 DETACH PARTITION prt2_p3; -ALTER TABLE prt2 ATTACH PARTITION prt2_p3 DEFAULT; -ANALYZE prt2; -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.b = 0 ORDER BY t1.a, t2.b; - QUERY PLAN --------------------------------------------------- - Sort - Sort Key: t1.a - -> Append - -> Hash Join - Hash Cond: (t2.b = t1.a) - -> Seq Scan on prt2_p1 t2 - -> Hash - -> Seq Scan on prt1_p1 t1 - Filter: (b = 0) - -> Hash Join - Hash Cond: (t2_1.b = t1_1.a) - -> Seq Scan on prt2_p2 t2_1 - -> Hash - -> Seq Scan on prt1_p2 t1_1 - Filter: (b = 0) - -> Hash Join - Hash Cond: (t2_2.b = t1_2.a) - -> Seq Scan on prt2_p3 t2_2 - -> Hash - -> Seq Scan on prt1_p3 t1_2 - Filter: (b = 0) -(21 rows) - --- test default partition behavior for list +-- test default partition behavior for list, should not use partition-wise join +-- since default partition from one side matches multiple partitions on the +-- other ALTER TABLE plt1 DETACH PARTITION plt1_p3; ALTER TABLE plt1 ATTACH PARTITION plt1_p3 DEFAULT; ANALYZE plt1; @@ -1405,26 +4178,24 @@ SELECT avg(t1.a), avg(t2.b), t1.c, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c Sort Key: t1.c -> HashAggregate Group Key: t1.c, t2.c - -> Append - -> Hash Join - Hash Cond: (t2.c = t1.c) - -> Seq Scan on plt2_p1 t2 - -> Hash - -> Seq Scan on plt1_p1 t1 + -> Hash Join + Hash Cond: ((t2.c)::text = (t1.c)::text) + -> Append + -> Seq Scan on plt2_p4 t2 + -> Seq Scan on plt2_p1 t2_1 + -> Seq Scan on plt2_p2 t2_2 + -> Seq Scan on plt2_p3 t2_3 + -> Hash + -> Append + -> Seq Scan on plt1_p4 t1 Filter: ((a % 25) = 0) - -> Hash Join - Hash Cond: (t2_1.c = t1_1.c) - -> Seq Scan on plt2_p2 t2_1 - -> Hash - -> Seq Scan on plt1_p2 t1_1 + -> Seq Scan on plt1_p1 t1_1 Filter: ((a % 25) = 0) - -> Hash Join - Hash Cond: (t2_2.c = t1_2.c) - -> Seq Scan on plt2_p3 t2_2 - -> Hash - -> Seq Scan on plt1_p3 t1_2 + -> Seq Scan on plt1_p2 t1_2 Filter: ((a % 25) = 0) -(23 rows) + -> Seq Scan on plt1_p3 t1_3 + Filter: ((a % 25) = 0) +(21 rows) -- -- multiple levels of partitioning @@ -1826,64 +4597,70 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt4_n t2 WHERE t1.a = t2.a; Hash Join Hash Cond: (t1.a = t2.a) -> Append - -> Seq Scan on prt1_p1 t1 - -> Seq Scan on prt1_p2 t1_1 - -> Seq Scan on prt1_p3 t1_2 + -> Seq Scan on prt1_p0 t1 + -> Seq Scan on prt1_p1 t1_1 + -> Seq Scan on prt1_p2 t1_2 + -> Seq Scan on prt1_p3 t1_3 + -> Seq Scan on prt1_p4 t1_4 -> Hash -> Append -> Seq Scan on prt4_n_p1 t2 -> Seq Scan on prt4_n_p2 t2_1 -> Seq Scan on prt4_n_p3 t2_2 -(11 rows) +(13 rows) EXPLAIN (COSTS OFF) SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt4_n t2, prt2 t3 WHERE t1.a = t2.a and t1.a = t3.b; - QUERY PLAN --------------------------------------------------------- + QUERY PLAN +---------------------------------------------------------- Hash Join - Hash Cond: (t2.a = t1.a) + Hash Cond: (t3.b = t1.a) -> Append - -> Seq Scan on prt4_n_p1 t2 - -> Seq Scan on prt4_n_p2 t2_1 - -> Seq Scan on prt4_n_p3 t2_2 + -> Seq Scan on prt2_p0 t3 + -> Seq Scan on prt2_p1 t3_1 + -> Seq Scan on prt2_p2 t3_2 + -> Seq Scan on prt2_p3 t3_3 + -> Seq Scan on prt2_p4 t3_4 + -> Seq Scan on prt2_p5 t3_5 -> Hash - -> Append - -> Hash Join - Hash Cond: (t1.a = t3.b) - -> Seq Scan on prt1_p1 t1 - -> Hash - -> Seq Scan on prt2_p1 t3 - -> Hash Join - Hash Cond: (t1_1.a = t3_1.b) - -> Seq Scan on prt1_p2 t1_1 - -> Hash - -> Seq Scan on prt2_p2 t3_1 - -> Hash Join - Hash Cond: (t1_2.a = t3_2.b) - -> Seq Scan on prt1_p3 t1_2 - -> Hash - -> Seq Scan on prt2_p3 t3_2 + -> Hash Join + Hash Cond: (t1.a = t2.a) + -> Append + -> Seq Scan on prt1_p0 t1 + -> Seq Scan on prt1_p1 t1_1 + -> Seq Scan on prt1_p2 t1_2 + -> Seq Scan on prt1_p3 t1_3 + -> Seq Scan on prt1_p4 t1_4 + -> Hash + -> Append + -> Seq Scan on prt4_n_p1 t2 + -> Seq Scan on prt4_n_p2 t2_1 + -> Seq Scan on prt4_n_p3 t2_2 (23 rows) -- partitionwise join can not be applied if there are no equi-join conditions -- between partition keys EXPLAIN (COSTS OFF) SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1 LEFT JOIN prt2 t2 ON (t1.a < t2.b); - QUERY PLAN ---------------------------------------------------------- + QUERY PLAN +-------------------------------------------- Nested Loop Left Join + Join Filter: (t1.a < t2.b) -> Append - -> Seq Scan on prt1_p1 t1 - -> Seq Scan on prt1_p2 t1_1 - -> Seq Scan on prt1_p3 t1_2 - -> Append - -> Index Scan using iprt2_p1_b on prt2_p1 t2 - Index Cond: (t1.a < b) - -> Index Scan using iprt2_p2_b on prt2_p2 t2_1 - Index Cond: (t1.a < b) - -> Index Scan using iprt2_p3_b on prt2_p3 t2_2 - Index Cond: (t1.a < b) -(12 rows) + -> Seq Scan on prt1_p0 t1 + -> Seq Scan on prt1_p1 t1_1 + -> Seq Scan on prt1_p2 t1_2 + -> Seq Scan on prt1_p3 t1_3 + -> Seq Scan on prt1_p4 t1_4 + -> Materialize + -> Append + -> Seq Scan on prt2_p0 t2 + -> Seq Scan on prt2_p1 t2_1 + -> Seq Scan on prt2_p2 t2_2 + -> Seq Scan on prt2_p3 t2_3 + -> Seq Scan on prt2_p4 t2_4 + -> Seq Scan on prt2_p5 t2_5 +(16 rows) -- equi-join with join condition on partial keys does not qualify for -- partitionwise join @@ -1969,16 +4746,17 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_n t1 JOIN prt2_n t2 ON (t1.c = t2.c) JOI -> Seq Scan on prt2_n_p2 t2_1 -> Hash -> Hash Join - Hash Cond: (t3.c = (t1.c)::text) + Hash Cond: ((t3.c)::text = (t1.c)::text) -> Append - -> Seq Scan on plt1_p1 t3 - -> Seq Scan on plt1_p2 t3_1 - -> Seq Scan on plt1_p3 t3_2 + -> Seq Scan on plt1_p4 t3 + -> Seq Scan on plt1_p1 t3_1 + -> Seq Scan on plt1_p2 t3_2 + -> Seq Scan on plt1_p3 t3_3 -> Hash -> Append -> Seq Scan on prt1_n_p1 t1 -> Seq Scan on prt1_n_p2 t1_1 -(16 rows) +(17 rows) -- partitionwise join can not be applied for a join between list and range -- partitioned table @@ -1989,14 +4767,16 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_n t1 FULL JOIN prt1 t2 ON (t1.c = t2.c); Hash Full Join Hash Cond: ((t2.c)::text = (t1.c)::text) -> Append - -> Seq Scan on prt1_p1 t2 - -> Seq Scan on prt1_p2 t2_1 - -> Seq Scan on prt1_p3 t2_2 + -> Seq Scan on prt1_p0 t2 + -> Seq Scan on prt1_p1 t2_1 + -> Seq Scan on prt1_p2 t2_2 + -> Seq Scan on prt1_p3 t2_3 + -> Seq Scan on prt1_p4 t2_4 -> Hash -> Append -> Seq Scan on prt1_n_p1 t1 -> Seq Scan on prt1_n_p2 t1_1 -(10 rows) +(12 rows) -- partitionwise join can not be applied if only one of joining table has -- default partition @@ -2012,16 +4792,23 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.b = -> Hash Join Hash Cond: (t2.b = t1.a) -> Append - -> Seq Scan on prt2_p1 t2 - -> Seq Scan on prt2_p2 t2_1 - -> Seq Scan on prt2_p3 t2_2 + -> Seq Scan on prt2_p0 t2 + -> Seq Scan on prt2_p1 t2_1 + -> Seq Scan on prt2_p2 t2_2 + -> Seq Scan on prt2_p3 t2_3 + -> Seq Scan on prt2_p4 t2_4 + -> Seq Scan on prt2_p5 t2_5 -> Hash -> Append - -> Seq Scan on prt1_p1 t1 + -> Seq Scan on prt1_p0 t1 Filter: (b = 0) - -> Seq Scan on prt1_p2 t1_1 + -> Seq Scan on prt1_p1 t1_1 Filter: (b = 0) - -> Seq Scan on prt1_p3 t1_2 + -> Seq Scan on prt1_p2 t1_2 Filter: (b = 0) -(16 rows) + -> Seq Scan on prt1_p3 t1_3 + Filter: (b = 0) + -> Seq Scan on prt1_p4 t1_4 + Filter: (b = 0) +(23 rows) diff --git a/src/test/regress/sql/partition_join.sql b/src/test/regress/sql/partition_join.sql index c1c9859651..0e884835fb 100644 --- a/src/test/regress/sql/partition_join.sql +++ b/src/test/regress/sql/partition_join.sql @@ -10,25 +10,39 @@ SET enable_partitionwise_join to true; -- partitioned by a single column -- CREATE TABLE prt1 (a int, b int, c varchar) PARTITION BY RANGE(a); +CREATE TABLE prt1_p0 PARTITION OF prt1 FOR VALUES FROM (MINVALUE) TO (0); CREATE TABLE prt1_p1 PARTITION OF prt1 FOR VALUES FROM (0) TO (250); CREATE TABLE prt1_p3 PARTITION OF prt1 FOR VALUES FROM (500) TO (600); CREATE TABLE prt1_p2 PARTITION OF prt1 FOR VALUES FROM (250) TO (500); -INSERT INTO prt1 SELECT i, i % 25, to_char(i, 'FM0000') FROM generate_series(0, 599) i WHERE i % 2 = 0; +CREATE TABLE prt1_p4 PARTITION OF prt1 FOR VALUES FROM (600) TO (800); +INSERT INTO prt1 SELECT i, i % 25, to_char(i, 'FM0000') FROM generate_series(-250, 799) i WHERE i % 2 = 0; +CREATE INDEX iprt1_p0_a on prt1_p0(a); CREATE INDEX iprt1_p1_a on prt1_p1(a); CREATE INDEX iprt1_p2_a on prt1_p2(a); CREATE INDEX iprt1_p3_a on prt1_p3(a); +CREATE INDEX iprt1_p4_a on prt1_p4(a); ANALYZE prt1; +-- prt2 have missing starting MINVALUE to -250 range and +-- extra bounds from 800 to MAXVALUE CREATE TABLE prt2 (a int, b int, c varchar) PARTITION BY RANGE(b); +CREATE TABLE prt2_p0 PARTITION OF prt2 FOR VALUES FROM (-250) TO (0); CREATE TABLE prt2_p1 PARTITION OF prt2 FOR VALUES FROM (0) TO (250); CREATE TABLE prt2_p2 PARTITION OF prt2 FOR VALUES FROM (250) TO (500); CREATE TABLE prt2_p3 PARTITION OF prt2 FOR VALUES FROM (500) TO (600); -INSERT INTO prt2 SELECT i % 25, i, to_char(i, 'FM0000') FROM generate_series(0, 599) i WHERE i % 3 = 0; +CREATE TABLE prt2_p4 PARTITION OF prt2 FOR VALUES FROM (600) TO (MAXVALUE); +INSERT INTO prt2 SELECT i % 25, i, to_char(i, 'FM0000') FROM generate_series(-250, 799) i WHERE i % 3 = 0; +CREATE INDEX iprt2_p0_b on prt2_p0(b); CREATE INDEX iprt2_p1_b on prt2_p1(b); CREATE INDEX iprt2_p2_b on prt2_p2(b); CREATE INDEX iprt2_p3_b on prt2_p3(b); +CREATE INDEX iprt2_p4_b on prt2_p4(b); ANALYZE prt2; +-- Partition-wise-join is possible with some partition bounds overlap +-- with each other completely and some partialy for inner,left,right, +-- full, semi and anti joins + -- inner join EXPLAIN (COSTS OFF) SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.b = 0 ORDER BY t1.a, t2.b; @@ -67,11 +81,19 @@ EXPLAIN (COSTS OFF) SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t2.b FROM prt2 t2 WHERE t2.a = 0) AND t1.b = 0 ORDER BY t1.a; SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t2.b FROM prt2 t2 WHERE t2.a = 0) AND t1.b = 0 ORDER BY t1.a; +EXPLAIN (COSTS OFF) +SELECT t1.* FROM prt2 t1 WHERE t1.b IN (SELECT t2.a FROM prt1 t2 WHERE t2.b = 0) AND t1.a = 0 ORDER BY t1.b; +SELECT t1.* FROM prt2 t1 WHERE t1.b IN (SELECT t2.a FROM prt1 t2 WHERE t2.b = 0) AND t1.a = 0 ORDER BY t1.b; + -- Anti-join with aggregates EXPLAIN (COSTS OFF) SELECT sum(t1.a), avg(t1.a), sum(t1.b), avg(t1.b) FROM prt1 t1 WHERE NOT EXISTS (SELECT 1 FROM prt2 t2 WHERE t1.a = t2.b); SELECT sum(t1.a), avg(t1.a), sum(t1.b), avg(t1.b) FROM prt1 t1 WHERE NOT EXISTS (SELECT 1 FROM prt2 t2 WHERE t1.a = t2.b); +EXPLAIN (COSTS OFF) +SELECT t1.b, t1.c FROM prt2 t1 WHERE NOT EXISTS (SELECT 1 FROM prt1 t2 WHERE t1.b = t2.a) and t1.a = 0; +SELECT t1.b, t1.c FROM prt2 t1 WHERE NOT EXISTS (SELECT 1 FROM prt1 t2 WHERE t1.b = t2.a) and t1.a = 0; + -- lateral reference EXPLAIN (COSTS OFF) SELECT * FROM prt1 t1 LEFT JOIN LATERAL @@ -93,20 +115,30 @@ SELECT t1.a, ss.t2a, ss.t2c FROM prt1 t1 LEFT JOIN LATERAL -- partitioned by expression -- CREATE TABLE prt1_e (a int, b int, c int) PARTITION BY RANGE(((a + b)/2)); +CREATE TABLE prt1_e_p0 PARTITION OF prt1_e FOR VALUES FROM (MINVALUE) TO (0); CREATE TABLE prt1_e_p1 PARTITION OF prt1_e FOR VALUES FROM (0) TO (250); CREATE TABLE prt1_e_p2 PARTITION OF prt1_e FOR VALUES FROM (250) TO (500); CREATE TABLE prt1_e_p3 PARTITION OF prt1_e FOR VALUES FROM (500) TO (600); +CREATE TABLE prt1_e_p4 PARTITION OF prt1_e FOR VALUES FROM (600) TO (MAXVALUE); INSERT INTO prt1_e SELECT i, i, i % 25 FROM generate_series(0, 599, 2) i; +INSERT INTO prt1_e SELECT i, i, i % 25 FROM generate_series(-250, 0, 2) i; +INSERT INTO prt1_e SELECT i, i, i % 25 FROM generate_series(600, 799, 2) i; +CREATE INDEX iprt1_e_p0_ab2 on prt1_e_p1(((a+b)/2)); CREATE INDEX iprt1_e_p1_ab2 on prt1_e_p1(((a+b)/2)); CREATE INDEX iprt1_e_p2_ab2 on prt1_e_p2(((a+b)/2)); CREATE INDEX iprt1_e_p3_ab2 on prt1_e_p3(((a+b)/2)); +CREATE INDEX iprt1_e_p4_ab2 on prt1_e_p1(((a+b)/2)); ANALYZE prt1_e; CREATE TABLE prt2_e (a int, b int, c int) PARTITION BY RANGE(((b + a)/2)); +CREATE TABLE prt2_e_p0 PARTITION OF prt2_e FOR VALUES FROM (MINVALUE) TO (0); CREATE TABLE prt2_e_p1 PARTITION OF prt2_e FOR VALUES FROM (0) TO (250); CREATE TABLE prt2_e_p2 PARTITION OF prt2_e FOR VALUES FROM (250) TO (500); CREATE TABLE prt2_e_p3 PARTITION OF prt2_e FOR VALUES FROM (500) TO (600); +CREATE TABLE prt2_e_p4 PARTITION OF prt2_e FOR VALUES FROM (600) TO (MAXVALUE); INSERT INTO prt2_e SELECT i, i, i % 25 FROM generate_series(0, 599, 3) i; +INSERT INTO prt2_e SELECT i, i, i % 25 FROM generate_series(-250, 0, 3) i; +INSERT INTO prt2_e SELECT i, i, i % 25 FROM generate_series(600, 799, 3) i; ANALYZE prt2_e; EXPLAIN (COSTS OFF) @@ -169,6 +201,128 @@ SELECT t1.a, t2.b FROM prt1 t1, prt2 t2 WHERE t1::text = t2::text AND t1.a = t2. RESET enable_hashjoin; RESET enable_nestloop; +-- test default partition behavior for range, partition-wise join is not +-- possible since more than one partition on one side matches default partition +-- on the other side. Default partition from prt1 matches default partition and +-- prt2_p4 from prt2 and default partition from prt2 matches default partition +-- and prt1_p0 from prt1 +ALTER TABLE prt1 DETACH PARTITION prt1_p3; +ALTER TABLE prt1 ATTACH PARTITION prt1_p3 DEFAULT; +ANALYZE prt1; +ALTER TABLE prt2 DETACH PARTITION prt2_p3; +ALTER TABLE prt2 ATTACH PARTITION prt2_p3 DEFAULT; +ANALYZE prt2; +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.b = 0 ORDER BY t1.a, t2.b; + +-- partition-wise join should be possible when we drop the first and last +-- partitions from both sides +ALTER TABLE prt1 DETACH PARTITION prt1_p0; +ALTER TABLE prt1 DETACH PARTITION prt1_p4; +ANALYZE prt1; +ALTER TABLE prt2 DETACH PARTITION prt2_p0; +ALTER TABLE prt2 DETACH PARTITION prt2_p4; +ANALYZE prt2; +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.b = 0 ORDER BY t1.a, t2.b; + +-- restore the partitioned tables for rest of the tests +ALTER TABLE prt1 ATTACH PARTITION prt1_p0 FOR VALUES FROM (MINVALUE) TO (0); +ALTER TABLE prt1 ATTACH PARTITION prt1_p4 FOR VALUES FROM (600) TO (800); +ALTER TABLE prt1 DETACH PARTITION prt1_p3; +ALTER TABLE prt1 ATTACH PARTITION prt1_p3 FOR VALUES FROM (500) TO (600); +ANALYZE prt1; +ALTER TABLE prt2 ATTACH PARTITION prt2_p0 FOR VALUES FROM (-250) TO (0); +ALTER TABLE prt2 ATTACH PARTITION prt2_p4 FOR VALUES FROM (600) TO (MAXVALUE); +ALTER TABLE prt2 DETACH PARTITION prt2_p3; +ALTER TABLE prt2 ATTACH PARTITION prt2_p3 FOR VALUES FROM (500) TO (600); +ANALYZE prt2; + +-- Add an extra partition to prt2 , Partition-wise join is possible with +-- extra partitions on inner side are allowed +DROP TABLE prt2_p4; +CREATE TABLE prt2_p4 PARTITION OF prt2 FOR VALUES FROM (600) TO (800); +CREATE TABLE prt2_p5 PARTITION OF prt2 FOR VALUES FROM (800) TO (1000); +INSERT INTO prt2 SELECT i % 25, i, to_char(i, 'FM0000') FROM generate_series(600, 999) i WHERE i % 3 = 0; +ANALYZE prt2; + +-- inner join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 INNER JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a; +SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 INNER JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a; + +-- left join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a; +SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a; + +-- semi join +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from prt1 t1 where exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c; +select t1.a, t1.b, t1.c from prt1 t1 where exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c; + +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from prt2 t1 where exists (select 1 from prt1 t2 WHERE t1.b = t2.a) and t1.a = 0 order by t1.a, t1.b, t1.c; +select t1.a, t1.b, t1.c from prt2 t1 where exists (select 1 from prt1 t2 WHERE t1.b = t2.a) and t1.a = 0 order by t1.a, t1.b, t1.c; + +-- anti join +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from prt1 t1 where not exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c; +select t1.a, t1.b, t1.c from prt1 t1 where not exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c; + +-- 3-way join when not every pair of joining relation can use partition-wise +-- join +EXPLAIN (COSTS OFF) +SELECT t1.a, t2.a, t3.c FROM prt1 t1 RIGHT JOIN prt2 t2 ON (t1.a = t2.b) INNER JOIN prt1 t3 ON (t2.b = t3.a) WHERE t2.a = 0 ORDER BY t1.a, t2.a, t3.c; +SELECT t1.a, t2.a, t3.c FROM prt1 t1 RIGHT JOIN prt2 t2 ON (t1.a = t2.b) INNER JOIN prt1 t3 ON (t2.b = t3.a) WHERE t2.a = 0 ORDER BY t1.a, t2.a, t3.c; + +-- partition-wise join can not handle missing partition on the inner side +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b WHERE t2.a = 0 ORDER BY t2.b; +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 FULL JOIN prt2 t2 ON t1.a = t2.b WHERE coalesce(t1.b, 0) + coalesce(t2.a, 0) = 0 ORDER BY t1.a, t2.a; +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from prt2 t1 where not exists (select 1 from prt1 t2 WHERE t1.b = t2.a) and t1.a = 0 order by t1.a, t1.b, t1.c; + +-- Partition-wise join can not handle the case when one partition from one side +-- matches with multiple partitions on the other side +DROP TABLE prt2_p4; +DROP TABLE prt2_p5; +CREATE TABLE prt2_p4 PARTITION OF prt2 FOR VALUES FROM (600) TO (700); +CREATE TABLE prt2_p5 PARTITION OF prt2 FOR VALUES FROM (700) TO (1000); +INSERT INTO prt2 SELECT i % 25, i, to_char(i, 'FM0000') FROM generate_series(600, 999, 3) i; +ANALYZE prt2; + +-- inner join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 INNER JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a; + +-- left join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b = 0 ORDER BY t1.a; + +-- right join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b WHERE t2.a = 0 ORDER BY t2.a; + +-- full join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM prt1 t1 FULL JOIN prt2 t2 ON t1.a = t2.b WHERE t1.b + t2.a = 0 ORDER BY t1.a, t2.a; + +-- semi join +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from prt1 t1 where exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c; + +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from prt2 t1 where exists (select 1 from prt1 t2 WHERE t1.b = t2.a) and t1.a = 0 order by t1.a, t1.b, t1.c; + +-- anti join +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from prt1 t1 where not exists (select 1 from prt2 t2 WHERE t1.a = t2.b) and t1.b = 0 order by t1.a, t1.b, t1.c; + +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from prt2 t1 where not exists (select 1 from prt1 t2 WHERE t1.b = t2.a) and t1.a = 0 order by t1.a, t1.b, t1.c; + -- -- partitioned by multiple columns -- @@ -193,28 +347,79 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_m WHERE prt1_m.c = 0) t1 -- -- tests for list partitioned tables. -- -CREATE TABLE plt1 (a int, b int, c text) PARTITION BY LIST(c); -CREATE TABLE plt1_p1 PARTITION OF plt1 FOR VALUES IN ('0000', '0003', '0004', '0010'); -CREATE TABLE plt1_p2 PARTITION OF plt1 FOR VALUES IN ('0001', '0005', '0002', '0009'); -CREATE TABLE plt1_p3 PARTITION OF plt1 FOR VALUES IN ('0006', '0007', '0008', '0011'); -INSERT INTO plt1 SELECT i, i, to_char(i/50, 'FM0000') FROM generate_series(0, 599, 2) i; -ANALYZE plt1; +\set part_mod 17 +\set cond_mod 47 +\set num_rows 500 + +CREATE TABLE plt1 (a int, b int, c varchar) PARTITION BY LIST(c); +CREATE TABLE plt1_p1 PARTITION OF plt1 FOR VALUES IN ('0001','0002','0003'); +CREATE TABLE plt1_p2 PARTITION OF plt1 FOR VALUES IN ('0004','0005','0006'); +CREATE TABLE plt1_p3 PARTITION OF plt1 FOR VALUES IN ('0008','0009'); +CREATE TABLE plt1_p4 PARTITION OF plt1 FOR VALUES IN ('0000','0010'); +INSERT INTO plt1 SELECT i, i % :cond_mod, to_char(i % :part_mod, 'FM0000') FROM generate_series(0, :num_rows) i WHERE i % :part_mod NOT IN (7, 11, 12, 13, 14, 15, 16); +ANALYSE plt1; + +-- plt2 have missing starting 0001, additional 0007, missing ending 0010 +-- and additional 0011 and 0012 bounds +CREATE TABLE plt2 (a int, b int, c varchar) PARTITION BY LIST(c); +CREATE TABLE plt2_p1 PARTITION OF plt2 FOR VALUES IN ('0002','0003'); +CREATE TABLE plt2_p2 PARTITION OF plt2 FOR VALUES IN ('0004','0005','0006'); +CREATE TABLE plt2_p3 PARTITION OF plt2 FOR VALUES IN ('0007','0008','0009'); +CREATE TABLE plt2_p4 PARTITION OF plt2 FOR VALUES IN ('0000','0011','0012'); +INSERT INTO plt2 SELECT i, i % :cond_mod, to_char(i % :part_mod, 'FM0000') FROM generate_series(0, :num_rows) i WHERE i % :part_mod NOT IN (1, 10, 13, 14, 15, 16); +ANALYSE plt2; + +-- Partition-wise-join is possible with some partition bounds overlap +-- with each other completely and some partialy for inner,left,right, +-- full, semi and anti joins -CREATE TABLE plt2 (a int, b int, c text) PARTITION BY LIST(c); -CREATE TABLE plt2_p1 PARTITION OF plt2 FOR VALUES IN ('0000', '0003', '0004', '0010'); -CREATE TABLE plt2_p2 PARTITION OF plt2 FOR VALUES IN ('0001', '0005', '0002', '0009'); -CREATE TABLE plt2_p3 PARTITION OF plt2 FOR VALUES IN ('0006', '0007', '0008', '0011'); -INSERT INTO plt2 SELECT i, i, to_char(i/50, 'FM0000') FROM generate_series(0, 599, 3) i; -ANALYZE plt2; +-- inner join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a; +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a; + +-- left join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a; +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a; + +-- right join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t2.a; +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t1.a; + +-- full join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a; +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a; + +-- semi join +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; +select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; +select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + +-- anti join +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; +select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; +select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; -- -- list partitioned by expression -- CREATE TABLE plt1_e (a int, b int, c text) PARTITION BY LIST(ltrim(c, 'A')); -CREATE TABLE plt1_e_p1 PARTITION OF plt1_e FOR VALUES IN ('0000', '0003', '0004', '0010'); -CREATE TABLE plt1_e_p2 PARTITION OF plt1_e FOR VALUES IN ('0001', '0005', '0002', '0009'); -CREATE TABLE plt1_e_p3 PARTITION OF plt1_e FOR VALUES IN ('0006', '0007', '0008', '0011'); -INSERT INTO plt1_e SELECT i, i, 'A' || to_char(i/50, 'FM0000') FROM generate_series(0, 599, 2) i; +CREATE TABLE plt1_e_p1 PARTITION OF plt1_e FOR VALUES IN ('0002', '0003'); +CREATE TABLE plt1_e_p2 PARTITION OF plt1_e FOR VALUES IN ('0004', '0005', '0006'); +CREATE TABLE plt1_e_p3 PARTITION OF plt1_e FOR VALUES IN ('0008', '0009'); +CREATE TABLE plt1_e_p4 PARTITION OF plt1_e FOR VALUES IN ('0000'); +INSERT INTO plt1_e SELECT i, i % :cond_mod, to_char(i % :part_mod, 'FM0000') FROM generate_series(0, :num_rows) i WHERE i % :part_mod NOT IN (1, 7, 10, 11, 12, 13, 14, 15, 16); ANALYZE plt1_e; -- test partition matching with N-way join @@ -222,6 +427,175 @@ EXPLAIN (COSTS OFF) SELECT avg(t1.a), avg(t2.b), avg(t3.a + t3.b), t1.c, t2.c, t3.c FROM plt1 t1, plt2 t2, plt1_e t3 WHERE t1.b = t2.b AND t1.c = t2.c AND ltrim(t3.c, 'A') = t1.c GROUP BY t1.c, t2.c, t3.c ORDER BY t1.c, t2.c, t3.c; SELECT avg(t1.a), avg(t2.b), avg(t3.a + t3.b), t1.c, t2.c, t3.c FROM plt1 t1, plt2 t2, plt1_e t3 WHERE t1.b = t2.b AND t1.c = t2.c AND ltrim(t3.c, 'A') = t1.c GROUP BY t1.c, t2.c, t3.c ORDER BY t1.c, t2.c, t3.c; +-- Add an extra partition to plt2 , Partition-wise join is possible with +-- partitions on inner side are allowed +CREATE TABLE plt2_p5 PARTITION OF plt2 FOR VALUES IN ('0013','0014'); +INSERT INTO plt2 SELECT i, i % :cond_mod, to_char(i % :part_mod, 'FM0000') FROM generate_series(0, :num_rows) i WHERE i % :part_mod IN (13, 14); +ANALYZE plt2; + +-- inner join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a; +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a; + +-- left join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a; +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a; + +-- right join, partition-wise join can not handle extra partition on the outer +-- side +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t2.a; +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t1.a; + +-- full join, partition-wise join can not handle extra partition on the outer +-- side +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a; +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a; + +-- semi join +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; +select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; +select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + +-- anti join +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; +select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; +select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + +-- Partition-wise join can not handle the case when one partition from one side +-- matches with multiple partitions on the other side +DROP TABLE plt2_p5; +CREATE TABLE plt2_p5 PARTITION OF plt2 FOR VALUES IN ('0001','0013','0014'); +INSERT INTO plt2 SELECT i, i % :cond_mod, to_char(i % :part_mod, 'FM0000') FROM generate_series(0, :num_rows) i WHERE i % :part_mod IN (1, 13, 14); +ANALYZE plt2; + +-- inner join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a; + +-- left join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a; + +-- right join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t2.a; + +-- full join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a; + +-- semi join +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + +-- anti join +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + +-- partition have a NULL on one side, Partition-wise join is possible with +-- NULL when NULL comparision is not strict i.e. NULL=NULL allowed +-- in this case NULL will be treated as addition partition bounds. +DROP TABLE plt2_p5; +DROP TABLE plt2_p4; +CREATE TABLE plt2_p4 PARTITION OF plt2 FOR VALUES IN ('0000',NULL,'0012'); +INSERT INTO plt2 SELECT i, i % :cond_mod, case when i % :part_mod = 11 then NULL else to_char(i % :part_mod, 'FM0000') end FROM generate_series(0, :num_rows) i WHERE i % :part_mod IN (0,11,12); +ANALYZE plt2; + +-- inner join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a; +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a; + +-- left join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a; +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a; + +-- right join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t2.a; +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t1.a; + +-- full join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a; +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a; + +-- semi join +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; +select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; +select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + +-- anti join +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; +select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; +select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + +-- partition have a NULL on both side with different partition bounds w.r.t other side +-- NULL when NULL comparision is not strict i.e. NULL=NULL allowed +-- Partition-wise join can not handle the case when one partition from one side +-- matches with multiple partitions on the other side +DROP TABLE plt1_p3; +CREATE TABLE plt1_p3 PARTITION OF plt1 FOR VALUES IN (NULL,'0008','0009'); +INSERT INTO plt1 SELECT i, i % :cond_mod, case when i % :part_mod = 7 then NULL else to_char(i % :part_mod, 'FM0000') end FROM generate_series(0, :num_rows) i WHERE i % :part_mod IN (7,8,9); +ANALYZE plt1; + +-- inner join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 INNER JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + t2.b = 0 ORDER BY t1.a; + +-- left join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.b + coalesce(t2.b, 0) = 0 ORDER BY t1.a; + +-- right join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + t2.b = 0 ORDER BY t2.a; + +-- full join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.a, t2.c FROM plt1 t1 FULL JOIN plt2 t2 ON t1.c = t2.c WHERE coalesce(t1.b, 0) + coalesce(t2.b, 0) = 0 ORDER BY t1.a, t2.a; + +-- semi join +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt1 t1 where exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt2 t1 where exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + +-- anti join +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt1 t1 where not exists (select 1 from plt2 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + +EXPLAIN (COSTS OFF) +select t1.a, t1.b, t1.c from plt2 t1 where not exists (select 1 from plt1 t2 WHERE t1.c = t2.c) and t1.b = 0 order by t1.a, t1.b, t1.c; + -- joins where one of the relations is proven empty EXPLAIN (COSTS OFF) SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.a = 1 AND t1.a = 2; @@ -267,27 +641,18 @@ EXPLAIN (COSTS OFF) SELECT avg(t1.a), avg(t2.b), avg(t3.a + t3.b), t1.c, t2.c, t3.c FROM pht1 t1, pht2 t2, pht1_e t3 WHERE t1.b = t2.b AND t1.c = t2.c AND ltrim(t3.c, 'A') = t1.c GROUP BY t1.c, t2.c, t3.c ORDER BY t1.c, t2.c, t3.c; SELECT avg(t1.a), avg(t2.b), avg(t3.a + t3.b), t1.c, t2.c, t3.c FROM pht1 t1, pht2 t2, pht1_e t3 WHERE t1.b = t2.b AND t1.c = t2.c AND ltrim(t3.c, 'A') = t1.c GROUP BY t1.c, t2.c, t3.c ORDER BY t1.c, t2.c, t3.c; --- test default partition behavior for range -ALTER TABLE prt1 DETACH PARTITION prt1_p3; -ALTER TABLE prt1 ATTACH PARTITION prt1_p3 DEFAULT; -ANALYZE prt1; -ALTER TABLE prt2 DETACH PARTITION prt2_p3; -ALTER TABLE prt2 ATTACH PARTITION prt2_p3 DEFAULT; -ANALYZE prt2; - -EXPLAIN (COSTS OFF) -SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.b = 0 ORDER BY t1.a, t2.b; - --- test default partition behavior for list +-- test default partition behavior for list, should not use partition-wise join +-- since default partition from one side matches multiple partitions on the +-- other ALTER TABLE plt1 DETACH PARTITION plt1_p3; ALTER TABLE plt1 ATTACH PARTITION plt1_p3 DEFAULT; ANALYZE plt1; ALTER TABLE plt2 DETACH PARTITION plt2_p3; ALTER TABLE plt2 ATTACH PARTITION plt2_p3 DEFAULT; ANALYZE plt2; - EXPLAIN (COSTS OFF) SELECT avg(t1.a), avg(t2.b), t1.c, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.c = t2.c WHERE t1.a % 25 = 0 GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c; + -- -- multiple levels of partitioning -- -- 2.19.2 --------------060602040206030304090709-- ^ permalink raw reply [nested|flat] 8+ messages in thread
* [PATCH 6/7] Change cluster to use the new pending sync infrastructure @ 2019-03-25 09:29 Kyotaro Horiguchi <[email protected]> 0 siblings, 0 replies; 8+ messages in thread From: Kyotaro Horiguchi @ 2019-03-25 09:29 UTC (permalink / raw) When wal_level is minimal, CLUSTER gets benefit by moving file sync from command end to transaction end by the pending-sync infrastructure that file sync is performed at commit time. --- src/backend/access/heap/rewriteheap.c | 25 +++++------------------- src/backend/catalog/storage.c | 36 +++++++++++++++++++++++++++++++++++ src/backend/commands/cluster.c | 13 +++++-------- src/include/access/rewriteheap.h | 2 +- src/include/catalog/storage.h | 3 ++- 5 files changed, 49 insertions(+), 30 deletions(-) diff --git a/src/backend/access/heap/rewriteheap.c b/src/backend/access/heap/rewriteheap.c index 1ac77f7c14..494f7fcd41 100644 --- a/src/backend/access/heap/rewriteheap.c +++ b/src/backend/access/heap/rewriteheap.c @@ -116,6 +116,7 @@ #include "access/xloginsert.h" #include "catalog/catalog.h" +#include "catalog/storage.h" #include "lib/ilist.h" @@ -144,7 +145,6 @@ typedef struct RewriteStateData Page rs_buffer; /* page currently being built */ BlockNumber rs_blockno; /* block where page will go */ bool rs_buffer_valid; /* T if any tuples in buffer */ - bool rs_use_wal; /* must we WAL-log inserts? */ bool rs_logical_rewrite; /* do we need to do logical rewriting */ TransactionId rs_oldest_xmin; /* oldest xmin used by caller to determine * tuple visibility */ @@ -238,15 +238,13 @@ static void logical_end_heap_rewrite(RewriteState state); * oldest_xmin xid used by the caller to determine which tuples are dead * freeze_xid xid before which tuples will be frozen * min_multi multixact before which multis will be removed - * use_wal should the inserts to the new heap be WAL-logged? * * Returns an opaque RewriteState, allocated in current memory context, * to be used in subsequent calls to the other functions. */ RewriteState begin_heap_rewrite(Relation old_heap, Relation new_heap, TransactionId oldest_xmin, - TransactionId freeze_xid, MultiXactId cutoff_multi, - bool use_wal) + TransactionId freeze_xid, MultiXactId cutoff_multi) { RewriteState state; MemoryContext rw_cxt; @@ -271,7 +269,6 @@ begin_heap_rewrite(Relation old_heap, Relation new_heap, TransactionId oldest_xm /* new_heap needn't be empty, just locked */ state->rs_blockno = RelationGetNumberOfBlocks(new_heap); state->rs_buffer_valid = false; - state->rs_use_wal = use_wal; state->rs_oldest_xmin = oldest_xmin; state->rs_freeze_xid = freeze_xid; state->rs_cutoff_multi = cutoff_multi; @@ -330,7 +327,7 @@ end_heap_rewrite(RewriteState state) /* Write the last page, if any */ if (state->rs_buffer_valid) { - if (state->rs_use_wal) + if (BlockNeedsWAL(state->rs_new_rel, state->rs_blockno)) log_newpage(&state->rs_new_rel->rd_node, MAIN_FORKNUM, state->rs_blockno, @@ -344,19 +341,7 @@ end_heap_rewrite(RewriteState state) (char *) state->rs_buffer, true); } - /* - * If the rel is WAL-logged, must fsync before commit. We use heap_sync - * to ensure that the toast table gets fsync'd too. - * - * It's obvious that we must do this when not WAL-logging. It's less - * obvious that we have to do it even if we did WAL-log the pages. The - * reason is the same as in tablecmds.c's copy_relation_data(): we're - * writing data that's not in shared buffers, and so a CHECKPOINT - * occurring during the rewriteheap operation won't have fsync'd data we - * wrote before the checkpoint. - */ - if (RelationNeedsWAL(state->rs_new_rel)) - heap_sync(state->rs_new_rel); + /* If we skipped using WAL, we will sync the relation at commit */ logical_end_heap_rewrite(state); @@ -692,7 +677,7 @@ raw_heap_insert(RewriteState state, HeapTuple tup) /* Doesn't fit, so write out the existing page */ /* XLOG stuff */ - if (state->rs_use_wal) + if (BlockNeedsWAL(state->rs_new_rel, state->rs_blockno)) log_newpage(&state->rs_new_rel->rd_node, MAIN_FORKNUM, state->rs_blockno, diff --git a/src/backend/catalog/storage.c b/src/backend/catalog/storage.c index a0cf8d3e27..cd623eb3bb 100644 --- a/src/backend/catalog/storage.c +++ b/src/backend/catalog/storage.c @@ -613,6 +613,42 @@ RecordWALSkipping(Relation rel) * must WAL-log any changes to the once-truncated blocks, because replaying * the truncation record will destroy them. */ +bool +BlockNeedsWAL(Relation rel, BlockNumber blkno) +{ + RelWalRequirement *walreq; + + if (!RelationNeedsWAL(rel)) + return false; + + /* fetch exising pending sync entry */ + walreq = getWalRequirementEntry(rel, false); + + /* + * no point in doing further work if we know that we don't have special + * WAL requirement + */ + if (!walreq) + return true; + + /* + * We don't skip WAL-logging for pages that once done. + */ + if (walreq->skip_wal_min_blk == InvalidBlockNumber || + walreq->skip_wal_min_blk > blkno) + return true; + + /* + * we don't skip WAL-logging for blocks that have got WAL-logged + * truncation + */ + if (walreq->wal_log_min_blk != InvalidBlockNumber && + walreq->wal_log_min_blk <= blkno) + return true; + + return false; +} + bool BufferNeedsWAL(Relation rel, Buffer buf) { diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c index 3e2a807640..e2c4897d07 100644 --- a/src/backend/commands/cluster.c +++ b/src/backend/commands/cluster.c @@ -767,7 +767,6 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, bool verbose, IndexScanDesc indexScan; TableScanDesc tableScan; HeapScanDesc heapScan; - bool use_wal; bool is_system_catalog; TransactionId OldestXmin; TransactionId FreezeXid; @@ -826,13 +825,11 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, bool verbose, LockRelationOid(OldHeap->rd_rel->reltoastrelid, AccessExclusiveLock); /* - * We need to log the copied data in WAL iff WAL archiving/streaming is - * enabled AND it's a WAL-logged rel. + * If wal_level is minimal, we skip WAL-logging even for WAL-requiring + * relations. Otherwise follow whether it's a WAL-logged rel. */ - use_wal = XLogIsNeeded() && RelationNeedsWAL(NewHeap); - - /* use_wal off requires smgr_targblock be initially invalid */ - Assert(RelationGetTargetBlock(NewHeap) == InvalidBlockNumber); + if (!XLogIsNeeded()) + heap_register_sync(NewHeap); /* * If both tables have TOAST tables, perform toast swap by content. It is @@ -899,7 +896,7 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, bool verbose, /* Initialize the rewrite operation */ rwstate = begin_heap_rewrite(OldHeap, NewHeap, OldestXmin, FreezeXid, - MultiXactCutoff, use_wal); + MultiXactCutoff); /* * Decide whether to use an indexscan or seqscan-and-optional-sort to scan diff --git a/src/include/access/rewriteheap.h b/src/include/access/rewriteheap.h index 6006249d96..64efecf48b 100644 --- a/src/include/access/rewriteheap.h +++ b/src/include/access/rewriteheap.h @@ -23,7 +23,7 @@ typedef struct RewriteStateData *RewriteState; extern RewriteState begin_heap_rewrite(Relation OldHeap, Relation NewHeap, TransactionId OldestXmin, TransactionId FreezeXid, - MultiXactId MultiXactCutoff, bool use_wal); + MultiXactId MultiXactCutoff); extern void end_heap_rewrite(RewriteState state); extern void rewrite_heap_tuple(RewriteState state, HeapTuple oldTuple, HeapTuple newTuple); diff --git a/src/include/catalog/storage.h b/src/include/catalog/storage.h index 76178b87f2..e8edbe5d71 100644 --- a/src/include/catalog/storage.h +++ b/src/include/catalog/storage.h @@ -33,7 +33,8 @@ extern int smgrGetPendingDeletes(bool forCommit, RelFileNode **ptr); extern void smgrDoPendingSyncs(bool isCommit); extern void smgrProcessWALRequirementInval(SubTransactionId sxid, bool isCommit); extern void RecordWALSkipping(Relation rel); -bool BufferNeedsWAL(Relation rel, Buffer buf); +extern bool BlockNeedsWAL(Relation rel, BlockNumber blkno); +extern bool BufferNeedsWAL(Relation rel, Buffer buf); extern void AtSubCommit_smgr(void); extern void AtSubAbort_smgr(void); extern void PostPrepare_smgr(void); -- 2.16.3 ----Next_Part(Mon_Mar_25_21_32_04_2019_838)-- Content-Type: Text/X-Patch; charset=us-ascii Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="v8-0007-Add-a-comment-to-ATExecSetTableSpace.patch" ^ permalink raw reply [nested|flat] 8+ messages in thread
* [PATCH v25 1/9] Row pattern recognition patch for raw parser. @ 2024-12-21 06:19 Tatsuo Ishii <[email protected]> 0 siblings, 0 replies; 8+ messages in thread From: Tatsuo Ishii @ 2024-12-21 06:19 UTC (permalink / raw) --- src/backend/parser/gram.y | 221 ++++++++++++++++++++++++++++++-- src/include/nodes/parsenodes.h | 67 ++++++++++ src/include/parser/kwlist.h | 8 ++ src/include/parser/parse_node.h | 1 + 4 files changed, 286 insertions(+), 11 deletions(-) diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 67eb96396a..d21dc28955 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -664,6 +664,21 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); json_object_constructor_null_clause_opt json_array_constructor_null_clause_opt +%type <target> row_pattern_measure_item + row_pattern_definition +%type <node> opt_row_pattern_common_syntax + opt_row_pattern_skip_to + row_pattern_subset_item + row_pattern_term +%type <list> opt_row_pattern_measures + row_pattern_measure_list + row_pattern_definition_list + opt_row_pattern_subset_clause + row_pattern_subset_list + row_pattern_subset_rhs + row_pattern +%type <boolean> opt_row_pattern_initial_or_seek + first_or_last /* * Non-keyword token types. These are hard-wired into the "flex" lexer. @@ -707,7 +722,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); CURRENT_TIME CURRENT_TIMESTAMP CURRENT_USER CURSOR CYCLE DATA_P DATABASE DAY_P DEALLOCATE DEC DECIMAL_P DECLARE DEFAULT DEFAULTS - DEFERRABLE DEFERRED DEFINER DELETE_P DELIMITER DELIMITERS DEPENDS DEPTH DESC + DEFERRABLE DEFERRED DEFINE DEFINER DELETE_P DELIMITER DELIMITERS DEPENDS DEPTH DESC DETACH DICTIONARY DISABLE_P DISCARD DISTINCT DO DOCUMENT_P DOMAIN_P DOUBLE_P DROP @@ -723,7 +738,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); HANDLER HAVING HEADER_P HOLD HOUR_P IDENTITY_P IF_P ILIKE IMMEDIATE IMMUTABLE IMPLICIT_P IMPORT_P IN_P INCLUDE - INCLUDING INCREMENT INDENT INDEX INDEXES INHERIT INHERITS INITIALLY INLINE_P + INCLUDING INCREMENT INDENT INDEX INDEXES INHERIT INHERITS INITIAL INITIALLY INLINE_P INNER_P INOUT INPUT_P INSENSITIVE INSERT INSTEAD INT_P INTEGER INTERSECT INTERVAL INTO INVOKER IS ISNULL ISOLATION @@ -736,7 +751,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); LEADING LEAKPROOF LEAST LEFT LEVEL LIKE LIMIT LISTEN LOAD LOCAL LOCALTIME LOCALTIMESTAMP LOCATION LOCK_P LOCKED LOGGED - MAPPING MATCH MATCHED MATERIALIZED MAXVALUE MERGE MERGE_ACTION METHOD + MAPPING MATCH MATCHED MATERIALIZED MAXVALUE MEASURES MERGE MERGE_ACTION METHOD MINUTE_P MINVALUE MODE MONTH_P MOVE NAME_P NAMES NATIONAL NATURAL NCHAR NESTED NEW NEXT NFC NFD NFKC NFKD NO @@ -748,8 +763,9 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); ORDER ORDINALITY OTHERS OUT_P OUTER_P OVER OVERLAPS OVERLAY OVERRIDING OWNED OWNER - PARALLEL PARAMETER PARSER PARTIAL PARTITION PASSING PASSWORD PATH - PERIOD PLACING PLAN PLANS POLICY + PARALLEL PARAMETER PARSER PARTIAL PARTITION PASSING PASSWORD PAST + PATH PATTERN_P PERIOD PERMUTE PLACING PLAN PLANS POLICY + POSITION PRECEDING PRECISION PRESERVE PREPARE PREPARED PRIMARY PRIOR PRIVILEGES PROCEDURAL PROCEDURE PROCEDURES PROGRAM PUBLICATION @@ -760,12 +776,13 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); RESET RESTART RESTRICT RETURN RETURNING RETURNS REVOKE RIGHT ROLE ROLLBACK ROLLUP ROUTINE ROUTINES ROW ROWS RULE - SAVEPOINT SCALAR SCHEMA SCHEMAS SCROLL SEARCH SECOND_P SECURITY SELECT + SAVEPOINT SCALAR SCHEMA SCHEMAS SCROLL SEARCH SECOND_P SECURITY SEEK SELECT SEQUENCE SEQUENCES + SERIALIZABLE SERVER SESSION SESSION_USER SET SETS SETOF SHARE SHOW SIMILAR SIMPLE SKIP SMALLINT SNAPSHOT SOME SOURCE SQL_P STABLE STANDALONE_P START STATEMENT STATISTICS STDIN STDOUT STORAGE STORED STRICT_P STRING_P STRIP_P - SUBSCRIPTION SUBSTRING SUPPORT SYMMETRIC SYSID SYSTEM_P SYSTEM_USER + SUBSCRIPTION SUBSET SUBSTRING SUPPORT SYMMETRIC SYSID SYSTEM_P SYSTEM_USER TABLE TABLES TABLESAMPLE TABLESPACE TARGET TEMP TEMPLATE TEMPORARY TEXT_P THEN TIES TIME TIMESTAMP TO TRAILING TRANSACTION TRANSFORM @@ -874,6 +891,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); %nonassoc UNBOUNDED NESTED /* ideally would have same precedence as IDENT */ %nonassoc IDENT PARTITION RANGE ROWS GROUPS PRECEDING FOLLOWING CUBE ROLLUP SET KEYS OBJECT_P SCALAR VALUE_P WITH WITHOUT PATH + MEASURES AFTER INITIAL SEEK PATTERN_P %left Op OPERATOR /* multi-character ops and user-defined operators */ %left '+' '-' %left '*' '/' '%' @@ -16326,7 +16344,8 @@ over_clause: OVER window_specification ; window_specification: '(' opt_existing_window_name opt_partition_clause - opt_sort_clause opt_frame_clause ')' + opt_sort_clause opt_row_pattern_measures opt_frame_clause + opt_row_pattern_common_syntax ')' { WindowDef *n = makeNode(WindowDef); @@ -16334,10 +16353,12 @@ window_specification: '(' opt_existing_window_name opt_partition_clause n->refname = $2; n->partitionClause = $3; n->orderClause = $4; + n->rowPatternMeasures = $5; /* copy relevant fields of opt_frame_clause */ - n->frameOptions = $5->frameOptions; - n->startOffset = $5->startOffset; - n->endOffset = $5->endOffset; + n->frameOptions = $6->frameOptions; + n->startOffset = $6->startOffset; + n->endOffset = $6->endOffset; + n->rpCommonSyntax = (RPCommonSyntax *)$7; n->location = @1; $$ = n; } @@ -16361,6 +16382,31 @@ opt_partition_clause: PARTITION BY expr_list { $$ = $3; } | /*EMPTY*/ { $$ = NIL; } ; +/* + * ROW PATTERN_P MEASURES + */ +opt_row_pattern_measures: MEASURES row_pattern_measure_list { $$ = $2; } + | /*EMPTY*/ { $$ = NIL; } + ; + +row_pattern_measure_list: + row_pattern_measure_item + { $$ = list_make1($1); } + | row_pattern_measure_list ',' row_pattern_measure_item + { $$ = lappend($1, $3); } + ; + +row_pattern_measure_item: + a_expr AS ColLabel + { + $$ = makeNode(ResTarget); + $$->name = $3; + $$->indirection = NIL; + $$->val = (Node *) $1; + $$->location = @1; + } + ; + /* * For frame clauses, we return a WindowDef, but only some fields are used: * frameOptions, startOffset, and endOffset. @@ -16520,6 +16566,143 @@ opt_window_exclusion_clause: | /*EMPTY*/ { $$ = 0; } ; +opt_row_pattern_common_syntax: +opt_row_pattern_skip_to opt_row_pattern_initial_or_seek + PATTERN_P '(' row_pattern ')' + opt_row_pattern_subset_clause + DEFINE row_pattern_definition_list + { + RPCommonSyntax *n = makeNode(RPCommonSyntax); + n->rpSkipTo = ((RPCommonSyntax *)$1)->rpSkipTo; + n->rpSkipVariable = ((RPCommonSyntax *)$1)->rpSkipVariable; + n->initial = $2; + n->rpPatterns = $5; + n->rpSubsetClause = $7; + n->rpDefs = $9; + $$ = (Node *) n; + } + | /*EMPTY*/ { $$ = NULL; } + ; + +opt_row_pattern_skip_to: + AFTER MATCH SKIP TO NEXT ROW + { + RPCommonSyntax *n = makeNode(RPCommonSyntax); + n->rpSkipTo = ST_NEXT_ROW; + n->rpSkipVariable = NULL; + $$ = (Node *) n; + } + | AFTER MATCH SKIP PAST LAST_P ROW + { + RPCommonSyntax *n = makeNode(RPCommonSyntax); + n->rpSkipTo = ST_PAST_LAST_ROW; + n->rpSkipVariable = NULL; + $$ = (Node *) n; + } + | AFTER MATCH SKIP TO first_or_last ColId + { + RPCommonSyntax *n = makeNode(RPCommonSyntax); + n->rpSkipTo = $5? ST_FIRST_VARIABLE : ST_LAST_VARIABLE; + n->rpSkipVariable = $6; + $$ = (Node *) n; + } +/* + | AFTER MATCH SKIP TO LAST_P ColId %prec LAST_P + { + RPCommonSyntax *n = makeNode(RPCommonSyntax); + n->rpSkipTo = ST_LAST_VARIABLE; + n->rpSkipVariable = $6; + $$ = n; + } + | AFTER MATCH SKIP TO ColId + { + RPCommonSyntax *n = makeNode(RPCommonSyntax); + n->rpSkipTo = ST_VARIABLE; + n->rpSkipVariable = $5; + $$ = n; + } +*/ + | /*EMPTY*/ + { + RPCommonSyntax *n = makeNode(RPCommonSyntax); + /* temporary set default to ST_NEXT_ROW */ + n->rpSkipTo = ST_PAST_LAST_ROW; + n->rpSkipVariable = NULL; + $$ = (Node *) n; + } + ; + +first_or_last: + FIRST_P { $$ = true; } + | LAST_P { $$ = false; } + ; + +opt_row_pattern_initial_or_seek: + INITIAL { $$ = true; } + | SEEK + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("SEEK is not supported"), + errhint("Use INITIAL."), + parser_errposition(@1))); + } + | /*EMPTY*/ { $$ = true; } + ; + +row_pattern: + row_pattern_term { $$ = list_make1($1); } + | row_pattern row_pattern_term { $$ = lappend($1, $2); } + ; + +row_pattern_term: + ColId { $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "", (Node *)makeString($1), NULL, @1); } + | ColId '*' { $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "*", (Node *)makeString($1), NULL, @1); } + | ColId '+' { $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "+", (Node *)makeString($1), NULL, @1); } + | ColId '?' { $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "?", (Node *)makeString($1), NULL, @1); } + ; + +opt_row_pattern_subset_clause: + SUBSET row_pattern_subset_list { $$ = $2; } + | /*EMPTY*/ { $$ = NIL; } + ; + +row_pattern_subset_list: + row_pattern_subset_item { $$ = list_make1($1); } + | row_pattern_subset_list ',' row_pattern_subset_item { $$ = lappend($1, $3); } + | /*EMPTY*/ { $$ = NIL; } + ; + +row_pattern_subset_item: ColId '=' '(' row_pattern_subset_rhs ')' + { + RPSubsetItem *n = makeNode(RPSubsetItem); + n->name = $1; + n->rhsVariable = $4; + $$ = (Node *) n; + } + ; + +row_pattern_subset_rhs: + ColId { $$ = list_make1(makeStringConst($1, @1)); } + | row_pattern_subset_rhs ',' ColId { $$ = lappend($1, makeStringConst($3, @1)); } + | /*EMPTY*/ { $$ = NIL; } + ; + +row_pattern_definition_list: + row_pattern_definition { $$ = list_make1($1); } + | row_pattern_definition_list ',' row_pattern_definition { $$ = lappend($1, $3); } + ; + +row_pattern_definition: + ColId AS a_expr + { + $$ = makeNode(ResTarget); + $$->name = $1; + $$->indirection = NIL; + $$->val = (Node *) $3; + $$->location = @1; + } + ; /* * Supporting nonterminals for expressions. @@ -17732,6 +17915,7 @@ unreserved_keyword: | INDEXES | INHERIT | INHERITS + | INITIAL | INLINE_P | INPUT_P | INSENSITIVE @@ -17760,6 +17944,7 @@ unreserved_keyword: | MATCHED | MATERIALIZED | MAXVALUE + | MEASURES | MERGE | METHOD | MINUTE_P @@ -17804,8 +17989,11 @@ unreserved_keyword: | PARTITION | PASSING | PASSWORD + | PAST | PATH + | PATTERN_P | PERIOD + | PERMUTE | PLAN | PLANS | POLICY @@ -17857,6 +18045,7 @@ unreserved_keyword: | SEARCH | SECOND_P | SECURITY + | SEEK | SEQUENCE | SEQUENCES | SERIALIZABLE @@ -17884,6 +18073,7 @@ unreserved_keyword: | STRING_P | STRIP_P | SUBSCRIPTION + | SUBSET | SUPPORT | SYSID | SYSTEM_P @@ -18078,6 +18268,7 @@ reserved_keyword: | CURRENT_USER | DEFAULT | DEFERRABLE + | DEFINE | DESC | DISTINCT | DO @@ -18241,6 +18432,7 @@ bare_label_keyword: | DEFAULTS | DEFERRABLE | DEFERRED + | DEFINE | DEFINER | DELETE_P | DELIMITER @@ -18318,6 +18510,7 @@ bare_label_keyword: | INDEXES | INHERIT | INHERITS + | INITIAL | INITIALLY | INLINE_P | INNER_P @@ -18372,6 +18565,7 @@ bare_label_keyword: | MATCHED | MATERIALIZED | MAXVALUE + | MEASURES | MERGE | MERGE_ACTION | METHOD @@ -18428,8 +18622,11 @@ bare_label_keyword: | PARTITION | PASSING | PASSWORD + | PAST | PATH + | PATTERN_P | PERIOD + | PERMUTE | PLACING | PLAN | PLANS @@ -18487,6 +18684,7 @@ bare_label_keyword: | SCROLL | SEARCH | SECURITY + | SEEK | SELECT | SEQUENCE | SEQUENCES @@ -18520,6 +18718,7 @@ bare_label_keyword: | STRING_P | STRIP_P | SUBSCRIPTION + | SUBSET | SUBSTRING | SUPPORT | SYMMETRIC diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 0f9462493e..defc62bf9b 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -552,6 +552,48 @@ typedef struct SortBy ParseLoc location; /* operator location, or -1 if none/unknown */ } SortBy; +/* + * AFTER MATCH row pattern skip to types in row pattern common syntax + */ +typedef enum RPSkipTo +{ + ST_NONE, /* AFTER MATCH omitted */ + ST_NEXT_ROW, /* SKIP TO NEXT ROW */ + ST_PAST_LAST_ROW, /* SKIP TO PAST LAST ROW */ + ST_FIRST_VARIABLE, /* SKIP TO FIRST variable name */ + ST_LAST_VARIABLE, /* SKIP TO LAST variable name */ + ST_VARIABLE /* SKIP TO variable name */ +} RPSkipTo; + +/* + * Row Pattern SUBSET clause item + */ +typedef struct RPSubsetItem +{ + NodeTag type; + char *name; /* Row Pattern SUBSET clause variable name */ + List *rhsVariable; /* Row Pattern SUBSET rhs variables (list of + * char *string) */ +} RPSubsetItem; + +/* + * RowPatternCommonSyntax - raw representation of row pattern common syntax + * + */ +typedef struct RPCommonSyntax +{ + NodeTag type; + RPSkipTo rpSkipTo; /* Row Pattern AFTER MATCH SKIP type */ + char *rpSkipVariable; /* Row Pattern Skip To variable name, if any */ + bool initial; /* true if <row pattern initial or seek> is + * initial */ + List *rpPatterns; /* PATTERN variables (list of A_Expr) */ + List *rpSubsetClause; /* row pattern subset clause (list of + * RPSubsetItem), if any */ + List *rpDefs; /* row pattern definitions clause (list of + * ResTarget) */ +} RPCommonSyntax; + /* * WindowDef - raw representation of WINDOW and OVER clauses * @@ -567,6 +609,9 @@ typedef struct WindowDef char *refname; /* referenced window name, if any */ List *partitionClause; /* PARTITION BY expression list */ List *orderClause; /* ORDER BY (list of SortBy) */ + List *rowPatternMeasures; /* row pattern measures (list of + * ResTarget) */ + RPCommonSyntax *rpCommonSyntax; /* row pattern common syntax */ int frameOptions; /* frame_clause options, see below */ Node *startOffset; /* expression for starting bound, if any */ Node *endOffset; /* expression for ending bound, if any */ @@ -1530,6 +1575,11 @@ typedef struct GroupingSet * the orderClause might or might not be copied (see copiedOrder); the framing * options are never copied, per spec. * + * "defineClause" is Row Pattern Recognition DEFINE clause (list of + * TargetEntry). TargetEntry.resname represents row pattern definition + * variable name. "patternVariable" and "patternRegexp" represents PATTERN + * clause. + * * The information relevant for the query jumbling is the partition clause * type and its bounds. */ @@ -1559,6 +1609,23 @@ typedef struct WindowClause Index winref; /* ID referenced by window functions */ /* did we copy orderClause from refname? */ bool copiedOrder pg_node_attr(query_jumble_ignore); + /* Row Pattern AFTER MACH SKIP clause */ + RPSkipTo rpSkipTo; /* Row Pattern Skip To type */ + char *rpSkipVariable; /* Row Pattern Skip To variable */ + bool initial; /* true if <row pattern initial or seek> is + * initial */ + /* Row Pattern DEFINE clause (list of TargetEntry) */ + List *defineClause; + /* Row Pattern DEFINE variable initial names (list of String) */ + List *defineInitial; + /* Row Pattern PATTERN variable name (list of String) */ + List *patternVariable; + + /* + * Row Pattern PATTERN regular expression quantifier ('+' or ''. list of + * String) + */ + List *patternRegexp; } WindowClause; /* diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h index 899d64ad55..f52d797615 100644 --- a/src/include/parser/kwlist.h +++ b/src/include/parser/kwlist.h @@ -129,6 +129,7 @@ PG_KEYWORD("default", DEFAULT, RESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("defaults", DEFAULTS, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("deferrable", DEFERRABLE, RESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("deferred", DEFERRED, UNRESERVED_KEYWORD, BARE_LABEL) +PG_KEYWORD("define", DEFINE, RESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("definer", DEFINER, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("delete", DELETE_P, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("delimiter", DELIMITER, UNRESERVED_KEYWORD, BARE_LABEL) @@ -215,6 +216,7 @@ PG_KEYWORD("index", INDEX, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("indexes", INDEXES, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("inherit", INHERIT, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("inherits", INHERITS, UNRESERVED_KEYWORD, BARE_LABEL) +PG_KEYWORD("initial", INITIAL, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("initially", INITIALLY, RESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("inline", INLINE_P, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("inner", INNER_P, TYPE_FUNC_NAME_KEYWORD, BARE_LABEL) @@ -273,6 +275,7 @@ PG_KEYWORD("match", MATCH, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("matched", MATCHED, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("materialized", MATERIALIZED, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("maxvalue", MAXVALUE, UNRESERVED_KEYWORD, BARE_LABEL) +PG_KEYWORD("measures", MEASURES, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("merge", MERGE, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("merge_action", MERGE_ACTION, COL_NAME_KEYWORD, BARE_LABEL) PG_KEYWORD("method", METHOD, UNRESERVED_KEYWORD, BARE_LABEL) @@ -337,8 +340,11 @@ PG_KEYWORD("partial", PARTIAL, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("partition", PARTITION, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("passing", PASSING, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("password", PASSWORD, UNRESERVED_KEYWORD, BARE_LABEL) +PG_KEYWORD("past", PAST, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("path", PATH, UNRESERVED_KEYWORD, BARE_LABEL) +PG_KEYWORD("pattern", PATTERN_P, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("period", PERIOD, UNRESERVED_KEYWORD, BARE_LABEL) +PG_KEYWORD("permute", PERMUTE, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("placing", PLACING, RESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("plan", PLAN, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("plans", PLANS, UNRESERVED_KEYWORD, BARE_LABEL) @@ -399,6 +405,7 @@ PG_KEYWORD("scroll", SCROLL, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("search", SEARCH, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("second", SECOND_P, UNRESERVED_KEYWORD, AS_LABEL) PG_KEYWORD("security", SECURITY, UNRESERVED_KEYWORD, BARE_LABEL) +PG_KEYWORD("seek", SEEK, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("select", SELECT, RESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("sequence", SEQUENCE, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("sequences", SEQUENCES, UNRESERVED_KEYWORD, BARE_LABEL) @@ -432,6 +439,7 @@ PG_KEYWORD("strict", STRICT_P, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("string", STRING_P, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("strip", STRIP_P, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("subscription", SUBSCRIPTION, UNRESERVED_KEYWORD, BARE_LABEL) +PG_KEYWORD("subset", SUBSET, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("substring", SUBSTRING, COL_NAME_KEYWORD, BARE_LABEL) PG_KEYWORD("support", SUPPORT, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("symmetric", SYMMETRIC, RESERVED_KEYWORD, BARE_LABEL) diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h index 2375e95c10..737f8a9e0e 100644 --- a/src/include/parser/parse_node.h +++ b/src/include/parser/parse_node.h @@ -51,6 +51,7 @@ typedef enum ParseExprKind EXPR_KIND_WINDOW_FRAME_RANGE, /* window frame clause with RANGE */ EXPR_KIND_WINDOW_FRAME_ROWS, /* window frame clause with ROWS */ EXPR_KIND_WINDOW_FRAME_GROUPS, /* window frame clause with GROUPS */ + EXPR_KIND_RPR_DEFINE, /* DEFINE */ EXPR_KIND_SELECT_TARGET, /* SELECT target list item */ EXPR_KIND_INSERT_TARGET, /* INSERT target list item */ EXPR_KIND_UPDATE_SOURCE, /* UPDATE assignment source item */ -- 2.25.1 ----Next_Part(Sat_Dec_21_18_20_04_2024_526)-- Content-Type: Text/X-Patch; charset=us-ascii Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="v25-0002-Row-pattern-recognition-patch-parse-analysis.patch" ^ permalink raw reply [nested|flat] 8+ messages in thread
* Re: [PATCH] Fix incorrect range in pg_regress comment @ 2025-02-02 22:01 Tom Lane <[email protected]> 0 siblings, 1 reply; 8+ messages in thread From: Tom Lane @ 2025-02-02 22:01 UTC (permalink / raw) To: Jelte Fennema-Nio <[email protected]>; +Cc: Ilia Evdokimov <[email protected]>; pgsql-hackers Jelte Fennema-Nio <[email protected]> writes: > On Sun, 2 Feb 2025 at 22:26, Tom Lane <[email protected]> wrote: >> Hmm, our convention is definitely that the numbers start with 1, >> so I do not want to make this change. Maybe we should change >> the code instead. > That would require any extensions that use the _0.out suffix to update > all those files to use _1.out as the suffix. One such extension is > Citus[1]. Oh. I see we did document it as 0-9 in [1], so I guess we're stuck with that now. Objection withdrawn. regards, tom lane [1] https://www.postgresql.org/docs/devel/regress-variant.html ^ permalink raw reply [nested|flat] 8+ messages in thread
* Re: [PATCH] Fix incorrect range in pg_regress comment @ 2025-02-03 00:34 Michael Paquier <[email protected]> parent: Tom Lane <[email protected]> 0 siblings, 1 reply; 8+ messages in thread From: Michael Paquier @ 2025-02-03 00:34 UTC (permalink / raw) To: Tom Lane <[email protected]>; +Cc: Jelte Fennema-Nio <[email protected]>; Ilia Evdokimov <[email protected]>; pgsql-hackers On Sun, Feb 02, 2025 at 05:01:33PM -0500, Tom Lane wrote: > Oh. I see we did document it as 0-9 in [1], so I guess we're > stuck with that now. Objection withdrawn. Oh. I didn't know that 0 was accepted. We learn new things every day. Right, let's adjust the comment to reflect what the docs say, as your patch does. I presume that Tom will do that.. -- Michael Attachments: [application/pgp-signature] signature.asc (833B, ../../[email protected]/2-signature.asc) download ^ permalink raw reply [nested|flat] 8+ messages in thread
* Re: [PATCH] Fix incorrect range in pg_regress comment @ 2025-02-03 03:38 Tom Lane <[email protected]> parent: Michael Paquier <[email protected]> 0 siblings, 1 reply; 8+ messages in thread From: Tom Lane @ 2025-02-03 03:38 UTC (permalink / raw) To: Michael Paquier <[email protected]>; +Cc: Jelte Fennema-Nio <[email protected]>; Ilia Evdokimov <[email protected]>; pgsql-hackers Michael Paquier <[email protected]> writes: > Right, let's adjust the comment to reflect what the docs say, as your > patch does. I presume that Tom will do that.. If I complain about it I gotta fix it, huh? Okay, done. regards, tom lane ^ permalink raw reply [nested|flat] 8+ messages in thread
* Re: [PATCH] Fix incorrect range in pg_regress comment @ 2025-02-04 00:35 Michael Paquier <[email protected]> parent: Tom Lane <[email protected]> 0 siblings, 0 replies; 8+ messages in thread From: Michael Paquier @ 2025-02-04 00:35 UTC (permalink / raw) To: Tom Lane <[email protected]>; +Cc: Jelte Fennema-Nio <[email protected]>; Ilia Evdokimov <[email protected]>; pgsql-hackers On Sun, Feb 02, 2025 at 10:38:32PM -0500, Tom Lane wrote: > If I complain about it I gotta fix it, huh? Okay, done. Thanks. :D -- Michael Attachments: [application/pgp-signature] signature.asc (833B, ../../[email protected]/2-signature.asc) download ^ permalink raw reply [nested|flat] 8+ messages in thread
end of thread, other threads:[~2025-02-04 00:35 UTC | newest] Thread overview: 8+ messages (download: mbox mbox.gz follow: Atom feed) -- links below jump to the message on this page -- 2019-01-22 12:28 [PATCH 3/3] Tests for 0:1, 1:1 and 1:0 partition matching Etsuro Fujita <[email protected]> 2019-01-31 11:31 [PATCH 3/3] Tests for 0:1, 1:1 and 1:0 partition matching Etsuro Fujita <[email protected]> 2019-03-25 09:29 [PATCH 6/7] Change cluster to use the new pending sync infrastructure Kyotaro Horiguchi <[email protected]> 2024-12-21 06:19 [PATCH v25 1/9] Row pattern recognition patch for raw parser. Tatsuo Ishii <[email protected]> 2025-02-02 22:01 Re: [PATCH] Fix incorrect range in pg_regress comment Tom Lane <[email protected]> 2025-02-03 00:34 ` Re: [PATCH] Fix incorrect range in pg_regress comment Michael Paquier <[email protected]> 2025-02-03 03:38 ` Re: [PATCH] Fix incorrect range in pg_regress comment Tom Lane <[email protected]> 2025-02-04 00:35 ` Re: [PATCH] Fix incorrect range in pg_regress comment Michael Paquier <[email protected]>
This inbox is served by agora; see mirroring instructions for how to clone and mirror all data and code used for this inbox