From c0750160b80eec6339924c96200167f694be99f0 Mon Sep 17 00:00:00 2001 From: pgsql-guo Date: Mon, 25 Jul 2022 02:27:47 +0000 Subject: [PATCH v4] Using each rel as both outer and inner for anti joins --- src/backend/commands/explain.c | 3 + src/backend/executor/nodeHashjoin.c | 9 + src/backend/executor/nodeMergejoin.c | 8 + src/backend/optimizer/path/costsize.c | 3 +- src/backend/optimizer/path/joinpath.c | 5 + src/backend/optimizer/path/joinrels.c | 3 + src/backend/optimizer/path/pathkeys.c | 4 +- src/backend/optimizer/plan/setrefs.c | 1 + src/include/nodes/nodes.h | 4 +- src/test/regress/expected/partition_join.out | 276 ++++++++++--------- 10 files changed, 179 insertions(+), 137 deletions(-) diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index e29c2ae206..419b1753e7 100644 --- a/src/backend/commands/explain.c +++ b/src/backend/commands/explain.c @@ -1550,6 +1550,9 @@ ExplainNode(PlanState *planstate, List *ancestors, case JOIN_ANTI: jointype = "Anti"; break; + case JOIN_RIGHT_ANTI: + jointype = "Right Anti"; + break; default: jointype = "???"; break; diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c index 87403e2478..7a2a9f17cc 100644 --- a/src/backend/executor/nodeHashjoin.c +++ b/src/backend/executor/nodeHashjoin.c @@ -482,6 +482,14 @@ ExecHashJoinImpl(PlanState *pstate, bool parallel) continue; } + /* + * In a right-antijoin, we never return a matched tuple. + * And we need to use current outer tuple to scan the + * bucket for matches + */ + if (node->js.jointype == JOIN_RIGHT_ANTI) + continue; + /* * If we only need to join to the first matching inner * tuple, then consider returning this one, but after that @@ -694,6 +702,7 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags) ExecInitNullTupleSlot(estate, innerDesc, &TTSOpsVirtual); break; case JOIN_RIGHT: + case JOIN_RIGHT_ANTI: hjstate->hj_NullOuterTupleSlot = ExecInitNullTupleSlot(estate, outerDesc, &TTSOpsVirtual); break; diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c index fed345eae5..1f2176e841 100644 --- a/src/backend/executor/nodeMergejoin.c +++ b/src/backend/executor/nodeMergejoin.c @@ -805,6 +805,13 @@ ExecMergeJoin(PlanState *pstate) break; } + /* + * In a right-antijoin, we never return a matched tuple. + * And we need to use current outer tuple for further scans + */ + if (node->js.jointype == JOIN_RIGHT_ANTI) + break; + /* * If we only need to join to the first matching inner * tuple, then consider returning this one, but after that @@ -1554,6 +1561,7 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags) ExecInitNullTupleSlot(estate, innerDesc, &TTSOpsVirtual); break; case JOIN_RIGHT: + case JOIN_RIGHT_ANTI: mergestate->mj_FillOuter = false; mergestate->mj_FillInner = true; mergestate->mj_NullOuterTupleSlot = diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c index fb28e6411a..2a82692801 100644 --- a/src/backend/optimizer/path/costsize.c +++ b/src/backend/optimizer/path/costsize.c @@ -3645,7 +3645,8 @@ initial_cost_mergejoin(PlannerInfo *root, JoinCostWorkspace *workspace, outerstartsel = 0.0; outerendsel = 1.0; } - else if (jointype == JOIN_RIGHT) + else if (jointype == JOIN_RIGHT || + jointype == JOIN_RIGHT_ANTI) { innerstartsel = 0.0; innerendsel = 1.0; diff --git a/src/backend/optimizer/path/joinpath.c b/src/backend/optimizer/path/joinpath.c index 2a3f0ab7bf..89408ba8b6 100644 --- a/src/backend/optimizer/path/joinpath.c +++ b/src/backend/optimizer/path/joinpath.c @@ -1212,6 +1212,7 @@ sort_inner_and_outer(PlannerInfo *root, save_jointype != JOIN_UNIQUE_OUTER && save_jointype != JOIN_FULL && save_jointype != JOIN_RIGHT && + save_jointype != JOIN_RIGHT_ANTI && outerrel->partial_pathlist != NIL && bms_is_empty(joinrel->lateral_relids)) { @@ -1621,6 +1622,7 @@ match_unsorted_outer(PlannerInfo *root, useallclauses = false; break; case JOIN_RIGHT: + case JOIN_RIGHT_ANTI: case JOIN_FULL: nestjoinOK = false; useallclauses = true; @@ -1799,6 +1801,7 @@ match_unsorted_outer(PlannerInfo *root, save_jointype != JOIN_UNIQUE_OUTER && save_jointype != JOIN_FULL && save_jointype != JOIN_RIGHT && + save_jointype != JOIN_RIGHT_ANTI && outerrel->partial_pathlist != NIL && bms_is_empty(joinrel->lateral_relids)) { @@ -2145,6 +2148,7 @@ hash_inner_and_outer(PlannerInfo *root, save_jointype != JOIN_UNIQUE_OUTER && save_jointype != JOIN_FULL && save_jointype != JOIN_RIGHT && + save_jointype != JOIN_RIGHT_ANTI && outerrel->partial_pathlist != NIL && bms_is_empty(joinrel->lateral_relids)) { @@ -2307,6 +2311,7 @@ select_mergejoin_clauses(PlannerInfo *root, switch (jointype) { case JOIN_RIGHT: + case JOIN_RIGHT_ANTI: case JOIN_FULL: *mergejoin_allowed = !have_nonmergeable_joinclause; break; diff --git a/src/backend/optimizer/path/joinrels.c b/src/backend/optimizer/path/joinrels.c index 9da3ff2f9a..c6e5e0195c 100644 --- a/src/backend/optimizer/path/joinrels.c +++ b/src/backend/optimizer/path/joinrels.c @@ -915,6 +915,9 @@ populate_joinrel_with_paths(PlannerInfo *root, RelOptInfo *rel1, add_paths_to_joinrel(root, joinrel, rel1, rel2, JOIN_ANTI, sjinfo, restrictlist); + add_paths_to_joinrel(root, joinrel, rel2, rel1, + JOIN_RIGHT_ANTI, sjinfo, + restrictlist); break; default: /* other values not expected here */ diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c index b5d6c977ee..40ad79bdba 100644 --- a/src/backend/optimizer/path/pathkeys.c +++ b/src/backend/optimizer/path/pathkeys.c @@ -1609,7 +1609,9 @@ build_join_pathkeys(PlannerInfo *root, JoinType jointype, List *outer_pathkeys) { - if (jointype == JOIN_FULL || jointype == JOIN_RIGHT) + if (jointype == JOIN_FULL || + jointype == JOIN_RIGHT || + jointype == JOIN_RIGHT_ANTI) return NIL; /* diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c index 1cb0abdbc1..51f548041e 100644 --- a/src/backend/optimizer/plan/setrefs.c +++ b/src/backend/optimizer/plan/setrefs.c @@ -2252,6 +2252,7 @@ set_join_references(PlannerInfo *root, Join *join, int rtoffset) inner_itlist->has_non_vars = false; break; case JOIN_RIGHT: + case JOIN_RIGHT_ANTI: outer_itlist->has_non_vars = false; break; case JOIN_FULL: diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index cdd6debfa0..4a9aa2308f 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -300,6 +300,7 @@ typedef enum JoinType */ JOIN_SEMI, /* 1 copy of each LHS row that has match(es) */ JOIN_ANTI, /* 1 copy of each LHS row that has no match */ + JOIN_RIGHT_ANTI, /* 1 copy of each RHS row that has no match */ /* * These codes are used internally in the planner, but are not supported @@ -332,7 +333,8 @@ typedef enum JoinType ((1 << JOIN_LEFT) | \ (1 << JOIN_FULL) | \ (1 << JOIN_RIGHT) | \ - (1 << JOIN_ANTI))) != 0) + (1 << JOIN_ANTI) | \ + (1 << JOIN_RIGHT_ANTI))) != 0) /* * AggStrategy - diff --git a/src/test/regress/expected/partition_join.out b/src/test/regress/expected/partition_join.out index 03926a8413..852b740416 100644 --- a/src/test/regress/expected/partition_join.out +++ b/src/test/regress/expected/partition_join.out @@ -2403,24 +2403,24 @@ SELECT t1.* FROM prt1_adv t1 WHERE NOT EXISTS (SELECT 1 FROM prt2_adv t2 WHERE t Sort Sort Key: t1.a -> Append - -> Hash Anti Join - Hash Cond: (t1_1.a = t2_1.b) - -> Seq Scan on prt1_adv_p1 t1_1 - Filter: (b = 0) + -> Hash Right Anti Join + Hash Cond: (t2_1.b = t1_1.a) + -> Seq Scan on prt2_adv_p1 t2_1 -> Hash - -> Seq Scan on prt2_adv_p1 t2_1 - -> Hash Anti Join - Hash Cond: (t1_2.a = t2_2.b) - -> Seq Scan on prt1_adv_p2 t1_2 - Filter: (b = 0) + -> Seq Scan on prt1_adv_p1 t1_1 + Filter: (b = 0) + -> Hash Right Anti Join + Hash Cond: (t2_2.b = t1_2.a) + -> Seq Scan on prt2_adv_p2 t2_2 -> Hash - -> Seq Scan on prt2_adv_p2 t2_2 - -> Hash Anti Join - Hash Cond: (t1_3.a = t2_3.b) - -> Seq Scan on prt1_adv_p3 t1_3 - Filter: (b = 0) + -> Seq Scan on prt1_adv_p2 t1_2 + Filter: (b = 0) + -> Hash Right Anti Join + Hash Cond: (t2_3.b = t1_3.a) + -> Seq Scan on prt2_adv_p3 t2_3 -> Hash - -> Seq Scan on prt2_adv_p3 t2_3 + -> Seq Scan on prt1_adv_p3 t1_3 + Filter: (b = 0) (21 rows) SELECT t1.* FROM prt1_adv t1 WHERE NOT EXISTS (SELECT 1 FROM prt2_adv t2 WHERE t1.a = t2.b) AND t1.b = 0 ORDER BY t1.a; @@ -2638,24 +2638,24 @@ SELECT t1.* FROM prt1_adv t1 WHERE NOT EXISTS (SELECT 1 FROM prt2_adv t2 WHERE t Sort Sort Key: t1.a -> Append - -> Hash Anti Join - Hash Cond: (t1_1.a = t2_1.b) - -> Seq Scan on prt1_adv_p1 t1_1 - Filter: (b = 0) + -> Hash Right Anti Join + Hash Cond: (t2_1.b = t1_1.a) + -> Seq Scan on prt2_adv_p1 t2_1 -> Hash - -> Seq Scan on prt2_adv_p1 t2_1 - -> Hash Anti Join - Hash Cond: (t1_2.a = t2_2.b) - -> Seq Scan on prt1_adv_p2 t1_2 - Filter: (b = 0) + -> Seq Scan on prt1_adv_p1 t1_1 + Filter: (b = 0) + -> Hash Right Anti Join + Hash Cond: (t2_2.b = t1_2.a) + -> Seq Scan on prt2_adv_p2 t2_2 -> Hash - -> Seq Scan on prt2_adv_p2 t2_2 - -> Hash Anti Join - Hash Cond: (t1_3.a = t2_3.b) - -> Seq Scan on prt1_adv_p3 t1_3 - Filter: (b = 0) + -> Seq Scan on prt1_adv_p2 t1_2 + Filter: (b = 0) + -> Hash Right Anti Join + Hash Cond: (t2_3.b = t1_3.a) + -> Seq Scan on prt2_adv_p3 t2_3 -> Hash - -> Seq Scan on prt2_adv_p3 t2_3 + -> Seq Scan on prt1_adv_p3 t1_3 + Filter: (b = 0) (21 rows) SELECT t1.* FROM prt1_adv t1 WHERE NOT EXISTS (SELECT 1 FROM prt2_adv t2 WHERE t1.a = t2.b) AND t1.b = 0 ORDER BY t1.a; @@ -2671,26 +2671,26 @@ SELECT t1.* FROM prt1_adv t1 WHERE NOT EXISTS (SELECT 1 FROM prt2_adv t2 WHERE t -- partitions on the nullable side EXPLAIN (COSTS OFF) SELECT t1.* FROM prt2_adv t1 WHERE NOT EXISTS (SELECT 1 FROM prt1_adv t2 WHERE t1.b = t2.a) AND t1.a = 0 ORDER BY t1.b; - QUERY PLAN ------------------------------------------------------- + QUERY PLAN +--------------------------------------------------------- Sort Sort Key: t1.b - -> Hash Anti Join - Hash Cond: (t1.b = t2.a) + -> Hash Right Anti Join + Hash Cond: (t2.a = t1.b) -> Append - -> Seq Scan on prt2_adv_p1 t1_1 - Filter: (a = 0) - -> Seq Scan on prt2_adv_p2 t1_2 - Filter: (a = 0) - -> Seq Scan on prt2_adv_p3 t1_3 - Filter: (a = 0) - -> Seq Scan on prt2_adv_extra t1_4 - Filter: (a = 0) + -> Seq Scan on prt1_adv_p1 t2_1 + -> Seq Scan on prt1_adv_p2 t2_2 + -> Seq Scan on prt1_adv_p3 t2_3 -> Hash -> Append - -> Seq Scan on prt1_adv_p1 t2_1 - -> Seq Scan on prt1_adv_p2 t2_2 - -> Seq Scan on prt1_adv_p3 t2_3 + -> Seq Scan on prt2_adv_p1 t1_1 + Filter: (a = 0) + -> Seq Scan on prt2_adv_p2 t1_2 + Filter: (a = 0) + -> Seq Scan on prt2_adv_p3 t1_3 + Filter: (a = 0) + -> Seq Scan on prt2_adv_extra t1_4 + Filter: (a = 0) (18 rows) -- full join; currently we can't do partitioned join if there are no matched @@ -2858,25 +2858,25 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_adv t1 LEFT JOIN prt2_adv t2 ON (t1.a = -- anti join EXPLAIN (COSTS OFF) SELECT t1.* FROM prt1_adv t1 WHERE NOT EXISTS (SELECT 1 FROM prt2_adv t2 WHERE t1.a = t2.b) AND t1.b = 0 ORDER BY t1.a; - QUERY PLAN --------------------------------------------------------- + QUERY PLAN +------------------------------------------------------ Sort Sort Key: t1.a - -> Hash Anti Join - Hash Cond: (t1.a = t2.b) + -> Hash Right Anti Join + Hash Cond: (t2.b = t1.a) -> Append - -> Seq Scan on prt1_adv_p1 t1_1 - Filter: (b = 0) - -> Seq Scan on prt1_adv_p2 t1_2 - Filter: (b = 0) - -> Seq Scan on prt1_adv_p3 t1_3 - Filter: (b = 0) + -> Seq Scan on prt2_adv_p1 t2_1 + -> Seq Scan on prt2_adv_p2 t2_2 + -> Seq Scan on prt2_adv_p3_1 t2_3 + -> Seq Scan on prt2_adv_p3_2 t2_4 -> Hash -> Append - -> Seq Scan on prt2_adv_p1 t2_1 - -> Seq Scan on prt2_adv_p2 t2_2 - -> Seq Scan on prt2_adv_p3_1 t2_3 - -> Seq Scan on prt2_adv_p3_2 t2_4 + -> Seq Scan on prt1_adv_p1 t1_1 + Filter: (b = 0) + -> Seq Scan on prt1_adv_p2 t1_2 + Filter: (b = 0) + -> Seq Scan on prt1_adv_p3 t1_3 + Filter: (b = 0) (17 rows) -- full join @@ -3280,27 +3280,30 @@ SELECT t1.a, t1.c, t2.a, t2.c FROM plt1_adv t1 LEFT JOIN plt2_adv t2 ON (t1.a = -- anti join EXPLAIN (COSTS OFF) SELECT t1.* FROM plt1_adv t1 WHERE NOT EXISTS (SELECT 1 FROM plt2_adv t2 WHERE t1.a = t2.a AND t1.c = t2.c) AND t1.b < 10 ORDER BY t1.a; - QUERY PLAN ----------------------------------------------------------------------- + QUERY PLAN +-------------------------------------------------------------------- Sort Sort Key: t1.a -> Append - -> Nested Loop Anti Join - Join Filter: ((t1_1.a = t2_1.a) AND (t1_1.c = t2_1.c)) - -> Seq Scan on plt1_adv_p1 t1_1 - Filter: (b < 10) + -> Hash Right Anti Join + Hash Cond: ((t2_1.a = t1_1.a) AND (t2_1.c = t1_1.c)) -> Seq Scan on plt2_adv_p1 t2_1 - -> Nested Loop Anti Join - Join Filter: ((t1_2.a = t2_2.a) AND (t1_2.c = t2_2.c)) - -> Seq Scan on plt1_adv_p2 t1_2 - Filter: (b < 10) + -> Hash + -> Seq Scan on plt1_adv_p1 t1_1 + Filter: (b < 10) + -> Hash Right Anti Join + Hash Cond: ((t2_2.a = t1_2.a) AND (t2_2.c = t1_2.c)) -> Seq Scan on plt2_adv_p2 t2_2 - -> Nested Loop Anti Join - Join Filter: ((t1_3.a = t2_3.a) AND (t1_3.c = t2_3.c)) - -> Seq Scan on plt1_adv_p3 t1_3 - Filter: (b < 10) + -> Hash + -> Seq Scan on plt1_adv_p2 t1_2 + Filter: (b < 10) + -> Hash Right Anti Join + Hash Cond: ((t2_3.a = t1_3.a) AND (t2_3.c = t1_3.c)) -> Seq Scan on plt2_adv_p3 t2_3 -(18 rows) + -> Hash + -> Seq Scan on plt1_adv_p3 t1_3 + Filter: (b < 10) +(21 rows) SELECT t1.* FROM plt1_adv t1 WHERE NOT EXISTS (SELECT 1 FROM plt2_adv t2 WHERE t1.a = t2.a AND t1.c = t2.c) AND t1.b < 10 ORDER BY t1.a; a | b | c @@ -3493,27 +3496,30 @@ SELECT t1.a, t1.c, t2.a, t2.c FROM plt2_adv t1 LEFT JOIN plt1_adv t2 ON (t1.a = -- anti join EXPLAIN (COSTS OFF) SELECT t1.* FROM plt1_adv t1 WHERE NOT EXISTS (SELECT 1 FROM plt2_adv t2 WHERE t1.a = t2.a AND t1.c = t2.c) AND t1.b < 10 ORDER BY t1.a; - QUERY PLAN ----------------------------------------------------------------------- + QUERY PLAN +-------------------------------------------------------------------- Sort Sort Key: t1.a -> Append - -> Nested Loop Anti Join - Join Filter: ((t1_1.a = t2_1.a) AND (t1_1.c = t2_1.c)) - -> Seq Scan on plt1_adv_p1 t1_1 - Filter: (b < 10) + -> Hash Right Anti Join + Hash Cond: ((t2_1.a = t1_1.a) AND (t2_1.c = t1_1.c)) -> Seq Scan on plt2_adv_p1 t2_1 - -> Nested Loop Anti Join - Join Filter: ((t1_2.a = t2_2.a) AND (t1_2.c = t2_2.c)) - -> Seq Scan on plt1_adv_p2 t1_2 - Filter: (b < 10) + -> Hash + -> Seq Scan on plt1_adv_p1 t1_1 + Filter: (b < 10) + -> Hash Right Anti Join + Hash Cond: ((t2_2.a = t1_2.a) AND (t2_2.c = t1_2.c)) -> Seq Scan on plt2_adv_p2 t2_2 - -> Nested Loop Anti Join - Join Filter: ((t1_3.a = t2_3.a) AND (t1_3.c = t2_3.c)) - -> Seq Scan on plt1_adv_p3 t1_3 - Filter: (b < 10) + -> Hash + -> Seq Scan on plt1_adv_p2 t1_2 + Filter: (b < 10) + -> Hash Right Anti Join + Hash Cond: ((t2_3.a = t1_3.a) AND (t2_3.c = t1_3.c)) -> Seq Scan on plt2_adv_p3 t2_3 -(18 rows) + -> Hash + -> Seq Scan on plt1_adv_p3 t1_3 + Filter: (b < 10) +(21 rows) SELECT t1.* FROM plt1_adv t1 WHERE NOT EXISTS (SELECT 1 FROM plt2_adv t2 WHERE t1.a = t2.a AND t1.c = t2.c) AND t1.b < 10 ORDER BY t1.a; a | b | c @@ -3526,26 +3532,26 @@ SELECT t1.* FROM plt1_adv t1 WHERE NOT EXISTS (SELECT 1 FROM plt2_adv t2 WHERE t -- partitions on the nullable side EXPLAIN (COSTS OFF) SELECT t1.* FROM plt2_adv t1 WHERE NOT EXISTS (SELECT 1 FROM plt1_adv t2 WHERE t1.a = t2.a AND t1.c = t2.c) AND t1.b < 10 ORDER BY t1.a; - QUERY PLAN ------------------------------------------------------- + QUERY PLAN +--------------------------------------------------------- Sort Sort Key: t1.a - -> Hash Anti Join - Hash Cond: ((t1.a = t2.a) AND (t1.c = t2.c)) + -> Hash Right Anti Join + Hash Cond: ((t2.a = t1.a) AND (t2.c = t1.c)) -> Append - -> Seq Scan on plt2_adv_extra t1_1 - Filter: (b < 10) - -> Seq Scan on plt2_adv_p1 t1_2 - Filter: (b < 10) - -> Seq Scan on plt2_adv_p2 t1_3 - Filter: (b < 10) - -> Seq Scan on plt2_adv_p3 t1_4 - Filter: (b < 10) + -> Seq Scan on plt1_adv_p1 t2_1 + -> Seq Scan on plt1_adv_p2 t2_2 + -> Seq Scan on plt1_adv_p3 t2_3 -> Hash -> Append - -> Seq Scan on plt1_adv_p1 t2_1 - -> Seq Scan on plt1_adv_p2 t2_2 - -> Seq Scan on plt1_adv_p3 t2_3 + -> Seq Scan on plt2_adv_extra t1_1 + Filter: (b < 10) + -> Seq Scan on plt2_adv_p1 t1_2 + Filter: (b < 10) + -> Seq Scan on plt2_adv_p2 t1_3 + Filter: (b < 10) + -> Seq Scan on plt2_adv_p3 t1_4 + Filter: (b < 10) (18 rows) -- full join; currently we can't do partitioned join if there are no matched @@ -3655,25 +3661,25 @@ SELECT t1.a, t1.c, t2.a, t2.c FROM plt1_adv t1 LEFT JOIN plt2_adv t2 ON (t1.a = -- anti join EXPLAIN (COSTS OFF) SELECT t1.* FROM plt1_adv t1 WHERE NOT EXISTS (SELECT 1 FROM plt2_adv t2 WHERE t1.a = t2.a AND t1.c = t2.c) AND t1.b < 10 ORDER BY t1.a; - QUERY PLAN --------------------------------------------------------- + QUERY PLAN +------------------------------------------------------ Sort Sort Key: t1.a - -> Hash Anti Join - Hash Cond: ((t1.a = t2.a) AND (t1.c = t2.c)) + -> Hash Right Anti Join + Hash Cond: ((t2.a = t1.a) AND (t2.c = t1.c)) -> Append - -> Seq Scan on plt1_adv_p1 t1_1 - Filter: (b < 10) - -> Seq Scan on plt1_adv_p2 t1_2 - Filter: (b < 10) - -> Seq Scan on plt1_adv_p3 t1_3 - Filter: (b < 10) + -> Seq Scan on plt2_adv_p1 t2_1 + -> Seq Scan on plt2_adv_p2_1 t2_2 + -> Seq Scan on plt2_adv_p2_2 t2_3 + -> Seq Scan on plt2_adv_p3 t2_4 -> Hash -> Append - -> Seq Scan on plt2_adv_p1 t2_1 - -> Seq Scan on plt2_adv_p2_1 t2_2 - -> Seq Scan on plt2_adv_p2_2 t2_3 - -> Seq Scan on plt2_adv_p3 t2_4 + -> Seq Scan on plt1_adv_p1 t1_1 + Filter: (b < 10) + -> Seq Scan on plt1_adv_p2 t1_2 + Filter: (b < 10) + -> Seq Scan on plt1_adv_p3 t1_3 + Filter: (b < 10) (17 rows) -- full join @@ -3830,28 +3836,30 @@ SELECT t1.a, t1.c, t2.a, t2.c FROM plt1_adv t1 LEFT JOIN plt2_adv t2 ON (t1.a = -- anti join EXPLAIN (COSTS OFF) SELECT t1.* FROM plt1_adv t1 WHERE NOT EXISTS (SELECT 1 FROM plt2_adv t2 WHERE t1.a = t2.a AND t1.c = t2.c) AND t1.b < 10 ORDER BY t1.a; - QUERY PLAN ----------------------------------------------------------------------- + QUERY PLAN +-------------------------------------------------------------------- Sort Sort Key: t1.a -> Append - -> Hash Anti Join - Hash Cond: ((t1_1.a = t2_1.a) AND (t1_1.c = t2_1.c)) - -> Seq Scan on plt1_adv_p1_null t1_1 - Filter: (b < 10) + -> Hash Right Anti Join + Hash Cond: ((t2_1.a = t1_1.a) AND (t2_1.c = t1_1.c)) + -> Seq Scan on plt2_adv_p1 t2_1 -> Hash - -> Seq Scan on plt2_adv_p1 t2_1 - -> Nested Loop Anti Join - Join Filter: ((t1_2.a = t2_2.a) AND (t1_2.c = t2_2.c)) - -> Seq Scan on plt1_adv_p2 t1_2 - Filter: (b < 10) + -> Seq Scan on plt1_adv_p1_null t1_1 + Filter: (b < 10) + -> Hash Right Anti Join + Hash Cond: ((t2_2.a = t1_2.a) AND (t2_2.c = t1_2.c)) -> Seq Scan on plt2_adv_p2 t2_2 - -> Nested Loop Anti Join - Join Filter: ((t1_3.a = t2_3.a) AND (t1_3.c = t2_3.c)) - -> Seq Scan on plt1_adv_p3 t1_3 - Filter: (b < 10) + -> Hash + -> Seq Scan on plt1_adv_p2 t1_2 + Filter: (b < 10) + -> Hash Right Anti Join + Hash Cond: ((t2_3.a = t1_3.a) AND (t2_3.c = t1_3.c)) -> Seq Scan on plt2_adv_p3_null t2_3 -(19 rows) + -> Hash + -> Seq Scan on plt1_adv_p3 t1_3 + Filter: (b < 10) +(21 rows) SELECT t1.* FROM plt1_adv t1 WHERE NOT EXISTS (SELECT 1 FROM plt2_adv t2 WHERE t1.a = t2.a AND t1.c = t2.c) AND t1.b < 10 ORDER BY t1.a; a | b | c -- 2.25.1