agora inbox for [email protected]help / color / mirror / Atom feed
[PATCH 3/3] Tests for 0:1, 1:1 and 1:0 partition matching 6+ messages / 4 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; 6+ 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] 6+ 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; 6+ 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] 6+ messages in thread
* [PATCH 2/3] Refactor create_limit_path() to share cost adjustment code @ 2019-03-06 11:28 Etsuro Fujita <[email protected]> 0 siblings, 0 replies; 6+ messages in thread From: Etsuro Fujita @ 2019-03-06 11:28 UTC (permalink / raw) --- src/backend/optimizer/util/pathnode.c | 87 +++++++++++++++++---------- src/include/optimizer/pathnode.h | 3 + 2 files changed, 58 insertions(+), 32 deletions(-) diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c index 169e51e792..9fd0ea4aba 100644 --- a/src/backend/optimizer/util/pathnode.c +++ b/src/backend/optimizer/util/pathnode.c @@ -3550,17 +3550,42 @@ create_limit_path(PlannerInfo *root, RelOptInfo *rel, /* * Adjust the output rows count and costs according to the offset/limit. - * This is only a cosmetic issue if we are at top level, but if we are - * building a subquery then it's important to report correct info to the - * outer planner. - * - * When the offset or count couldn't be estimated, use 10% of the - * estimated number of rows emitted from the subpath. - * - * XXX we don't bother to add eval costs of the offset/limit expressions - * themselves to the path costs. In theory we should, but in most cases - * those expressions are trivial and it's just not worth the trouble. */ + adjust_limit_rows_costs(&pathnode->path.rows, + &pathnode->path.startup_cost, + &pathnode->path.total_cost, + offset_est, count_est); + + return pathnode; +} + +/* + * adjust_limit_rows_costs + * Adjust the size and cost estimates for a LimitPath node according to the + * offset/limit. + * + * This is only a cosmetic issue if we are at top level, but if we are + * building a subquery then it's important to report correct info to the outer + * planner. + * + * When the offset or count couldn't be estimated, use 10% of the estimated + * number of rows emitted from the subpath. + * + * XXX we don't bother to add eval costs of the offset/limit expressions + * themselves to the path costs. In theory we should, but in most cases those + * expressions are trivial and it's just not worth the trouble. + */ +void +adjust_limit_rows_costs(double *rows, /* in/out parameter */ + Cost *startup_cost, /* in/out parameter */ + Cost *total_cost, /* in/out parameter */ + int64 offset_est, + int64 count_est) +{ + double input_rows = *rows; + Cost input_startup_cost = *startup_cost; + Cost input_total_cost = *total_cost; + if (offset_est != 0) { double offset_rows; @@ -3568,16 +3593,16 @@ create_limit_path(PlannerInfo *root, RelOptInfo *rel, if (offset_est > 0) offset_rows = (double) offset_est; else - offset_rows = clamp_row_est(subpath->rows * 0.10); - if (offset_rows > pathnode->path.rows) - offset_rows = pathnode->path.rows; - if (subpath->rows > 0) - pathnode->path.startup_cost += - (subpath->total_cost - subpath->startup_cost) - * offset_rows / subpath->rows; - pathnode->path.rows -= offset_rows; - if (pathnode->path.rows < 1) - pathnode->path.rows = 1; + offset_rows = clamp_row_est(input_rows * 0.10); + if (offset_rows > *rows) + offset_rows = *rows; + if (input_rows > 0) + *startup_cost += + (input_total_cost - input_startup_cost) + * offset_rows / input_rows; + *rows -= offset_rows; + if (*rows < 1) + *rows = 1; } if (count_est != 0) @@ -3587,19 +3612,17 @@ create_limit_path(PlannerInfo *root, RelOptInfo *rel, if (count_est > 0) count_rows = (double) count_est; else - count_rows = clamp_row_est(subpath->rows * 0.10); - if (count_rows > pathnode->path.rows) - count_rows = pathnode->path.rows; - if (subpath->rows > 0) - pathnode->path.total_cost = pathnode->path.startup_cost + - (subpath->total_cost - subpath->startup_cost) - * count_rows / subpath->rows; - pathnode->path.rows = count_rows; - if (pathnode->path.rows < 1) - pathnode->path.rows = 1; + count_rows = clamp_row_est(input_rows * 0.10); + if (count_rows > *rows) + count_rows = *rows; + if (input_rows > 0) + *total_cost = *startup_cost + + (input_total_cost - input_startup_cost) + * count_rows / input_rows; + *rows = count_rows; + if (*rows < 1) + *rows = 1; } - - return pathnode; } diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h index 574bb85b50..95609aad53 100644 --- a/src/include/optimizer/pathnode.h +++ b/src/include/optimizer/pathnode.h @@ -265,6 +265,9 @@ extern LimitPath *create_limit_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath, Node *limitOffset, Node *limitCount, int64 offset_est, int64 count_est); +extern void adjust_limit_rows_costs(double *rows, + Cost *startup_cost, Cost *total_cost, + int64 offset_est, int64 count_est); extern Path *reparameterize_path(PlannerInfo *root, Path *path, Relids required_outer, -- 2.19.2 --------------060600050702040203040200 Content-Type: text/x-patch; name="v5-0003-postgres_fdw-Perform-UPPERREL_FINAL-step-remotely.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename*0="v5-0003-postgres_fdw-Perform-UPPERREL_FINAL-step-remotely.pa"; filename*1="tch" ^ permalink raw reply [nested|flat] 6+ messages in thread
* [PATCH v13 1/8] Row pattern recognition patch for raw parser. @ 2024-01-22 09:45 Tatsuo Ishii <[email protected]> 0 siblings, 0 replies; 6+ messages in thread From: Tatsuo Ishii @ 2024-01-22 09:45 UTC (permalink / raw) --- src/backend/parser/gram.y | 220 ++++++++++++++++++++++++++++++-- src/include/nodes/parsenodes.h | 57 +++++++++ src/include/parser/kwlist.h | 8 ++ src/include/parser/parse_node.h | 1 + 4 files changed, 275 insertions(+), 11 deletions(-) diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 3460fea56b..84eb88ac2a 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -660,6 +660,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. @@ -703,7 +718,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 @@ -719,7 +734,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 @@ -732,7 +747,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 METHOD + MAPPING MATCH MATCHED MATERIALIZED MAXVALUE MEASURES MERGE METHOD MINUTE_P MINVALUE MODE MONTH_P MOVE NAME_P NAMES NATIONAL NATURAL NCHAR NEW NEXT NFC NFD NFKC NFKD NO NONE @@ -744,8 +759,8 @@ 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 - PLACING PLANS POLICY + PARALLEL PARAMETER PARSER PARTIAL PARTITION PASSING PASSWORD PAST + PATTERN_P PERMUTE PLACING PLANS POLICY POSITION PRECEDING PRECISION PRESERVE PREPARE PREPARED PRIMARY PRIOR PRIVILEGES PROCEDURAL PROCEDURE PROCEDURES PROGRAM PUBLICATION @@ -756,12 +771,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 SQL_P STABLE STANDALONE_P START STATEMENT STATISTICS STDIN STDOUT STORAGE STORED STRICT_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 TEMP TEMPLATE TEMPORARY TEXT_P THEN TIES TIME TIMESTAMP TO TRAILING TRANSACTION TRANSFORM @@ -867,6 +883,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); %nonassoc UNBOUNDED /* 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 + MEASURES AFTER INITIAL SEEK PATTERN_P %left Op OPERATOR /* multi-character ops and user-defined operators */ %left '+' '-' %left '*' '/' '%' @@ -15928,7 +15945,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); @@ -15936,10 +15954,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; } @@ -15963,6 +15983,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. @@ -16122,6 +16167,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. @@ -17250,6 +17432,7 @@ unreserved_keyword: | INDEXES | INHERIT | INHERITS + | INITIAL | INLINE_P | INPUT_P | INSENSITIVE @@ -17277,6 +17460,7 @@ unreserved_keyword: | MATCHED | MATERIALIZED | MAXVALUE + | MEASURES | MERGE | METHOD | MINUTE_P @@ -17319,6 +17503,9 @@ unreserved_keyword: | PARTITION | PASSING | PASSWORD + | PAST + | PATTERN_P + | PERMUTE | PLANS | POLICY | PRECEDING @@ -17369,6 +17556,7 @@ unreserved_keyword: | SEARCH | SECOND_P | SECURITY + | SEEK | SEQUENCE | SEQUENCES | SERIALIZABLE @@ -17394,6 +17582,7 @@ unreserved_keyword: | STRICT_P | STRIP_P | SUBSCRIPTION + | SUBSET | SUPPORT | SYSID | SYSTEM_P @@ -17581,6 +17770,7 @@ reserved_keyword: | CURRENT_USER | DEFAULT | DEFERRABLE + | DEFINE | DESC | DISTINCT | DO @@ -17743,6 +17933,7 @@ bare_label_keyword: | DEFAULTS | DEFERRABLE | DEFERRED + | DEFINE | DEFINER | DELETE_P | DELIMITER @@ -17818,6 +18009,7 @@ bare_label_keyword: | INDEXES | INHERIT | INHERITS + | INITIAL | INITIALLY | INLINE_P | INNER_P @@ -17867,6 +18059,7 @@ bare_label_keyword: | MATCHED | MATERIALIZED | MAXVALUE + | MEASURES | MERGE | METHOD | MINVALUE @@ -17920,6 +18113,9 @@ bare_label_keyword: | PARTITION | PASSING | PASSWORD + | PAST + | PATTERN_P + | PERMUTE | PLACING | PLANS | POLICY @@ -17976,6 +18172,7 @@ bare_label_keyword: | SCROLL | SEARCH | SECURITY + | SEEK | SELECT | SEQUENCE | SEQUENCES @@ -18007,6 +18204,7 @@ bare_label_keyword: | STRICT_P | STRIP_P | SUBSCRIPTION + | SUBSET | SUBSTRING | SUPPORT | SYMMETRIC diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index b3181f34ae..64e9df0a48 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -540,6 +540,44 @@ typedef struct SortBy int 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 * @@ -555,6 +593,8 @@ 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 */ @@ -1476,6 +1516,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. */ @@ -1507,6 +1552,18 @@ 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 2331acac09..6b3734a865 100644 --- a/src/include/parser/kwlist.h +++ b/src/include/parser/kwlist.h @@ -128,6 +128,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) @@ -212,6 +213,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) @@ -265,6 +267,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("method", METHOD, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("minute", MINUTE_P, UNRESERVED_KEYWORD, AS_LABEL) @@ -326,6 +329,9 @@ 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("pattern", PATTERN_P, UNRESERVED_KEYWORD, BARE_LABEL) +PG_KEYWORD("permute", PERMUTE, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("placing", PLACING, RESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("plans", PLANS, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("policy", POLICY, UNRESERVED_KEYWORD, BARE_LABEL) @@ -385,6 +391,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) @@ -416,6 +423,7 @@ PG_KEYWORD("stored", STORED, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("strict", STRICT_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 99d6515736..1e43cef6f4 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(Mon_Jan_22_19_26_18_2024_011)-- Content-Type: Text/X-Patch; charset=us-ascii Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="v13-0002-Row-pattern-recognition-patch-parse-analysis.patch" ^ permalink raw reply [nested|flat] 6+ messages in thread
* Re: minor doc issue in 9.16.2.1.1. Boolean Predicate Check Expressions @ 2024-10-17 06:07 jian he <[email protected]> 2024-10-17 13:59 ` Re: minor doc issue in 9.16.2.1.1. Boolean Predicate Check Expressions Bruce Momjian <[email protected]> 0 siblings, 1 reply; 6+ messages in thread From: jian he @ 2024-10-17 06:07 UTC (permalink / raw) To: Bruce Momjian <[email protected]>; +Cc: David G. Johnston <[email protected]>; pgsql-hackers On Thu, Oct 17, 2024 at 7:59 AM Bruce Momjian <[email protected]> wrote: > > > Where are we on this? I still see this behavior. > > --------------------------------------------------------------------------- > > but I found following two examples returning different results, > > i think they should return the same value. > > select json_value('1', '$ == "1"' returning jsonb error on error); > > select json_query('1', '$ == "1"' returning jsonb error on error); This part has been resolved. see section Note section in https://www.postgresql.org/docs/current/functions-json.html#SQLJSON-QUERY-FUNCTIONS > > On Fri, Jun 21, 2024 at 04:53:55PM +0800, jian he wrote: > > On Fri, Jun 21, 2024 at 11:11 AM David G. Johnston > > <[email protected]> wrote: > > > > > > On Thu, Jun 20, 2024 at 7:30 PM jian he <[email protected]> wrote: > > >> > > >> "predicate check expressions return the single three-valued result of > > >> > > >> the predicate: true, false, or unknown." > > >> "unknown" is wrong, because `select 'unknown'::jsonb;` will fail. > > >> here "unknown" should be "null"? see jsonb_path_query doc entry also. > > >> doc (https://www.postgresql.org/docs/devel/functions-json.html#FUNCTIONS-SQLJSON-CHECK-EXPRESSIONS) <<QUOTE>> While SQL-standard path expressions return the relevant element(s) of the queried JSON value, predicate check expressions return the single three-valued result of the predicate: true, false, or unknown. <<END OF QUOTE>> https://www.postgresql.org/docs/current/datatype-boolean.html says "The boolean type can have several states: “true”, “false”, and a third state, “unknown”, which is represented by the SQL null value." but here select jsonb_path_query('1', '$ == "a"'); return JSON null value, not SQL null value. however. select jsonb_path_match('1', '$ == "a"'); return SQL null value. maybe we can change to "predicate check expressions return the single three-valued result of the predicate: true, false, or null" Then in the <note> section mention that when Predicate check expressions cannot be applied, it returns JSON null for function jsonb_path_query, return SQL NULL for function jsonb_path_match or @@ operator. ^ permalink raw reply [nested|flat] 6+ messages in thread
* Re: minor doc issue in 9.16.2.1.1. Boolean Predicate Check Expressions 2024-10-17 06:07 Re: minor doc issue in 9.16.2.1.1. Boolean Predicate Check Expressions jian he <[email protected]> @ 2024-10-17 13:59 ` Bruce Momjian <[email protected]> 0 siblings, 0 replies; 6+ messages in thread From: Bruce Momjian @ 2024-10-17 13:59 UTC (permalink / raw) To: jian he <[email protected]>; +Cc: David G. Johnston <[email protected]>; pgsql-hackers On Thu, Oct 17, 2024 at 02:07:00PM +0800, jian he wrote: > On Thu, Oct 17, 2024 at 7:59 AM Bruce Momjian <[email protected]> wrote: > > > > > > Where are we on this? I still see this behavior. > > > > --------------------------------------------------------------------------- > > > > but I found following two examples returning different results, > > > i think they should return the same value. > > > select json_value('1', '$ == "1"' returning jsonb error on error); > > > select json_query('1', '$ == "1"' returning jsonb error on error); > > This part has been resolved. > see section Note section in > https://www.postgresql.org/docs/current/functions-json.html#SQLJSON-QUERY-FUNCTIONS Okay, good. > > > >> the predicate: true, false, or unknown." > > > >> "unknown" is wrong, because `select 'unknown'::jsonb;` will fail. > > > >> here "unknown" should be "null"? see jsonb_path_query doc entry also. > > > >> > > doc (https://www.postgresql.org/docs/devel/functions-json.html#FUNCTIONS-SQLJSON-CHECK-EXPRESSIONS) > <<QUOTE>> > While SQL-standard path expressions return the relevant element(s) of > the queried JSON value, predicate check expressions return the single > three-valued result of the predicate: true, false, or unknown. > <<END OF QUOTE>> > > https://www.postgresql.org/docs/current/datatype-boolean.html > says > "The boolean type can have several states: “true”, “false”, and a > third state, “unknown”, which is represented by the SQL null value." > > but here > select jsonb_path_query('1', '$ == "a"'); > return JSON null value, not SQL null value. > > however. > select jsonb_path_match('1', '$ == "a"'); > return SQL null value. > > > maybe we can change to > "predicate check expressions return the single three-valued result of > the predicate: true, false, or null" Yes, done in the attached patch. > Then in the <note> section mention that > when Predicate check expressions cannot be applied, it returns JSON > null for function jsonb_path_query, > return SQL NULL for function jsonb_path_match or @@ operator. The section is titled, "9.16.2.1. Deviations from the SQL Standard". Is this a deviation from the standard? If not, I think we have to distinguish SQL null and JSON null somewhere else. Is the "Note" text also a deviation? -- Bruce Momjian <[email protected]> https://momjian.us EDB https://enterprisedb.com When a patient asks the doctor, "Am I going to die?", he means "Am I going to die soon?" Attachments: [text/x-diff] json.diff (681B, ../../[email protected]/2-json.diff) download | inline diff: diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index f8a0d76d12b..bf35145de89 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -17648,7 +17648,7 @@ SELECT '{ element(s) of the queried JSON value, predicate check expressions return the single three-valued result of the predicate: <literal>true</literal>, - <literal>false</literal>, or <literal>unknown</literal>. + <literal>false</literal>, or <literal>NULL</literal>. For example, we could write this SQL-standard filter expression: <screen> <prompt>=></prompt> <userinput>select jsonb_path_query(:'json', '$.track.segments ?(@[*].HR > 130)');</userinput> ^ permalink raw reply [nested|flat] 6+ messages in thread
end of thread, other threads:[~2024-10-17 13:59 UTC | newest] Thread overview: 6+ 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-06 11:28 [PATCH 2/3] Refactor create_limit_path() to share cost adjustment code Etsuro Fujita <[email protected]> 2024-01-22 09:45 [PATCH v13 1/8] Row pattern recognition patch for raw parser. Tatsuo Ishii <[email protected]> 2024-10-17 06:07 Re: minor doc issue in 9.16.2.1.1. Boolean Predicate Check Expressions jian he <[email protected]> 2024-10-17 13:59 ` Re: minor doc issue in 9.16.2.1.1. Boolean Predicate Check Expressions Bruce Momjian <[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