diff --git a/contrib/postgres_fdw/deparse.c b/contrib/postgres_fdw/deparse.c index 691658f..287c7d5 100644 --- a/contrib/postgres_fdw/deparse.c +++ b/contrib/postgres_fdw/deparse.c @@ -763,21 +763,21 @@ build_tlist_to_deparse(RelOptInfo *foreignrel) * List of columns selected is returned in retrieved_attrs. */ extern void deparseSelectStmtForRel(StringInfo buf, PlannerInfo *root, RelOptInfo *rel, List *tlist, List *remote_conds, List *pathkeys, List **retrieved_attrs, List **params_list) { deparse_expr_cxt context; /* We handle relations for foreign tables and joins between those */ - Assert(rel->reloptkind == RELOPT_JOINREL || + Assert(IS_JOIN_REL(rel) || rel->reloptkind == RELOPT_BASEREL || rel->reloptkind == RELOPT_OTHER_MEMBER_REL); /* Fill portions of context common to join and base relation */ context.buf = buf; context.root = root; context.foreignrel = rel; context.params_list = params_list; /* Construct SELECT clause and FROM clause */ @@ -817,21 +817,21 @@ deparseSelectSql(List *tlist, List **retrieved_attrs, deparse_expr_cxt *context) StringInfo buf = context->buf; RelOptInfo *foreignrel = context->foreignrel; PlannerInfo *root = context->root; PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) foreignrel->fdw_private; /* * Construct SELECT list */ appendStringInfoString(buf, "SELECT "); - if (foreignrel->reloptkind == RELOPT_JOINREL) + if (IS_JOIN_REL(foreignrel)) { /* For a join relation use the input tlist */ deparseExplicitTargetList(tlist, retrieved_attrs, context); } else { /* * For a base relation fpinfo->attrs_used gives the list of columns * required to be fetched from the foreign server. */ @@ -845,22 +845,21 @@ deparseSelectSql(List *tlist, List **retrieved_attrs, deparse_expr_cxt *context) deparseTargetList(buf, root, foreignrel->relid, rel, false, fpinfo->attrs_used, false, retrieved_attrs); heap_close(rel, NoLock); } /* * Construct FROM clause */ appendStringInfoString(buf, " FROM "); - deparseFromExprForRel(buf, root, foreignrel, - (foreignrel->reloptkind == RELOPT_JOINREL), + deparseFromExprForRel(buf, root, foreignrel, IS_JOIN_REL(foreignrel), context->params_list); } /* * Emit a target list that retrieves the columns specified in attrs_used. * This is used for both SELECT and RETURNING targetlists; the is_returning * parameter is true only for a RETURNING targetlist. * * The tlist text is appended to buf, and we also create an integer List * of the columns being retrieved, which is returned to *retrieved_attrs. @@ -981,21 +980,21 @@ deparseLockingClause(deparse_expr_cxt *context) * before 8.3. */ if (relid == root->parse->resultRelation && (root->parse->commandType == CMD_UPDATE || root->parse->commandType == CMD_DELETE)) { /* Relation is UPDATE/DELETE target, so use FOR UPDATE */ appendStringInfoString(buf, " FOR UPDATE"); /* Add the relation alias if we are here for a join relation */ - if (rel->reloptkind == RELOPT_JOINREL) + if (IS_JOIN_REL(rel)) appendStringInfo(buf, " OF %s%d", REL_ALIAS_PREFIX, relid); } else { PlanRowMark *rc = get_plan_rowmark(root->rowMarks, relid); if (rc) { /* * Relation is specified as a FOR UPDATE/SHARE target, so @@ -1017,22 +1016,21 @@ deparseLockingClause(deparse_expr_cxt *context) case LCS_FORSHARE: appendStringInfoString(buf, " FOR SHARE"); break; case LCS_FORNOKEYUPDATE: case LCS_FORUPDATE: appendStringInfoString(buf, " FOR UPDATE"); break; } /* Add the relation alias if we are here for a join relation */ - if (rel->reloptkind == RELOPT_JOINREL && - rc->strength != LCS_NONE) + if (IS_JOIN_REL(rel) && rc->strength != LCS_NONE) appendStringInfo(buf, " OF %s%d", REL_ALIAS_PREFIX, relid); } } } } /* * Deparse conditions from the provided list and append them to buf. * * The conditions in the list are assumed to be ANDed. This function is used to @@ -1155,21 +1153,21 @@ deparseExplicitTargetList(List *tlist, List **retrieved_attrs, * The function constructs ... JOIN ... ON ... for join relation. For a base * relation it just returns schema-qualified tablename, with the appropriate * alias if so requested. */ static void deparseFromExprForRel(StringInfo buf, PlannerInfo *root, RelOptInfo *foreignrel, bool use_alias, List **params_list) { PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) foreignrel->fdw_private; - if (foreignrel->reloptkind == RELOPT_JOINREL) + if (IS_JOIN_REL(foreignrel)) { RelOptInfo *rel_o = fpinfo->outerrel; RelOptInfo *rel_i = fpinfo->innerrel; StringInfoData join_sql_o; StringInfoData join_sql_i; /* Deparse outer relation */ initStringInfo(&join_sql_o); deparseFromExprForRel(&join_sql_o, root, rel_o, true, params_list); @@ -1860,21 +1858,21 @@ deparseExpr(Expr *node, deparse_expr_cxt *context) * Deparse given Var node into context->buf. * * If the Var belongs to the foreign relation, just print its remote name. * Otherwise, it's effectively a Param (and will in fact be a Param at * run time). Handle it the same way we handle plain Params --- see * deparseParam for comments. */ static void deparseVar(Var *node, deparse_expr_cxt *context) { - bool qualify_col = (context->foreignrel->reloptkind == RELOPT_JOINREL); + bool qualify_col = IS_JOIN_REL(context->foreignrel); if (bms_is_member(node->varno, context->foreignrel->relids) && node->varlevelsup == 0) deparseColumnRef(context->buf, node->varno, node->varattno, context->root, qualify_col); else { /* Treat like a Param */ if (context->params_list) { diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c index daf0438..594292a 100644 --- a/contrib/postgres_fdw/postgres_fdw.c +++ b/contrib/postgres_fdw/postgres_fdw.c @@ -1163,21 +1163,21 @@ postgresGetForeignPlan(PlannerInfo *root, local_exprs = lappend(local_exprs, rinfo->clause); else if (is_foreign_expr(root, foreignrel, rinfo->clause)) { remote_conds = lappend(remote_conds, rinfo); remote_exprs = lappend(remote_exprs, rinfo->clause); } else local_exprs = lappend(local_exprs, rinfo->clause); } - if (foreignrel->reloptkind == RELOPT_JOINREL) + if (IS_JOIN_REL(foreignrel)) { /* For a join relation, get the conditions from fdw_private structure */ remote_conds = fpinfo->remote_conds; local_exprs = fpinfo->local_conds; /* Build the list of columns to be fetched from the foreign server. */ fdw_scan_tlist = build_tlist_to_deparse(foreignrel); /* * Ensure that the outer plan produces a tuple whose descriptor @@ -1221,21 +1221,21 @@ postgresGetForeignPlan(PlannerInfo *root, &retrieved_attrs, ¶ms_list); /* * Build the fdw_private list that will be available to the executor. * Items in the list must match order in enum FdwScanPrivateIndex. */ fdw_private = list_make4(makeString(sql.data), remote_conds, retrieved_attrs, makeInteger(fpinfo->fetch_size)); - if (foreignrel->reloptkind == RELOPT_JOINREL) + if (IS_JOIN_REL(foreignrel)) fdw_private = lappend(fdw_private, makeString(fpinfo->relation_name->data)); /* * Create the ForeignScan node for the given relation. * * Note that the remote parameter expressions are stored in the fdw_exprs * field of the finished plan node; we can't keep them in private state * because then they wouldn't be subject to later planner processing. */ @@ -2498,21 +2498,21 @@ estimate_path_cost_size(PlannerInfo *root, List *retrieved_attrs; /* * param_join_conds might contain both clauses that are safe to send * across, and clauses that aren't. */ classifyConditions(root, foreignrel, param_join_conds, &remote_param_join_conds, &local_param_join_conds); /* Build the list of columns to be fetched from the foreign server. */ - if (foreignrel->reloptkind == RELOPT_JOINREL) + if (IS_JOIN_REL(foreignrel)) fdw_scan_tlist = build_tlist_to_deparse(foreignrel); else fdw_scan_tlist = NIL; /* * The complete list of remote conditions includes everything from * baserestrictinfo plus any extra join_conds relevant to this * particular path. */ remote_conds = list_concat(list_copy(remote_param_join_conds), @@ -2579,21 +2579,21 @@ estimate_path_cost_size(PlannerInfo *root, * We will come here again and again with different set of pathkeys * that caller wants to cost. We don't need to calculate the cost of * bare scan each time. Instead, use the costs if we have cached them * already. */ if (fpinfo->rel_startup_cost > 0 && fpinfo->rel_total_cost > 0) { startup_cost = fpinfo->rel_startup_cost; run_cost = fpinfo->rel_total_cost - fpinfo->rel_startup_cost; } - else if (foreignrel->reloptkind != RELOPT_JOINREL) + else if (!IS_JOIN_REL(foreignrel)) { /* Clamp retrieved rows estimates to at most foreignrel->tuples. */ retrieved_rows = Min(retrieved_rows, foreignrel->tuples); /* * Cost as though this were a seqscan, which is pessimistic. We * effectively imagine the local_conds are being evaluated * remotely, too. */ startup_cost = 0; diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c index 1a60563..dc2b34b 100644 --- a/src/backend/catalog/partition.c +++ b/src/backend/catalog/partition.c @@ -13,38 +13,41 @@ *------------------------------------------------------------------------- */ #include "postgres.h" #include "access/heapam.h" #include "access/htup_details.h" #include "access/nbtree.h" #include "access/sysattr.h" #include "catalog/dependency.h" +#include "catalog/heap.h" #include "catalog/indexing.h" #include "catalog/objectaddress.h" #include "catalog/partition.h" #include "catalog/pg_collation.h" #include "catalog/pg_inherits.h" #include "catalog/pg_inherits_fn.h" #include "catalog/pg_opclass.h" #include "catalog/pg_partitioned_table.h" #include "catalog/pg_partitioned_table_fn.h" #include "catalog/pg_type.h" #include "executor/executor.h" #include "miscadmin.h" #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" #include "nodes/parsenodes.h" #include "optimizer/clauses.h" +#include "optimizer/cost.h" #include "optimizer/planmain.h" #include "optimizer/var.h" +#include "rewrite/rewriteManip.h" #include "storage/lmgr.h" #include "utils/array.h" #include "utils/builtins.h" #include "utils/datum.h" #include "utils/memutils.h" #include "utils/fmgroids.h" #include "utils/inval.h" #include "utils/lsyscache.h" #include "utils/ruleutils.h" #include "utils/rel.h" @@ -2412,20 +2415,78 @@ make_range_bound(PartitionKey key, List *val, bool inclusive, bool lower) key->tcinfo->typbyval[i], key->tcinfo->typlen[i]); i++; } } return bound; } /* + * Return a copy of input BoundCollection structure containg nparts number of + * partitions. The data types of bounds are described by given partition key + * specificiation. + */ +static BoundCollection +copy_bounds(BoundCollection src_bounds, PartitionKey key, int nparts) +{ + BoundCollection dst_bounds; + int i; + + dst_bounds = (BoundCollection) palloc(sizeof(BoundCollectionData)); + + if (src_bounds->listinfo) + { + ListInfo *dst_li = (ListInfo *) palloc(sizeof(ListInfo)); + ListInfo *src_li = src_bounds->listinfo; + + Assert(!src_bounds->rangeinfo); + dst_bounds->rangeinfo = NULL; + + /* Copy the ListInfo structure. */ + dst_li->nvalues = src_li->nvalues; + dst_li->has_null = src_li->has_null; + dst_li->null_index = src_li->null_index; + + dst_li->values = (Datum *) palloc(sizeof(Datum) * dst_li->nvalues); + dst_li->indexes = (int *) palloc(sizeof(int) * dst_li->nvalues); + for (i = 0; i < dst_li->nvalues; i++) + { + dst_li->values[i] = datumCopy(src_li->values[i], + key->tcinfo->typbyval[0], + key->tcinfo->typlen[0]); + dst_li->indexes[i] = src_li->indexes[i]; + } + + dst_bounds->listinfo = dst_li; + } + else + { + RangeInfo *dst_ri = (RangeInfo *) palloc(sizeof(RangeInfo)); + RangeInfo *src_ri = src_bounds->rangeinfo; + + Assert(!src_bounds->listinfo && src_bounds->rangeinfo); + dst_bounds->listinfo = NULL; + + /* Copy RangeInfo structure. */ + dst_ri = (RangeInfo *) palloc(sizeof(RangeInfo)); + dst_ri->ranges = (PartitionRange **) palloc(sizeof(PartitionRange *) * nparts); + for (i = 0; i < nparts; i++) + dst_ri->ranges[i] = copy_range(src_ri->ranges[i], key); + + dst_bounds->rangeinfo = dst_ri; + } + + return dst_bounds; +} + +/* * Make and return a copy of input PartitionRange. */ static PartitionRange * copy_range(PartitionRange *src, PartitionKey key) { PartitionRange *result; result = (PartitionRange *) palloc0(sizeof(PartitionRange)); result->lower = copy_range_bound(src->lower, key); result->upper = copy_range_bound(src->upper, key); @@ -2647,10 +2708,175 @@ tuple_rightof_bound(PartitionKey key, Datum *tuple, PartitionRangeBound *bound) static bool tuple_leftof_bound(PartitionKey key, Datum *tuple, PartitionRangeBound *bound) { int32 cmpval = partition_range_tuple_cmp(key, tuple, bound->val); if (!cmpval) return !bound->lower ? bound->inclusive : !bound->inclusive; return cmpval < 0; } + +/* + * find_partition_scheme + * Find the "canonical" partition scheme for the given base table. + * + * The function searches the list of canonical partition schemes for one that + * exactly matches the partitioning properties of the given relation. If it + * does not find one, the function creates a canonical partition scheme + * structure and adds it to the list. + * + * For an unpartitioned table, it returns NULL. + */ + +extern PartitionScheme +find_partition_scheme(PlannerInfo *root, Relation relation) +{ + PartitionKey part_key = RelationGetPartitionKey(relation); + PartitionDesc part_desc = RelationGetPartitionDesc(relation); + ListCell *lc; + int nparts; + int partnatts; + int cnt_pks; + PartitionScheme part_scheme = NULL; + + /* No partition scheme for an unpartitioned relation. */ + if (!part_desc || !part_key) + return NULL; + + nparts = part_desc->nparts; + partnatts = part_key->partnatts; + + /* Search for a matching partition scheme and return if found one. */ + foreach (lc, root->part_schemes) + { + part_scheme = lfirst(lc); + + /* Match number of partitions and partitioning strategy. */ + if (nparts != part_scheme->nparts || + part_key->strategy != part_scheme->strategy || + partnatts != part_scheme->partnatts) + continue; + + /* Match the partition key types. */ + for (cnt_pks = 0; cnt_pks < partnatts; cnt_pks++) + { + /* + * It suffices to check the OID of support function as it always has + * two arguments and returns boolean. For types, it suffices to match + * the type id, mod and collation; len, byval and align are depedent on + * the first two. + */ + if (part_key->partopfamily[cnt_pks] != part_scheme->partopfamily[cnt_pks] || + part_key->partopcintype[cnt_pks] != part_scheme->partopcintype[cnt_pks] || + part_key->tcinfo->typid[cnt_pks] != part_scheme->key_types[cnt_pks] || + part_key->tcinfo->typmod[cnt_pks] != part_scheme->key_typmods[cnt_pks] || + part_key->tcinfo->typcoll[cnt_pks] != part_scheme->key_collations[cnt_pks]) + break; + } + + /* Some partition key didn't match. Check next partitioning scheme. */ + if (cnt_pks < partnatts) + continue; + + if (!partition_bounds_equal(part_key, part_desc->bounds, + part_scheme->bounds, nparts)) + continue; + + /* Found matching partition scheme. */ + return part_scheme; + } + + /* Did not find matching partition scheme. Create one. */ + part_scheme = (PartitionScheme) palloc0(sizeof(PartitionSchemeData)); + + /* Copy partition bounds/lists. */ + part_scheme->nparts = part_desc->nparts; + part_scheme->strategy = part_key->strategy; + part_scheme->bounds = copy_bounds(part_desc->bounds, part_key, + part_scheme->nparts); + + /* Store partition key information. */ + part_scheme->partnatts = part_key->partnatts; + + part_scheme->partopfamily = (Oid *) palloc(sizeof(Oid) * partnatts); + part_scheme->partopcintype = (Oid *) palloc(sizeof(Oid) * partnatts); + part_scheme->key_types = (Oid *) palloc(sizeof(Oid) * partnatts); + part_scheme->key_typmods = (int32 *) palloc(sizeof(int32) * partnatts); + part_scheme->key_collations = (Oid *) palloc(sizeof(Oid) * partnatts); + + for (cnt_pks = 0; cnt_pks < partnatts; cnt_pks++) + { + part_scheme->partopfamily[cnt_pks] = part_key->partopfamily[cnt_pks]; + part_scheme->partopcintype[cnt_pks] = part_key->partopcintype[cnt_pks]; + part_scheme->key_types[cnt_pks] = part_key->tcinfo->typid[cnt_pks]; + part_scheme->key_typmods[cnt_pks] = part_key->tcinfo->typmod[cnt_pks]; + part_scheme->key_collations[cnt_pks] = part_key->tcinfo->typcoll[cnt_pks]; + } + + /* Add the partitioning scheme to PlannerInfo. */ + root->part_schemes = lappend(root->part_schemes, part_scheme); + + return part_scheme; +} + +/* + * build_baserel_partition_key_exprs + * Collect partition key expressions for a given base relation. + * + * The function converts single column partition keys into corresponding Var + * nodes. It restamps Var nodes in partition key expressions by given varno. + * The partition key expressions are returned as an array of Lists to be stored + * in RelOptInfo of the base relation. + */ +extern List ** +build_baserel_partition_key_exprs(Relation relation, Index varno) +{ + PartitionKey part_key = RelationGetPartitionKey(relation); + int num_pkexprs; + int cnt_pke; + List **partexprs; + ListCell *lc; + + if (!part_key || part_key->partnatts <= 0) + return NULL; + + num_pkexprs = part_key->partnatts; + partexprs = (List **) palloc(sizeof(List *) * num_pkexprs); + lc = list_head(part_key->partexprs); + + for (cnt_pke = 0; cnt_pke < num_pkexprs; cnt_pke++) + { + AttrNumber attno = part_key->partattrs[cnt_pke]; + Expr *pkexpr; + + if (attno != InvalidAttrNumber) + { + /* Single column partition key is stored as a Var node. */ + Form_pg_attribute att_tup; + + if (attno < 0) + att_tup = SystemAttributeDefinition(attno, + relation->rd_rel->relhasoids); + else + att_tup = relation->rd_att->attrs[attno - 1]; + + pkexpr = (Expr *) makeVar(varno, attno, att_tup->atttypid, + att_tup->atttypmod, + att_tup->attcollation, 0); + } + else + { + if (lc == NULL) + elog(ERROR, "wrong number of partition key expressions"); + + /* Re-stamp the expressions with given varno. */ + pkexpr = (Expr *) copyObject(lfirst(lc)); + ChangeVarNodes((Node *) pkexpr, 1, varno, 0); + lc = lnext(lc); + } + + partexprs[cnt_pke] = list_make1(pkexpr); + } + + return partexprs; +} diff --git a/src/backend/foreign/foreign.c b/src/backend/foreign/foreign.c index 242d6d2..75c95e4 100644 --- a/src/backend/foreign/foreign.c +++ b/src/backend/foreign/foreign.c @@ -714,21 +714,21 @@ get_foreign_server_oid(const char *servername, bool missing_ok) * Since the plan created using this path will presumably only be used to * execute EPQ checks, efficiency of the path is not a concern. But since the * path list in RelOptInfo is anyway sorted by total cost we are likely to * choose the most efficient path, which is all for the best. */ extern Path * GetExistingLocalJoinPath(RelOptInfo *joinrel) { ListCell *lc; - Assert(joinrel->reloptkind == RELOPT_JOINREL); + Assert(IS_JOIN_REL(joinrel)); foreach(lc, joinrel->pathlist) { Path *path = (Path *) lfirst(lc); JoinPath *joinpath = NULL; /* Skip parameterised paths. */ if (path->param_info != NULL) continue; @@ -779,27 +779,27 @@ GetExistingLocalJoinPath(RelOptInfo *joinrel) * If either inner or outer path is a ForeignPath corresponding to a * pushed down join, replace it with the fdw_outerpath, so that we * maintain path for EPQ checks built entirely of local join * strategies. */ if (IsA(joinpath->outerjoinpath, ForeignPath)) { ForeignPath *foreign_path; foreign_path = (ForeignPath *) joinpath->outerjoinpath; - if (foreign_path->path.parent->reloptkind == RELOPT_JOINREL) + if (IS_JOIN_REL(foreign_path->path.parent)) joinpath->outerjoinpath = foreign_path->fdw_outerpath; } if (IsA(joinpath->innerjoinpath, ForeignPath)) { ForeignPath *foreign_path; foreign_path = (ForeignPath *) joinpath->innerjoinpath; - if (foreign_path->path.parent->reloptkind == RELOPT_JOINREL) + if (IS_JOIN_REL(foreign_path->path.parent)) joinpath->innerjoinpath = foreign_path->fdw_outerpath; } return (Path *) joinpath; } return NULL; } diff --git a/src/backend/optimizer/README b/src/backend/optimizer/README index 775bcc3..29f2bb1 100644 --- a/src/backend/optimizer/README +++ b/src/backend/optimizer/README @@ -967,10 +967,51 @@ beneath a Gather node - which we call "partial" paths since they return only a subset of the results in each worker - must be kept separate from ordinary paths (see RelOptInfo's partial_pathlist and the function add_partial_path). One of the keys to making parallel query effective is to run as much of the query in parallel as possible. Therefore, we expect it to generally be desirable to postpone the Gather stage until as near to the top of the plan as possible. Expanding the range of cases in which more work can be pushed below the Gather (and costing them accurately) is likely to keep us busy for a long time to come. + +Partition-wise joins +-------------------- +A join between two similarly partitioned tables can be broken down into joins +between their matching partitions if there exists an equi-join condition +between the partition keys of the joining tables. The equi-join between +partition keys implies that for a given row in a given partition of a given +partitioned table, its joining row, if exists, should exist only in the +matching partition of the other partitioned table; no row from non-matching +partitions in the other partitioned table can join with the given row from the +first table. This condition allows the join between partitioned table to be +broken into joins between the matching partitions. The resultant join is +partitioned in the same way as the joining relations, thus allowing an N-way +join between similarly partitioned tables having equi-join condition between +their partition keys to be broken down into N-way joins between their matching +partitions. This technique of breaking down a join between partition tables +into join between their partitions is called partition-wise join. We will use +term "partitioned relation" for both partitioned table as well as join between +partitioned tables which can use partition-wise join technique. + +Partitioning properties of a partitioned table are stored in +PartitionSchemeData structure. Planner maintains a list of canonical partition +schemes (distinct PartitionSchemeData objects) so that any two partitioned +relations with same partitioning scheme share the same PartitionSchemeData +object. This reduces memory consumed by PartitionSchemeData objects and makes +it easy to compare the partition schemes of joining relations. RelOptInfos of +partitioned relations hold partition key expressions and the RelOptInfos of +the partition relations of that relation. + +Partition-wise joins are planned in two phases + +1. First phase creates the RelOptInfos for joins between matching partitions +and creates join paths for these joins. The join paths for join between +partitions are created using the same techniques as described above. + +2. After creating all possible paths for joins between the partitions, +Append/MergeAppend paths are created to construct join between the partitioned +relations by choosing the one path from each of the RelOptInfos created +in the first phase. Append/Merge append paths with different possible +parameterizations and pathkeys are created based on the paths created for joins +between partitions. diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c index 99b6bc8..ee737f2 100644 --- a/src/backend/optimizer/path/allpaths.c +++ b/src/backend/optimizer/path/allpaths.c @@ -11,22 +11,24 @@ * src/backend/optimizer/path/allpaths.c * *------------------------------------------------------------------------- */ #include "postgres.h" #include #include +#include "miscadmin.h" #include "access/sysattr.h" #include "access/tsmapi.h" +#include "catalog/partition.h" #include "catalog/pg_class.h" #include "catalog/pg_operator.h" #include "catalog/pg_proc.h" #include "foreign/fdwapi.h" #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" #ifdef OPTIMIZER_DEBUG #include "nodes/print.h" #endif #include "optimizer/clauses.h" @@ -37,20 +39,21 @@ #include "optimizer/plancat.h" #include "optimizer/planner.h" #include "optimizer/prep.h" #include "optimizer/restrictinfo.h" #include "optimizer/tlist.h" #include "optimizer/var.h" #include "parser/parse_clause.h" #include "parser/parsetree.h" #include "rewrite/rewriteManip.h" #include "utils/lsyscache.h" +#include "utils/rel.h" /* results of subquery_is_pushdown_safe */ typedef struct pushdown_safety_info { bool *unsafeColumns; /* which output columns are unsafe to use */ bool unsafeVolatile; /* don't push down volatile quals */ bool unsafeLeaky; /* don't push down leaky quals */ } pushdown_safety_info; @@ -119,20 +122,24 @@ static void check_output_expressions(Query *subquery, static void compare_tlist_datatypes(List *tlist, List *colTypes, pushdown_safety_info *safetyInfo); static bool targetIsInAllPartitionLists(TargetEntry *tle, Query *query); static bool qual_is_pushdown_safe(Query *subquery, Index rti, Node *qual, pushdown_safety_info *safetyInfo); static void subquery_push_qual(Query *subquery, RangeTblEntry *rte, Index rti, Node *qual); static void recurse_push_qual(Node *setOp, Query *topquery, RangeTblEntry *rte, Index rti, Node *qual); static void remove_unused_subquery_outputs(Query *subquery, RelOptInfo *rel); +static void add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel, + List *live_childrels); +static void generate_partition_wise_join_paths(PlannerInfo *root, + RelOptInfo *rel); /* * make_one_rel * Finds all possible access paths for executing a query, returning a * single rel that represents the join of all base rels in the query. */ RelOptInfo * make_one_rel(PlannerInfo *root, List *joinlist) { @@ -861,20 +868,44 @@ static void set_append_rel_size(PlannerInfo *root, RelOptInfo *rel, Index rti, RangeTblEntry *rte) { int parentRTindex = rti; bool has_live_children; double parent_rows; double parent_size; double *parent_attrsizes; int nattrs; ListCell *l; + Oid *part_oids = NULL; + int nparts = 0; + + /* Fetch the number of partitions of a partitioned table and their Oids. */ + if (rel->part_scheme) + { + RangeTblEntry *rte = root->simple_rte_array[rel->relid]; + + /* + * We need not lock the relation since it was already locked, either by + * the rewriter or when expand_inherited_rtentry() added it to the + * query's rangetable. + */ + Relation relation = heap_open(rte->relid, NoLock); + PartitionDesc part_desc = RelationGetPartitionDesc(relation); + + part_oids = part_desc->oids; + nparts = part_desc->nparts; + + Assert(part_oids && nparts > 0); + + rel->part_rels = (RelOptInfo **)palloc0(sizeof(RelOptInfo *) * nparts); + heap_close(relation, NoLock); + } /* * Initialize to compute size estimates for whole append relation. * * We handle width estimates by weighting the widths of different child * rels proportionally to their number of rows. This is sensible because * the use of width estimates is mainly to compute the total relation * "footprint" if we have to sort or hash it. To do this, we sum the * total equivalent size (in "double" arithmetic) and then divide by the * total rowcount estimate. This is done separately for the total rel @@ -892,35 +923,84 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel, foreach(l, root->append_rel_list) { AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l); int childRTindex; RangeTblEntry *childRTE; RelOptInfo *childrel; List *childquals; Node *childqual; ListCell *parentvars; ListCell *childvars; + int cnt_parts; /* append_rel_list contains all append rels; ignore others */ if (appinfo->parent_relid != parentRTindex) continue; childRTindex = appinfo->child_relid; childRTE = root->simple_rte_array[childRTindex]; /* * The child rel's RelOptInfo was already created during * add_base_rels_to_query. */ childrel = find_base_rel(root, childRTindex); + + /* + * Recursively save topmost parent's relid in RelOptInfos of + * partitions. + */ + if (rel->top_parent_relids) + childrel->top_parent_relids = rel->top_parent_relids; + else + childrel->top_parent_relids = bms_copy(rel->relids); + Assert(childrel->reloptkind == RELOPT_OTHER_MEMBER_REL); + + /* + * For two partitioned tables with the same partitioning scheme, it is + * assumed that the Oids of matching partitions from both the tables + * are placed at the same position in the array of partition oids in + * respective partition descriptors. Saving the RelOptInfo of a + * partition in the same location as its Oid makes it easy to find the + * RelOptInfos of matching partitions for partition-wise join. + */ + for (cnt_parts = 0; cnt_parts < nparts; cnt_parts++) + { + if (part_oids[cnt_parts] == childRTE->relid) + { + Assert(!rel->part_rels[cnt_parts]); + rel->part_rels[cnt_parts] = childrel; + } + } + + /* + * Copy/Modify targetlist. Partition-wise join technique may join this + * child with a child of another partitioned table, such that this + * child forms the nullable side of the outer join. In such a case, we + * will need the targetlist of this child, even if it's deemed empty. + * Hence set the targetlist before bailing out in case the child is + * proven empty. + * + * NB: the resulting childrel->reltarget->exprs may contain arbitrary + * expressions, which otherwise would not occur in a rel's targetlist. + * Code that might be looking at an appendrel child must cope with + * such. (Normally, a rel's targetlist would only include Vars and + * PlaceHolderVars.) XXX we do not bother to update the cost or width + * fields of childrel->reltarget; not clear if that would be useful. + */ + childrel->reltarget->exprs = (List *) + adjust_appendrel_attrs(root, + (Node *) rel->reltarget->exprs, + appinfo); + /* * We have to copy the parent's targetlist and quals to the child, * with appropriate substitution of variables. However, only the * baserestrictinfo quals are needed before we can check for * constraint exclusion; so do that first and then check to see if we * can disregard this child. * * As of 8.4, the child rel's targetlist might contain non-Var * expressions, which means that substitution into the quals could * produce opportunities for const-simplification, and perhaps even @@ -953,38 +1033,25 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel, if (relation_excluded_by_constraints(root, childrel, childRTE)) { /* * This child need not be scanned, so we can omit it from the * appendrel. */ set_dummy_rel_pathlist(childrel); continue; } - /* - * CE failed, so finish copying/modifying targetlist and join quals. - * - * NB: the resulting childrel->reltarget->exprs may contain arbitrary - * expressions, which otherwise would not occur in a rel's targetlist. - * Code that might be looking at an appendrel child must cope with - * such. (Normally, a rel's targetlist would only include Vars and - * PlaceHolderVars.) XXX we do not bother to update the cost or width - * fields of childrel->reltarget; not clear if that would be useful. - */ + /* CE failed, so finish copying/modifying join quals. */ childrel->joininfo = (List *) adjust_appendrel_attrs(root, (Node *) rel->joininfo, appinfo); - childrel->reltarget->exprs = (List *) - adjust_appendrel_attrs(root, - (Node *) rel->reltarget->exprs, - appinfo); /* * We have to make child entries in the EquivalenceClass data * structures as well. This is needed either if the parent * participates in some eclass joins (because we will want to consider * inner-indexscan joins on the individual children) or if the parent * has useful pathkeys (because we should try to build MergeAppend * paths that produce those sort orderings). */ if (rel->has_eclass_joins || has_useful_pathkeys(root, rel)) @@ -1073,20 +1140,30 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel, } if (child_width <= 0) child_width = get_typavgwidth(exprType(childvar), exprTypmod(childvar)); Assert(child_width > 0); parent_attrsizes[pndx] += child_width * childrel->rows; } } } + /* Should have found all the childrels of a partitioned relation. */ + if (rel->part_scheme) + { + int cnt_parts; + for (cnt_parts = 0; cnt_parts < nparts; cnt_parts++) + if (!rel->part_rels[cnt_parts]) + elog(ERROR, "could not find the RelOptInfo of a partition with oid %u", + part_oids[cnt_parts]); + } + if (has_live_children) { /* * Save the finished size estimates. */ int i; Assert(parent_rows > 0); rel->rows = parent_rows; rel->reltarget->width = rint(parent_size / parent_rows); @@ -1115,41 +1192,32 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel, /* * set_append_rel_pathlist * Build access paths for an "append relation" */ static void set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, Index rti, RangeTblEntry *rte) { int parentRTindex = rti; List *live_childrels = NIL; - List *subpaths = NIL; - bool subpaths_valid = true; - List *partial_subpaths = NIL; - bool partial_subpaths_valid = true; - List *all_child_pathkeys = NIL; - List *all_child_outers = NIL; ListCell *l; /* - * Generate access paths for each member relation, and remember the - * cheapest path for each one. Also, identify all pathkeys (orderings) - * and parameterizations (required_outer sets) available for the member - * relations. + * Generate access paths for each member relation and remember the + * non-dummy children. */ foreach(l, root->append_rel_list) { AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l); int childRTindex; RangeTblEntry *childRTE; RelOptInfo *childrel; - ListCell *lcp; /* append_rel_list contains all append rels; ignore others */ if (appinfo->parent_relid != parentRTindex) continue; /* Re-locate the child RTE and RelOptInfo */ childRTindex = appinfo->child_relid; childRTE = root->simple_rte_array[childRTindex]; childrel = root->simple_rel_array[childRTindex]; @@ -1170,20 +1238,70 @@ set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, /* * If child is dummy, ignore it. */ if (IS_DUMMY_REL(childrel)) continue; /* * Child is live, so add it to the live_childrels list for use below. */ live_childrels = lappend(live_childrels, childrel); + } + + /* Add Append/MergeAppend paths to the "append" relation. */ + add_paths_to_append_rel(root, rel, live_childrels); +} + +/* + * add_paths_to_append_rel + * Generate Append/MergeAppend paths for given "append" relation. An + * "append" relation can be a base parent relation or a join between + * partitioned tables. + * + * The function collects all parameterizations and orderings supported by the + * non-dummy children. For every such parameterization or ordering, it creates + * an append path collecting one path from each non-dummy child with given + * parameterization or ordering. Similarly it collects partial paths from + * non-dummy children to create partial append paths. + */ +static void +add_paths_to_append_rel(PlannerInfo *root, RelOptInfo *rel, + List *live_childrels) +{ + List *subpaths = NIL; + bool subpaths_valid = true; + List *partial_subpaths = NIL; + bool partial_subpaths_valid = true; + List *all_child_pathkeys = NIL; + List *all_child_outers = NIL; + ListCell *l; + + /* An append relation with all its children dummy is dummy. */ + if (live_childrels == NIL) + { + /* Mark the relation as dummy, if not already done so. */ + if (!IS_DUMMY_REL(rel)) + set_dummy_rel_pathlist(rel); + + /* No more paths need to be added. */ + return; + } + + /* + * For every non-dummy child, remember the cheapest path. Also, identify + * all pathkeys (orderings) and parameterizations (required_outer sets) + * available for the non-dummy member relations. + */ + foreach (l, live_childrels) + { + RelOptInfo *childrel = lfirst(l); + ListCell *lcp; /* * If child has an unparameterized cheapest-total path, add that to * the unparameterized Append path we are constructing for the parent. * If not, there's no workable unparameterized path. */ if (childrel->cheapest_total_path->param_info == NULL) subpaths = accumulate_append_subpath(subpaths, childrel->cheapest_total_path); else @@ -2181,27 +2299,34 @@ standard_join_search(PlannerInfo *root, int levels_needed, List *initial_rels) * Determine all possible pairs of relations to be joined at this * level, and build paths for making each one from every available * pair of lower-level relations. */ join_search_one_level(root, lev); /* * Run generate_gather_paths() for each just-processed joinrel. We * could not do this earlier because both regular and partial paths * can get added to a particular joinrel at multiple times within + * join_search_one_level. Similarly, create append paths for joinrels + * which used partition-wise join technique. We can not do this + * earlier because the paths can get added to a relation representing + * join between children at multiple times within * join_search_one_level. After that, we're done creating paths for * the joinrel, so run set_cheapest(). */ foreach(lc, root->join_rel_level[lev]) { rel = (RelOptInfo *) lfirst(lc); + /* Create Append/MergeAppend paths for partition-wise joins. */ + generate_partition_wise_join_paths(root, rel); + /* Create GatherPaths for any useful partial paths for rel */ generate_gather_paths(root, rel); /* Find and save the cheapest paths for this rel */ set_cheapest(rel); #ifdef OPTIMIZER_DEBUG debug_print_rel(root, rel); #endif } @@ -2851,20 +2976,92 @@ remove_unused_subquery_outputs(Query *subquery, RelOptInfo *rel) * OK, we don't need it. Replace the expression with a NULL constant. * Preserve the exposed type of the expression, in case something * looks at the rowtype of the subquery's result. */ tle->expr = (Expr *) makeNullConst(exprType(texpr), exprTypmod(texpr), exprCollation(texpr)); } } +/* + * generate_partition_wise_join_paths + * Create appends paths for given join relation, if partition-wise join + * technique was used for this join. + * + * The function collects the non-dummy children and hands them off to + * add_paths_to_append_rel(), which does the actual work. + * + * This must not be called until after we're done creating all paths for the + * join between children for specified join between parents. (Otherwise, + * add_path might delete a path that some Append/MergeAppend path has a + * reference to.) + */ + +static void +generate_partition_wise_join_paths(PlannerInfo *root, RelOptInfo *rel) +{ + List *live_children = NIL; + int cnt_part; + + /* Handle only join relations. */ + if (!IS_JOIN_REL(rel)) + return; + + /* + * If partition-wise join technique was not used for any of the join + * orders, the join is not partitioned. Reset the partitioning scheme. + * TODO: find a condition when some joining order would use partition-wise + * join technique and other wouldn't. + */ + if (!rel->part_rels) + rel->part_scheme = NULL; + + /* If the relation is not partitioned or is proven dummy, nothing to do. */ + if (!rel->part_scheme || IS_DUMMY_REL(rel)) + return; + + /* Guard against stack overflow due to overly deep partition hierarchy. */ + check_stack_depth(); + + for (cnt_part = 0; cnt_part < rel->part_scheme->nparts; cnt_part++) + { + RelOptInfo *child_rel = rel->part_rels[cnt_part]; + + /* Ignore dummy child. */ + if (!IS_DUMMY_REL(child_rel)) + { + /* Recursively collect the paths from child joinrel. */ + generate_partition_wise_join_paths(root, child_rel); + + /* Find the cheapest of the paths for this rel. */ + set_cheapest(child_rel); + +#ifdef OPTIMIZER_DEBUG + debug_print_rel(root, rel); +#endif + + live_children = lappend(live_children, child_rel); + } + } + + /* + * Create append paths by collecting subpaths from live children. Even if + * there are no live children, we should create an append path with no + * subpaths i.e. a dummy access path. + */ + add_paths_to_append_rel(root, rel, live_children); + + if (live_children) + list_free(live_children); +} + /***************************************************************************** * DEBUG SUPPORT *****************************************************************************/ #ifdef OPTIMIZER_DEBUG static void print_relids(PlannerInfo *root, Relids relids) { int x; diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c index 2a49639..a23da1c 100644 --- a/src/backend/optimizer/path/costsize.c +++ b/src/backend/optimizer/path/costsize.c @@ -119,20 +119,21 @@ bool enable_seqscan = true; bool enable_indexscan = true; bool enable_indexonlyscan = true; bool enable_bitmapscan = true; bool enable_tidscan = true; bool enable_sort = true; bool enable_hashagg = true; bool enable_nestloop = true; bool enable_material = true; bool enable_mergejoin = true; bool enable_hashjoin = true; +bool enable_partition_wise_join = true; typedef struct { PlannerInfo *root; QualCost total; } cost_qual_eval_context; static List *extract_nonindex_conditions(List *qual_clauses, List *indexquals); static MergeScanSelCache *cached_scansel(PlannerInfo *root, RestrictInfo *rinfo, diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c index 0e50ad5..73026a3 100644 --- a/src/backend/optimizer/path/equivclass.c +++ b/src/backend/optimizer/path/equivclass.c @@ -2359,20 +2359,22 @@ eclass_useful_for_merging(PlannerInfo *root, /* * Note we don't test ec_broken; if we did, we'd need a separate code path * to look through ec_sources. Checking the members anyway is OK as a * possibly-overoptimistic heuristic. */ /* If specified rel is a child, we must consider the topmost parent rel */ if (rel->reloptkind == RELOPT_OTHER_MEMBER_REL) relids = find_childrel_top_parent(root, rel)->relids; + else if (rel->reloptkind == RELOPT_OTHER_JOINREL) + relids = rel->top_parent_relids; else relids = rel->relids; /* If rel already includes all members of eclass, no point in searching */ if (bms_is_subset(eclass->ec_relids, relids)) return false; /* To join, we need a member not in the given rel */ foreach(lc, eclass->ec_members) { diff --git a/src/backend/optimizer/path/joinpath.c b/src/backend/optimizer/path/joinpath.c index cc7384f..fae15de 100644 --- a/src/backend/optimizer/path/joinpath.c +++ b/src/backend/optimizer/path/joinpath.c @@ -18,23 +18,33 @@ #include "executor/executor.h" #include "foreign/fdwapi.h" #include "optimizer/cost.h" #include "optimizer/pathnode.h" #include "optimizer/paths.h" /* Hook for plugins to get control in add_paths_to_joinrel() */ set_join_pathlist_hook_type set_join_pathlist_hook = NULL; -#define PATH_PARAM_BY_REL(path, rel) \ +/* + * Paths parameterized by the parent can be considered to be parameterized by + * any of its child. + */ +#define PATH_PARAM_BY_PARENT(path, rel) \ + ((path)->param_info && bms_overlap(PATH_REQ_OUTER(path), \ + (rel)->top_parent_relids)) +#define PATH_PARAM_BY_REL_SELF(path, rel) \ ((path)->param_info && bms_overlap(PATH_REQ_OUTER(path), (rel)->relids)) +#define PATH_PARAM_BY_REL(path, rel) \ + (PATH_PARAM_BY_REL_SELF(path, rel) || PATH_PARAM_BY_PARENT(path, rel)) + static void sort_inner_and_outer(PlannerInfo *root, RelOptInfo *joinrel, RelOptInfo *outerrel, RelOptInfo *innerrel, JoinType jointype, JoinPathExtraData *extra); static void match_unsorted_outer(PlannerInfo *root, RelOptInfo *joinrel, RelOptInfo *outerrel, RelOptInfo *innerrel, JoinType jointype, JoinPathExtraData *extra); static void consider_parallel_nestloop(PlannerInfo *root, RelOptInfo *joinrel, RelOptInfo *outerrel, RelOptInfo *innerrel, @@ -125,38 +135,51 @@ add_paths_to_joinrel(PlannerInfo *root, * directly to the parameter source rel instead of joining to the other * input rel. (But see allow_star_schema_join().) This restriction * reduces the number of parameterized paths we have to deal with at * higher join levels, without compromising the quality of the resulting * plan. We express the restriction as a Relids set that must overlap the * parameterization of any proposed join path. */ foreach(lc, root->join_info_list) { SpecialJoinInfo *sjinfo = (SpecialJoinInfo *) lfirst(lc); + Relids joinrelids; + + /* + * PlannerInfo doesn't contain the SpecialJoinInfos created for joins + * between child relations, even if there is a SpecialJoinInfo node for + * the join between the topmost parents. Hence while calculating Relids + * set representing the restriction, consider relids of topmost parent + * of partitions. + */ + if (joinrel->reloptkind == RELOPT_OTHER_JOINREL) + joinrelids = joinrel->top_parent_relids; + else + joinrelids = joinrel->relids; /* * SJ is relevant to this join if we have some part of its RHS * (possibly not all of it), and haven't yet joined to its LHS. (This * test is pretty simplistic, but should be sufficient considering the * join has already been proven legal.) If the SJ is relevant, it * presents constraints for joining to anything not in its RHS. */ - if (bms_overlap(joinrel->relids, sjinfo->min_righthand) && - !bms_overlap(joinrel->relids, sjinfo->min_lefthand)) + if (bms_overlap(joinrelids, sjinfo->min_righthand) && + !bms_overlap(joinrelids, sjinfo->min_lefthand)) extra.param_source_rels = bms_join(extra.param_source_rels, bms_difference(root->all_baserels, sjinfo->min_righthand)); /* full joins constrain both sides symmetrically */ if (sjinfo->jointype == JOIN_FULL && - bms_overlap(joinrel->relids, sjinfo->min_lefthand) && - !bms_overlap(joinrel->relids, sjinfo->min_righthand)) + bms_overlap(joinrelids, sjinfo->min_lefthand) && + !bms_overlap(joinrelids, sjinfo->min_righthand)) extra.param_source_rels = bms_join(extra.param_source_rels, bms_difference(root->all_baserels, sjinfo->min_lefthand)); } /* * However, when a LATERAL subquery is involved, there will simply not be * any paths for the joinrel that aren't parameterized by whatever the * subquery is parameterized by, unless its parameterization is resolved * within the joinrel. So we might as well allow additional dependencies @@ -272,20 +295,38 @@ try_nestloop_path(PlannerInfo *root, Path *outer_path, Path *inner_path, List *pathkeys, JoinType jointype, JoinPathExtraData *extra) { Relids required_outer; JoinCostWorkspace workspace; /* + * For a join between child relations, if the inner path is parameterized + * by the parent of the outer relation, it can be considered to be + * parameterized by the outer relation. We will be able to create a + * nestloop join path with inner relation parameterized by the outer + * relation by translating the inner path to be parameterized by the outer + * child relation. + */ + if (PATH_PARAM_BY_PARENT(inner_path, outer_path->parent)) + { + inner_path = reparameterize_path_by_child(root, inner_path, + outer_path->parent); + + /* If we could not translate the path, don't produce nest loop path. */ + if (!inner_path) + return; + } + + /* * Check to see if proposed path is still parameterized, and reject if the * parameterization wouldn't be sensible --- unless allow_star_schema_join * says to allow it anyway. Also, we must reject if have_dangerous_phv * doesn't like the look of it, which could only happen if the nestloop is * still parameterized. */ required_outer = calc_nestloop_required_outer(outer_path, inner_path); if (required_outer && ((!bms_overlap(required_outer, extra->param_source_rels) && diff --git a/src/backend/optimizer/path/joinrels.c b/src/backend/optimizer/path/joinrels.c index 01d4fea..da8ad83 100644 --- a/src/backend/optimizer/path/joinrels.c +++ b/src/backend/optimizer/path/joinrels.c @@ -7,38 +7,57 @@ * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * src/backend/optimizer/path/joinrels.c * *------------------------------------------------------------------------- */ #include "postgres.h" +#include "miscadmin.h" +#include "catalog/partition.h" +#include "optimizer/clauses.h" #include "optimizer/joininfo.h" #include "optimizer/pathnode.h" #include "optimizer/paths.h" +#include "optimizer/prep.h" +#include "optimizer/cost.h" #include "utils/memutils.h" static void make_rels_by_clause_joins(PlannerInfo *root, RelOptInfo *old_rel, ListCell *other_rels); static void make_rels_by_clauseless_joins(PlannerInfo *root, RelOptInfo *old_rel, ListCell *other_rels); static bool has_join_restriction(PlannerInfo *root, RelOptInfo *rel); static bool has_legal_joinclause(PlannerInfo *root, RelOptInfo *rel); static bool is_dummy_rel(RelOptInfo *rel); static void mark_dummy_rel(RelOptInfo *rel); static bool restriction_is_constant_false(List *restrictlist, bool only_pushed_down); +static void populate_joinrel_with_paths(PlannerInfo *root, RelOptInfo *rel1, + RelOptInfo *rel2, RelOptInfo *joinrel, + SpecialJoinInfo *sjinfo, List *restrictlist); +static void try_partition_wise_join(PlannerInfo *root, RelOptInfo *rel1, + RelOptInfo *rel2, RelOptInfo *joinrel, + SpecialJoinInfo *parent_sjinfo, + List *parent_restrictlist); +static SpecialJoinInfo * build_child_join_sjinfo(PlannerInfo *root, + SpecialJoinInfo *parent_sjinfo, + List *append_rel_infos1, + List *append_rel_infos2); +static bool have_partkey_equi_join(RelOptInfo *rel1, RelOptInfo *rel2, + JoinType jointype, List *restrictlist); +static int match_expr_to_partition_keys(Expr *expr, RelOptInfo *rel); /* * join_search_one_level * Consider ways to produce join relations containing exactly 'level' * jointree items. (This is one step of the dynamic-programming method * embodied in standard_join_search.) Join rel nodes for each feasible * combination of lower-level rels are created and returned in a list. * Implementation paths are created for each such joinrel, too. * @@ -717,20 +736,44 @@ make_join_rel(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2) /* * If we've already proven this join is empty, we needn't consider any * more paths for it. */ if (is_dummy_rel(joinrel)) { bms_free(joinrelids); return joinrel; } + /* Add paths to the join relation. */ + populate_joinrel_with_paths(root, rel1, rel2, joinrel, sjinfo, + restrictlist); + + /* Apply partition-wise join technique, if possible. */ + try_partition_wise_join(root, rel1, rel2, joinrel, sjinfo, restrictlist); + + bms_free(joinrelids); + + return joinrel; +} + +/* + * populate_joinrel_with_paths + * Add paths joining given input relations to the given joinrel. The + * SpecialJoinInfo provides details about the join and the restrictlist + * contains the join clauses and the other clauses applicable for given pair + * of the joining relations. + */ +static void +populate_joinrel_with_paths(PlannerInfo *root, RelOptInfo *rel1, + RelOptInfo *rel2, RelOptInfo *joinrel, + SpecialJoinInfo *sjinfo, List *restrictlist) +{ /* * Consider paths using each rel as both outer and inner. Depending on * the join type, a provably empty outer or inner rel might mean the join * is provably empty too; in which case throw away any previously computed * paths and mark the join as dummy. (We do it this way since it's * conceivable that dummy-ness of a multi-element join might only be * noticeable for certain construction paths.) * * Also, a provably constant-false join restriction typically means that * we can skip evaluating one or both sides of the join. We do this by @@ -861,27 +904,22 @@ make_join_rel(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2) mark_dummy_rel(rel2); add_paths_to_joinrel(root, joinrel, rel1, rel2, JOIN_ANTI, sjinfo, restrictlist); break; default: /* other values not expected here */ elog(ERROR, "unrecognized join type: %d", (int) sjinfo->jointype); break; } - - bms_free(joinrelids); - - return joinrel; } - /* * have_join_order_restriction * Detect whether the two relations should be joined to satisfy * a join-order restriction arising from special or lateral joins. * * In practice this is always used with have_relevant_joinclause(), and so * could be merged with that function, but it seems clearer to separate the * two concerns. We need this test because there are degenerate cases where * a clauseless join must be performed to satisfy join-order restrictions. * Also, if one rel has a lateral reference to the other, or both are needed @@ -1242,10 +1280,378 @@ restriction_is_constant_false(List *restrictlist, bool only_pushed_down) /* constant NULL is as good as constant FALSE for our purposes */ if (con->constisnull) return true; if (!DatumGetBool(con->constvalue)) return true; } } return false; } + +/* + * TODO: In addition, the comments for individual comments and chunks of code + * need to do a better job explaining how each part of the patch contributes to + * the overall picture. Make a distinction between the partition-wise join as a + * join performed by breaking down join between partitioned tables and + * other-join-rel which is anologous to other-base-rel. + */ + +/* + * Assess whether join between given two partitioned relations can be broken + * down into joins between matching partitions; a technique called + * "partition-wise join" + * + * Partition-wise join is possible when a. Joining relations have same + * partitioning scheme b. There exists an equi-join between the partition keys + * of the two relations. + * + * Partition-wise join is planned in two phases + * + * 1. Create the RelOptInfos for joins between matching partitions and add join + * paths to those. This function is responsible for this phase. + * + * 2. Add Append/MergeAppend paths to the RelOptInfo representing the join + * between the partitioned relations by choosing one path from each of the + * RelOptInfos created in the first phase. The second phase is implemented by + * generate_partition_wise_join_paths(). + * + * The RelOptInfo, SpecialJoinInfo and restrictlist for each child join are + * obtained by translating the respective parent join structures. + */ +static void +try_partition_wise_join(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2, + RelOptInfo *joinrel, SpecialJoinInfo *parent_sjinfo, + List *parent_restrictlist) +{ + int nparts; + int cnt_parts; + PartitionScheme part_scheme; + + + /* Nothing to do, if the join relation is not partitioned. */ + if (!joinrel->part_scheme) + return; + + /* + * If any of the joining relation is proven empty, either the join will be + * empty (INNER join) or will have the inner side all nullified. We take + * care of such cases when creating join paths for parent relations. + * Nothing to be done here. Also, nothing to do, if the join is proven + * empty. + */ + if (IS_DUMMY_REL(rel1) || IS_DUMMY_REL(rel2) || IS_DUMMY_REL(joinrel)) + return; + + /* + * Partitioning scheme in join relation indicates a possibilty that the + * join may be partitioned, but it's not necessary that every join order + * can use partition-wise join technique. If one of joining relations turns + * out to be unpartitioned, this pair of joining relations can not use + * partition-wise join technique. + */ + if (!rel1->part_scheme || !rel2->part_scheme) + return; + + /* + * If an equi-join condition between the partition keys of the joining + * relations does not exist, this pair of joining relations can not use + * partition-wise technique. + */ + if (!have_partkey_equi_join(rel1, rel2, parent_sjinfo->jointype, + parent_restrictlist)) + return; + + /* + * The partition scheme of the join relation should match that of the + * joining relations. + */ + Assert(joinrel->part_scheme == rel1->part_scheme && + joinrel->part_scheme == rel2->part_scheme); + + /* Make sure we have RelOptInfos of the partitions available. */ + Assert(rel1->part_rels && rel2->part_rels); + + /* Guard against stack overflow due to overly deep partition hierarchy. */ + check_stack_depth(); + + part_scheme = joinrel->part_scheme; + nparts = part_scheme->nparts; + + elog(DEBUG3, "join between relations %s and %s is considered for partition-wise join.", + bmsToString(rel1->relids), bmsToString(rel2->relids)); + + /* Allocate the array for child RelOptInfos if not done already. */ + if (!joinrel->part_rels) + joinrel->part_rels = (RelOptInfo **) palloc0(sizeof(RelOptInfo *) * nparts); + + /* + * This joinrel is partitioned, so iterate over the partitions and create + * paths for each one, allowing us to eventually build an append-of-joins + * path for the parent. Since this routine may be called multiple times + * for various join orders, the RelOptInfo needed for each child join may + * or may not already exist, but the paths for this join order definitely + * do not. Note that we don't create any actual AppendPath at this stage; + * it only makes sense to do that at the end, after each possible join + * order has been considered for each child join. The best join order may + * differ from child to child. + */ + for (cnt_parts = 0; cnt_parts < nparts; cnt_parts++) + { + RelOptInfo *child_joinrel; + RelOptInfo *child_rel1 = rel1->part_rels[cnt_parts]; + RelOptInfo *child_rel2 = rel2->part_rels[cnt_parts]; + SpecialJoinInfo *child_sjinfo; + List *child_restrictlist; + List *join_appinfos; + List *appinfos1; + List *appinfos2; + + /* We should have a valid RelOptInfo for partitions being joined. */ + Assert(child_rel1 && child_rel2); + + /* We should never try to join two overlapping sets of rels. */ + Assert(!bms_overlap(child_rel1->relids, child_rel2->relids)); + + appinfos1 = find_appinfos_by_relids(root, child_rel1->relids); + appinfos2 = find_appinfos_by_relids(root, child_rel2->relids); + + /* + * Construct SpecialJoinInfo from parent join relations's + * SpecialJoinInfo. + */ + child_sjinfo = build_child_join_sjinfo(root, parent_sjinfo, + appinfos1, appinfos2); + + /* Construct the parent-child relid map for the join relation. */ + join_appinfos = list_concat(appinfos1, appinfos2); + + /* + * Construct restrictions applicable to the child join from + * those applicable to the parent join. + */ + child_restrictlist = (List *) adjust_join_appendrel_attrs(root, + (Node *)parent_restrictlist, + join_appinfos); + + child_joinrel = joinrel->part_rels[cnt_parts]; + + /* Construct the join relation for given partition of the join. */ + if (!child_joinrel) + { + child_joinrel = build_child_join_rel(root, child_rel1, + child_rel2, joinrel, + child_sjinfo, + child_restrictlist, + join_appinfos); + + joinrel->part_rels[cnt_parts] = child_joinrel; + + } + + /* + * If we've already proven that this join is empty, we needn't consider + * any more paths for it. + */ + if (is_dummy_rel(child_joinrel)) + continue; + + populate_joinrel_with_paths(root, child_rel1, child_rel2, child_joinrel, + child_sjinfo, child_restrictlist); + + /* + * If the child relations themselves are partitioned, try partition-wise join + * recursively. + */ + try_partition_wise_join(root, child_rel1, child_rel2, child_joinrel, + child_sjinfo, child_restrictlist); + } +} + +/* + * Construct the SpecialJoinInfo for the join between children by translating + * SpecialJoinInfo for the join between parents. + */ +static SpecialJoinInfo * +build_child_join_sjinfo(PlannerInfo *root, SpecialJoinInfo *parent_sjinfo, + List *append_rel_infos1, List *append_rel_infos2) +{ + SpecialJoinInfo *sjinfo = copyObject(parent_sjinfo); + + sjinfo->min_lefthand = adjust_child_relids(sjinfo->min_lefthand, + append_rel_infos1); + sjinfo->min_righthand = adjust_child_relids(sjinfo->min_righthand, + append_rel_infos2); + sjinfo->syn_lefthand = adjust_child_relids(sjinfo->syn_lefthand, + append_rel_infos1); + sjinfo->syn_righthand = adjust_child_relids(sjinfo->syn_righthand, + append_rel_infos2); + + /* Replace the Var nodes of parent with those of children in expressions. */ + sjinfo->semi_rhs_exprs = (List *) adjust_join_appendrel_attrs(root, + (Node *) sjinfo->semi_rhs_exprs, + append_rel_infos2); + return sjinfo; +} + +/* + * Replace parent relids by child relids in the given relid set. + */ +Relids +adjust_child_relids(Relids relids, List *append_rel_infos) +{ + ListCell *lc; + foreach (lc, append_rel_infos) + { + AppendRelInfo *appinfo = lfirst(lc); + + /* Remove parent, add child*/ + if (bms_is_member(appinfo->parent_relid, relids)) + { + relids = bms_del_member(relids, appinfo->parent_relid); + relids = bms_add_member(relids, appinfo->child_relid); + } + } + return relids; +} + +/* + * Returns true if there exists an equi-join condition for each pair of + * partition key from given relations being joined. + */ +static bool +have_partkey_equi_join(RelOptInfo *rel1, RelOptInfo *rel2, + JoinType jointype, List *restrictlist) +{ + PartitionScheme part_scheme = rel1->part_scheme; + ListCell *lc; + int cnt_pks; + int num_pks; + bool *pk_has_clause; + + /* + * This function should be called when the joining relations have same + * partitioning scheme. + */ + Assert(rel1->part_scheme == rel2->part_scheme); + Assert(part_scheme); + + num_pks = part_scheme->partnatts; + + pk_has_clause = (bool *) palloc0(sizeof(bool) * num_pks); + + foreach (lc, restrictlist) + { + RestrictInfo *rinfo = lfirst(lc); + OpExpr *opexpr; + Expr *expr1; + Expr *expr2; + int ipk1; + int ipk2; + + /* If processing an outer join, only use its own join clauses. */ + if (IS_OUTER_JOIN(jointype) && rinfo->is_pushed_down) + continue; + + /* Skip clauses which can not be used for a join. */ + if (!rinfo->can_join) + continue; + + /* Skip clauses which are not equality conditions. */ + if (rinfo->hashjoinoperator == InvalidOid && !rinfo->mergeopfamilies) + continue; + + opexpr = (OpExpr *) rinfo->clause; + Assert(is_opclause(opexpr)); + + + /* Match the operands to the relation. */ + if (bms_is_subset(rinfo->left_relids, rel1->relids) && + bms_is_subset(rinfo->right_relids, rel2->relids)) + { + expr1 = linitial(opexpr->args); + expr2 = lsecond(opexpr->args); + } + else if (bms_is_subset(rinfo->left_relids, rel2->relids) && + bms_is_subset(rinfo->right_relids, rel1->relids)) + { + expr1 = lsecond(opexpr->args); + expr2 = linitial(opexpr->args); + } + else + continue; + + /* Associate matching clauses with partition keys. */ + ipk1 = match_expr_to_partition_keys(expr1, rel1); + ipk2 = match_expr_to_partition_keys(expr2, rel2); + + /* + * If the clause refers to different partition keys from + * both relations, it can not be used for partition-wise join. + */ + if (ipk1 != ipk2) + continue; + + /* + * The clause allows partition-wise join if only it uses the same + * operator family as that specified by the partition key. + */ + if (!list_member_oid(rinfo->mergeopfamilies, + part_scheme->partopfamily[ipk1])) + continue; + + /* Mark the partition key as having an equi-join clause. */ + pk_has_clause[ipk1] = true; + } + + /* Check whether every partition key has an equi-join condition. */ + for (cnt_pks = 0; cnt_pks < num_pks; cnt_pks++) + { + if (!pk_has_clause[cnt_pks]) + { + pfree(pk_has_clause); + return false; + } + } + + pfree(pk_has_clause); + return true; +} + +/* + * Find the partition key from the given relation matching the given + * expression. If found, return the index of the partition key, else return -1. + */ +static int +match_expr_to_partition_keys(Expr *expr, RelOptInfo *rel) +{ + int cnt_pks; + int num_pks; + + /* This function should be called only for partitioned relations. */ + Assert(rel->part_scheme); + + num_pks = rel->part_scheme->partnatts; + + /* + * Remove the relabel decoration. We can assume that there is at most one + * RelabelType node; eval_const_expressions() simplifies multiple + * RelabelType nodes into one. + */ + if (IsA(expr, RelabelType)) + expr = (Expr *) ((RelabelType *) expr)->arg; + + for (cnt_pks = 0; cnt_pks < num_pks; cnt_pks++) + { + List *pkexprs = rel->partexprs[cnt_pks]; + ListCell *lc; + + foreach(lc, pkexprs) + { + Expr *pkexpr = lfirst(lc); + if (equal(pkexpr, expr)) + return cnt_pks; + } + } + + return -1; +} diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c index 4436ac1..6996590 100644 --- a/src/backend/optimizer/path/pathkeys.c +++ b/src/backend/optimizer/path/pathkeys.c @@ -1081,26 +1081,38 @@ select_outer_pathkeys_for_merge(PlannerInfo *root, List *mergeclauses, RelOptInfo *joinrel) { List *pathkeys = NIL; int nClauses = list_length(mergeclauses); EquivalenceClass **ecs; int *scores; int necs; ListCell *lc; int j; + Relids relids; /* Might have no mergeclauses */ if (nClauses == 0) return NIL; /* + * For a child join relation, use parent relids to find potential + * join partners from equivalence classes. A potential join partner of + * parent also indicates potential join partner of the child. By using + * parent relids we eliminate duplicates arising out of many children. + */ + if (joinrel->reloptkind == RELOPT_OTHER_JOINREL) + relids = joinrel->top_parent_relids; + else + relids = joinrel->relids; + + /* * Make arrays of the ECs used by the mergeclauses (dropping any * duplicates) and their "popularity" scores. */ ecs = (EquivalenceClass **) palloc(nClauses * sizeof(EquivalenceClass *)); scores = (int *) palloc(nClauses * sizeof(int)); necs = 0; foreach(lc, mergeclauses) { RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc); @@ -1126,21 +1138,21 @@ select_outer_pathkeys_for_merge(PlannerInfo *root, continue; /* compute score */ score = 0; foreach(lc2, oeclass->ec_members) { EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2); /* Potential future join partner? */ if (!em->em_is_const && !em->em_is_child && - !bms_overlap(em->em_relids, joinrel->relids)) + !bms_overlap(em->em_relids, relids)) score++; } ecs[necs] = oeclass; scores[necs] = score; necs++; } /* * Find out if we have all the ECs mentioned in query_pathkeys; if so we diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index 32f4031..b221e2c 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -235,21 +235,22 @@ static Plan *prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys, const AttrNumber *reqColIdx, bool adjust_tlist_in_place, int *p_numsortkeys, AttrNumber **p_sortColIdx, Oid **p_sortOperators, Oid **p_collations, bool **p_nullsFirst); static EquivalenceMember *find_ec_member_for_tle(EquivalenceClass *ec, TargetEntry *tle, Relids relids); -static Sort *make_sort_from_pathkeys(Plan *lefttree, List *pathkeys); +static Sort *make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, + Relids relids); static Sort *make_sort_from_groupcols(List *groupcls, AttrNumber *grpColIdx, Plan *lefttree); static Material *make_material(Plan *lefttree); static WindowAgg *make_windowagg(List *tlist, Index winref, int partNumCols, AttrNumber *partColIdx, Oid *partOperators, int ordNumCols, AttrNumber *ordColIdx, Oid *ordOperators, int frameOptions, Node *startOffset, Node *endOffset, Plan *lefttree); static Group *make_group(List *tlist, List *qual, int numGroupCols, @@ -1507,21 +1508,21 @@ create_sort_plan(PlannerInfo *root, SortPath *best_path, int flags) Plan *subplan; /* * We don't want any excess columns in the sorted tuples, so request a * smaller tlist. Otherwise, since Sort doesn't project, tlist * requirements pass through. */ subplan = create_plan_recurse(root, best_path->subpath, flags | CP_SMALL_TLIST); - plan = make_sort_from_pathkeys(subplan, best_path->path.pathkeys); + plan = make_sort_from_pathkeys(subplan, best_path->path.pathkeys, NULL); copy_generic_path_info(&plan->plan, (Path *) best_path); return plan; } /* * create_group_plan * * Create a Group plan for 'best_path' and (recursively) plans @@ -3517,31 +3518,33 @@ create_mergejoin_plan(PlannerInfo *root, List *innerpathkeys; int nClauses; Oid *mergefamilies; Oid *mergecollations; int *mergestrategies; bool *mergenullsfirst; int i; ListCell *lc; ListCell *lop; ListCell *lip; + Path *outer_path = best_path->jpath.outerjoinpath; + Path *inner_path = best_path->jpath.innerjoinpath; /* * MergeJoin can project, so we don't have to demand exact tlists from the * inputs. However, if we're intending to sort an input's result, it's * best to request a small tlist so we aren't sorting more data than * necessary. */ - outer_plan = create_plan_recurse(root, best_path->jpath.outerjoinpath, + outer_plan = create_plan_recurse(root, outer_path, (best_path->outersortkeys != NIL) ? CP_SMALL_TLIST : 0); - inner_plan = create_plan_recurse(root, best_path->jpath.innerjoinpath, + inner_plan = create_plan_recurse(root, inner_path, (best_path->innersortkeys != NIL) ? CP_SMALL_TLIST : 0); /* Sort join qual clauses into best execution order */ /* NB: do NOT reorder the mergeclauses */ joinclauses = order_qual_clauses(root, best_path->jpath.joinrestrictinfo); /* Get the join qual clauses (in plain expression form) */ /* Any pseudoconstant clauses are ignored here */ if (IS_OUTER_JOIN(best_path->jpath.jointype)) { @@ -3573,48 +3576,52 @@ create_mergejoin_plan(PlannerInfo *root, otherclauses = (List *) replace_nestloop_params(root, (Node *) otherclauses); } /* * Rearrange mergeclauses, if needed, so that the outer variable is always * on the left; mark the mergeclause restrictinfos with correct * outer_is_left status. */ mergeclauses = get_switched_clauses(best_path->path_mergeclauses, - best_path->jpath.outerjoinpath->parent->relids); + outer_path->parent->relids); /* * Create explicit sort nodes for the outer and inner paths if necessary. */ if (best_path->outersortkeys) { + Relids outer_relids = outer_path->parent->relids; Sort *sort = make_sort_from_pathkeys(outer_plan, - best_path->outersortkeys); + best_path->outersortkeys, + outer_relids); label_sort_with_costsize(root, sort, -1.0); outer_plan = (Plan *) sort; outerpathkeys = best_path->outersortkeys; } else - outerpathkeys = best_path->jpath.outerjoinpath->pathkeys; + outerpathkeys = outer_path->pathkeys; if (best_path->innersortkeys) { + Relids inner_relids = inner_path->parent->relids; Sort *sort = make_sort_from_pathkeys(inner_plan, - best_path->innersortkeys); + best_path->innersortkeys, + inner_relids); label_sort_with_costsize(root, sort, -1.0); inner_plan = (Plan *) sort; innerpathkeys = best_path->innersortkeys; } else - innerpathkeys = best_path->jpath.innerjoinpath->pathkeys; + innerpathkeys = inner_path->pathkeys; /* * If specified, add a materialize node to shield the inner plan from the * need to handle mark/restore. */ if (best_path->materialize_inner) { Plan *matplan = (Plan *) make_material(inner_plan); /* @@ -5330,25 +5337,25 @@ prepare_sort_from_pathkeys(Plan *lefttree, List *pathkeys, /* * We shouldn't be trying to sort by an equivalence class that * contains a constant, so no need to consider such cases any * further. */ if (em->em_is_const) continue; /* - * Ignore child members unless they match the rel being + * Ignore child members unless they belong to the rel being * sorted. */ if (em->em_is_child && - !bms_equal(em->em_relids, relids)) + !bms_is_subset(em->em_relids, relids)) continue; sortexpr = em->em_expr; exprvars = pull_var_clause((Node *) sortexpr, PVC_INCLUDE_AGGREGATES | PVC_INCLUDE_WINDOWFUNCS | PVC_INCLUDE_PLACEHOLDERS); foreach(k, exprvars) { if (!tlist_member_ignore_relabel(lfirst(k), tlist)) @@ -5445,57 +5452,58 @@ find_ec_member_for_tle(EquivalenceClass *ec, Expr *emexpr; /* * We shouldn't be trying to sort by an equivalence class that * contains a constant, so no need to consider such cases any further. */ if (em->em_is_const) continue; /* - * Ignore child members unless they match the rel being sorted. + * Ignore child members unless they belong to the rel being sorted. */ if (em->em_is_child && - !bms_equal(em->em_relids, relids)) + !bms_is_subset(em->em_relids, relids)) continue; /* Match if same expression (after stripping relabel) */ emexpr = em->em_expr; while (emexpr && IsA(emexpr, RelabelType)) emexpr = ((RelabelType *) emexpr)->arg; if (equal(emexpr, tlexpr)) return em; } return NULL; } /* * make_sort_from_pathkeys * Create sort plan to sort according to given pathkeys * * 'lefttree' is the node which yields input tuples * 'pathkeys' is the list of pathkeys by which the result is to be sorted + * 'relids' is the set of relations required by prepare_sort_from_pathkeys() */ static Sort * -make_sort_from_pathkeys(Plan *lefttree, List *pathkeys) +make_sort_from_pathkeys(Plan *lefttree, List *pathkeys, Relids relids) { int numsortkeys; AttrNumber *sortColIdx; Oid *sortOperators; Oid *collations; bool *nullsFirst; /* Compute sort column info, and adjust lefttree as needed */ lefttree = prepare_sort_from_pathkeys(lefttree, pathkeys, - NULL, + relids, NULL, false, &numsortkeys, &sortColIdx, &sortOperators, &collations, &nullsFirst); /* Now build the Sort node */ return make_sort(lefttree, numsortkeys, diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c index 193b2c9..6c703b8 100644 --- a/src/backend/optimizer/prep/prepunion.c +++ b/src/backend/optimizer/prep/prepunion.c @@ -1873,20 +1873,71 @@ adjust_appendrel_attrs(PlannerInfo *root, Node *node, AppendRelInfo *appinfo) appinfo); } result = (Node *) newnode; } else result = adjust_appendrel_attrs_mutator(node, &context); return result; } +/* + * find_appinfos_by_relids + * Find AppendRelInfo structures for all relations specified by relids. + */ +List * +find_appinfos_by_relids(PlannerInfo *root, Relids relids) +{ + ListCell *lc; + List *appinfo_list = NIL; + + foreach (lc, root->append_rel_list) + { + AppendRelInfo *appinfo = lfirst(lc); + + if (bms_is_member(appinfo->child_relid, relids)) + appinfo_list = lappend(appinfo_list, appinfo); + } + + Assert(list_length(appinfo_list) == bms_num_members(relids)); + return appinfo_list; +} + +/* + * adjust_join_appendrel_attrs + * + * Replace the parent references in the given node by the child references + * specified by the list of AppendRelInfo. + * + * This function is a wrapper around adjust_appendrel_attrs() which handles + * only one AppendRelInfo at a time. + * + * TODO: measure how much memory we are leaking, how does the memory usage grow + * for N-way joins between tables having many many partitions with N very + * large. + */ + +Node * +adjust_join_appendrel_attrs(PlannerInfo *root, Node *node, + List *append_rel_infos) +{ + ListCell *lc; + + foreach (lc, append_rel_infos) + { + AppendRelInfo *appinfo = lfirst(lc); + node = adjust_appendrel_attrs(root, node, appinfo); + } + + return node; +} + static Node * adjust_appendrel_attrs_mutator(Node *node, adjust_appendrel_attrs_context *context) { AppendRelInfo *appinfo = context->appinfo; if (node == NULL) return NULL; if (IsA(node, Var)) { diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c index abb7507..0463369 100644 --- a/src/backend/optimizer/util/pathnode.c +++ b/src/backend/optimizer/util/pathnode.c @@ -16,21 +16,23 @@ #include #include "miscadmin.h" #include "nodes/nodeFuncs.h" #include "optimizer/clauses.h" #include "optimizer/cost.h" #include "optimizer/pathnode.h" #include "optimizer/paths.h" #include "optimizer/planmain.h" +#include "optimizer/prep.h" #include "optimizer/restrictinfo.h" +#include "optimizer/tlist.h" #include "optimizer/var.h" #include "parser/parsetree.h" #include "utils/lsyscache.h" #include "utils/selfuncs.h" typedef enum { COSTS_EQUAL, /* path costs are fuzzily equal */ COSTS_BETTER1, /* first path is cheaper than second */ @@ -3202,10 +3204,165 @@ reparameterize_path(PlannerInfo *root, Path *path, rel, spath->subpath, spath->path.pathkeys, required_outer); } default: break; } return NULL; } + +/* + * reparameterize_path_by_child + * Given a path parameterized by the parent of the given relation, + * translate the path to be parameterized by the given child relation. + * + * The function creates a new path of the same type as the given path, but + * parameterized by the given child relation. If it can not reparameterize the + * path as required, it returns NULL. + * + * The cost, number of rows, width and parallel path properties depend upon + * path->parent, which does not change during the translation. Hence those + * members are copied as they are. + */ + +Path * +reparameterize_path_by_child(PlannerInfo *root, Path *path, + RelOptInfo *child_rel) +{ + Path *new_path; + ParamPathInfo *new_ppi; + ParamPathInfo *old_ppi; + List *child_aris; + + /* + * If the path is not parameterized by parent of the given relation, it + * doesn't need reparameterization. + */ + if (!path->param_info || + !bms_overlap(PATH_REQ_OUTER(path), child_rel->top_parent_relids)) + return path; + + switch (nodeTag(path)) + { + case T_Path: + new_path = makeNode(Path); + memcpy(new_path, path, sizeof(Path)); + break; + + case T_HashPath: + new_path = (Path *) makeNode(HashPath); + memcpy(new_path, path, sizeof(HashPath)); + break; + + case T_MergePath: + new_path = (Path *) makeNode(MergePath); + memcpy(new_path, path, sizeof(MergePath)); + break; + + case T_NestPath: + new_path = (Path *) makeNode(NestPath); + memcpy(new_path, path, sizeof(NestPath)); + break; + + case T_IndexPath: + new_path = (Path *) makeNode(IndexPath); + memcpy(new_path, path, sizeof(IndexPath)); + break; + + case T_AppendPath: + new_path = (Path *) makeNode(AppendPath); + memcpy(new_path, path, sizeof(AppendPath)); + break; + + /* + * TODO: + * If this method of translation is fine add more path types here. + */ + + default: + /* Path type unsupported by this function. */ + return NULL; + } + + /* + * Gather AppendRelInfos of the base partition relations in the outer child + * relation. We need those for translating parent path to that of child by + * substituting parent Var nodes and relids with those of children. + */ + child_aris = find_appinfos_by_relids(root, child_rel->relids); + + /* Adjust the parameterization information. */ + old_ppi = new_path->param_info; + new_ppi = makeNode(ParamPathInfo); + new_ppi->ppi_req_outer = adjust_child_relids(bms_copy(old_ppi->ppi_req_outer), + child_aris); + new_ppi->ppi_rows = old_ppi->ppi_rows; + new_ppi->ppi_clauses = (List *) adjust_join_appendrel_attrs(root, + (Node *) old_ppi->ppi_clauses, + child_aris); + + /* Adjust the path target. */ + new_path->pathtarget = copy_pathtarget(new_path->pathtarget); + new_path->pathtarget->exprs = (List *) adjust_join_appendrel_attrs(root, + (Node *) new_path->pathtarget->exprs, + child_aris); + new_path->param_info = new_ppi; + + /* + * Change parameterization of subpaths recursively. Also carry out any + * pathtype specific adjustments. + */ + switch (nodeTag(path)) + { + case T_HashPath: + case T_MergePath: + case T_NestPath: + { + JoinPath *jpath = (JoinPath *)new_path; + + jpath->outerjoinpath = reparameterize_path_by_child(root, + jpath->outerjoinpath, + child_rel); + jpath->innerjoinpath = reparameterize_path_by_child(root, + jpath->innerjoinpath, + child_rel); + jpath->joinrestrictinfo = (List *) adjust_join_appendrel_attrs(root, + (Node *) jpath->joinrestrictinfo, + child_aris); + } + break; + + case T_AppendPath: + { + AppendPath *apath = (AppendPath *)new_path; + List *subpaths = NIL; + ListCell *lc; + + foreach (lc, apath->subpaths) + subpaths = lappend(subpaths, + reparameterize_path_by_child(root, + lfirst(lc), + child_rel)); + apath->subpaths = subpaths; + } + + case T_IndexPath: + { + IndexPath *ipath = (IndexPath *)new_path; + + ipath->indexquals = (List *) adjust_join_appendrel_attrs(root, + (Node *) ipath->indexquals, + child_aris); + ipath->indexquals = (List *) adjust_join_appendrel_attrs(root, + (Node *) ipath->indexorderbys, + child_aris); + } + + default: + /* Nothing to do. */ + break; + } + + return new_path; +} diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c index f8bfa4b..a0e17d5 100644 --- a/src/backend/optimizer/util/plancat.c +++ b/src/backend/optimizer/util/plancat.c @@ -404,20 +404,35 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent, } else { rel->serverid = InvalidOid; rel->fdwroutine = NULL; } /* Collect info about relation's foreign keys, if relevant */ get_relation_foreign_keys(root, rel, relation); + /* + * Lookup partition scheme for the given relation. Only parent relations + * can be partitioned. + */ + if (inhparent) + rel->part_scheme = find_partition_scheme(root, relation); + else + rel->part_scheme = NULL; + + if (rel->part_scheme) + rel->partexprs = build_baserel_partition_key_exprs(relation, + rel->relid); + else + rel->partexprs = NULL; + heap_close(relation, NoLock); /* * Allow a plugin to editorialize on the info we obtained from the * catalogs. Actions might include altering the assumed relation size, * removing an index, or adding a hypothetical index to the indexlist. */ if (get_relation_info_hook) (*get_relation_info_hook) (root, relationObjectId, inhparent, rel); } diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c index deef560..93275b4 100644 --- a/src/backend/optimizer/util/relnode.c +++ b/src/backend/optimizer/util/relnode.c @@ -8,29 +8,35 @@ * * * IDENTIFICATION * src/backend/optimizer/util/relnode.c * *------------------------------------------------------------------------- */ #include "postgres.h" #include "miscadmin.h" +#include "catalog/heap.h" +#include "catalog/partition.h" #include "optimizer/clauses.h" #include "optimizer/cost.h" +#include "nodes/makefuncs.h" #include "optimizer/pathnode.h" #include "optimizer/paths.h" #include "optimizer/placeholder.h" #include "optimizer/plancat.h" +#include "optimizer/prep.h" #include "optimizer/restrictinfo.h" #include "optimizer/tlist.h" +#include "rewrite/rewriteManip.h" #include "utils/hsearch.h" +#include "utils/rel.h" typedef struct JoinHashEntry { Relids join_relids; /* hash key --- MUST BE FIRST */ RelOptInfo *join_rel; } JoinHashEntry; static void build_joinrel_tlist(PlannerInfo *root, RelOptInfo *joinrel, RelOptInfo *input_rel); @@ -40,20 +46,25 @@ static List *build_joinrel_restrictlist(PlannerInfo *root, RelOptInfo *inner_rel); static void build_joinrel_joinlist(RelOptInfo *joinrel, RelOptInfo *outer_rel, RelOptInfo *inner_rel); static List *subbuild_joinrel_restrictlist(RelOptInfo *joinrel, List *joininfo_list, List *new_restrictlist); static List *subbuild_joinrel_joinlist(RelOptInfo *joinrel, List *joininfo_list, List *new_joininfo); +static void set_foreign_rel_properties(RelOptInfo *joinrel, + RelOptInfo *outer_rel, RelOptInfo *inner_rel); +static void build_joinrel_partition_info(RelOptInfo *joinrel, + RelOptInfo *outer_rel, RelOptInfo *inner_rel, + JoinType jointype); /* * setup_simple_rel_arrays * Prepare the arrays we use for quickly accessing base relations. */ void setup_simple_rel_arrays(PlannerInfo *root) { Index rti; @@ -130,20 +141,24 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptKind reloptkind) rel->serverid = InvalidOid; rel->userid = rte->checkAsUser; rel->useridiscurrent = false; rel->fdwroutine = NULL; rel->fdw_private = NULL; rel->baserestrictinfo = NIL; rel->baserestrictcost.startup = 0; rel->baserestrictcost.per_tuple = 0; rel->joininfo = NIL; rel->has_eclass_joins = false; + rel->part_scheme = NULL; + rel->partexprs = NULL; + rel->top_parent_relids = NULL; + rel->part_rels = NULL; /* Check type of rtable entry */ switch (rte->rtekind) { case RTE_RELATION: /* Table --- retrieve statistics from the system catalogs */ get_relation_info(root, rte->relid, rte->inh, rel); break; case RTE_SUBQUERY: case RTE_FUNCTION: @@ -307,20 +322,70 @@ find_join_rel(PlannerInfo *root, Relids relids) if (bms_equal(rel->relids, relids)) return rel; } } return NULL; } /* + * set_foreign_rel_properties + * Set up foreign-join fields if outer and inner relation are foreign + * tables (or joins) belonging to the same server and assigned to the same + * user to check access permissions as. + * + * In addition to an exact match of userid, we allow the case where one side + * has zero userid (implying current user) and the other side has explicit + * userid that happens to equal the current user; but in that case, pushdown of + * the join is only valid for the current user. The useridiscurrent field + * records whether we had to make such an assumption for this join or any + * sub-join. + * + * Otherwise these fields are left invalid, so GetForeignJoinPaths will not be + * called for the join relation. + * + */ +static void +set_foreign_rel_properties(RelOptInfo *joinrel, RelOptInfo *outer_rel, + RelOptInfo *inner_rel) +{ + if (OidIsValid(outer_rel->serverid) && + inner_rel->serverid == outer_rel->serverid) + { + if (inner_rel->userid == outer_rel->userid) + { + joinrel->serverid = outer_rel->serverid; + joinrel->userid = outer_rel->userid; + joinrel->useridiscurrent = outer_rel->useridiscurrent || inner_rel->useridiscurrent; + joinrel->fdwroutine = outer_rel->fdwroutine; + } + else if (!OidIsValid(inner_rel->userid) && + outer_rel->userid == GetUserId()) + { + joinrel->serverid = outer_rel->serverid; + joinrel->userid = outer_rel->userid; + joinrel->useridiscurrent = true; + joinrel->fdwroutine = outer_rel->fdwroutine; + } + else if (!OidIsValid(outer_rel->userid) && + inner_rel->userid == GetUserId()) + { + joinrel->serverid = outer_rel->serverid; + joinrel->userid = inner_rel->userid; + joinrel->useridiscurrent = true; + joinrel->fdwroutine = outer_rel->fdwroutine; + } + } +} + +/* * build_join_rel * Returns relation entry corresponding to the union of two given rels, * creating a new relation entry if none already exists. * * 'joinrelids' is the Relids set that uniquely identifies the join * 'outer_rel' and 'inner_rel' are relation nodes for the relations to be * joined * 'sjinfo': join context info * 'restrictlist_ptr': result variable. If not NULL, *restrictlist_ptr * receives the list of RestrictInfo nodes that apply to this @@ -356,21 +421,25 @@ build_join_rel(PlannerInfo *root, joinrel, outer_rel, inner_rel); return joinrel; } /* * Nope, so make one. */ joinrel = makeNode(RelOptInfo); + + Assert(!IS_OTHER_REL(outer_rel->reloptkind) && + !IS_OTHER_REL(inner_rel->reloptkind)); joinrel->reloptkind = RELOPT_JOINREL; + joinrel->relids = bms_copy(joinrelids); joinrel->rows = 0; /* cheap startup cost is interesting iff not all tuples to be retrieved */ joinrel->consider_startup = (root->tuple_fraction > 0); joinrel->consider_param_startup = false; joinrel->consider_parallel = false; joinrel->reltarget = create_empty_pathtarget(); joinrel->pathlist = NIL; joinrel->ppilist = NIL; joinrel->partial_pathlist = NIL; @@ -402,61 +471,27 @@ build_join_rel(PlannerInfo *root, joinrel->serverid = InvalidOid; joinrel->userid = InvalidOid; joinrel->useridiscurrent = false; joinrel->fdwroutine = NULL; joinrel->fdw_private = NULL; joinrel->baserestrictinfo = NIL; joinrel->baserestrictcost.startup = 0; joinrel->baserestrictcost.per_tuple = 0; joinrel->joininfo = NIL; joinrel->has_eclass_joins = false; + joinrel->part_scheme = NULL; + joinrel->partexprs = NULL; + joinrel->top_parent_relids = NULL; + joinrel->part_rels = NULL; - /* - * Set up foreign-join fields if outer and inner relation are foreign - * tables (or joins) belonging to the same server and assigned to the same - * user to check access permissions as. In addition to an exact match of - * userid, we allow the case where one side has zero userid (implying - * current user) and the other side has explicit userid that happens to - * equal the current user; but in that case, pushdown of the join is only - * valid for the current user. The useridiscurrent field records whether - * we had to make such an assumption for this join or any sub-join. - * - * Otherwise these fields are left invalid, so GetForeignJoinPaths will - * not be called for the join relation. - */ - if (OidIsValid(outer_rel->serverid) && - inner_rel->serverid == outer_rel->serverid) - { - if (inner_rel->userid == outer_rel->userid) - { - joinrel->serverid = outer_rel->serverid; - joinrel->userid = outer_rel->userid; - joinrel->useridiscurrent = outer_rel->useridiscurrent || inner_rel->useridiscurrent; - joinrel->fdwroutine = outer_rel->fdwroutine; - } - else if (!OidIsValid(inner_rel->userid) && - outer_rel->userid == GetUserId()) - { - joinrel->serverid = outer_rel->serverid; - joinrel->userid = outer_rel->userid; - joinrel->useridiscurrent = true; - joinrel->fdwroutine = outer_rel->fdwroutine; - } - else if (!OidIsValid(outer_rel->userid) && - inner_rel->userid == GetUserId()) - { - joinrel->serverid = outer_rel->serverid; - joinrel->userid = inner_rel->userid; - joinrel->useridiscurrent = true; - joinrel->fdwroutine = outer_rel->fdwroutine; - } - } + /* Compute information relevant to the foreign relations. */ + set_foreign_rel_properties(joinrel, outer_rel, inner_rel); /* * Create a new tlist containing just the vars that need to be output from * this join (ie, are needed for higher joinclauses or final output). * * NOTE: the tlist order for a join rel will depend on which pair of outer * and inner rels we first try to build it from. But the contents should * be the same regardless. */ build_joinrel_tlist(root, joinrel, outer_rel); @@ -468,20 +503,24 @@ build_join_rel(PlannerInfo *root, * sets of any PlaceHolderVars computed here to direct_lateral_relids, so * now we can finish computing that. This is much like the computation of * the transitively-closed lateral_relids in min_join_parameterization, * except that here we *do* have to consider the added PHVs. */ joinrel->direct_lateral_relids = bms_del_members(joinrel->direct_lateral_relids, joinrel->relids); if (bms_is_empty(joinrel->direct_lateral_relids)) joinrel->direct_lateral_relids = NULL; + /* Store the partition information. */ + build_joinrel_partition_info(joinrel, outer_rel, inner_rel, + sjinfo->jointype); + /* * Construct restrict and join clause lists for the new joinrel. (The * caller might or might not need the restrictlist, but I need it anyway * for set_joinrel_size_estimates().) */ restrictlist = build_joinrel_restrictlist(root, joinrel, outer_rel, inner_rel); if (restrictlist_ptr) *restrictlist_ptr = restrictlist; build_joinrel_joinlist(joinrel, outer_rel, inner_rel); @@ -510,58 +549,168 @@ build_join_rel(PlannerInfo *root, * assume this doesn't matter, because we should hit all the same baserels * and joinclauses while building up to this joinrel no matter which we * take; therefore, we should make the same decision here however we get * here. */ if (inner_rel->consider_parallel && outer_rel->consider_parallel && is_parallel_safe(root, (Node *) restrictlist) && is_parallel_safe(root, (Node *) joinrel->reltarget->exprs)) joinrel->consider_parallel = true; - /* - * Add the joinrel to the query's joinrel list, and store it into the - * auxiliary hashtable if there is one. NB: GEQO requires us to append - * the new joinrel to the end of the list! - */ - root->join_rel_list = lappend(root->join_rel_list, joinrel); - - if (root->join_rel_hash) - { - JoinHashEntry *hentry; - bool found; - - hentry = (JoinHashEntry *) hash_search(root->join_rel_hash, - &(joinrel->relids), - HASH_ENTER, - &found); - Assert(!found); - hentry->join_rel = joinrel; - } + /* Add the joinrel to the query's PlannerInfo. */ + add_join_rel(root, joinrel); /* * Also, if dynamic-programming join search is active, add the new joinrel * to the appropriate sublist. Note: you might think the Assert on number * of members should be for equality, but some of the level 1 rels might * have been joinrels already, so we can only assert <=. */ if (root->join_rel_level) { Assert(root->join_cur_level > 0); Assert(root->join_cur_level <= bms_num_members(joinrel->relids)); root->join_rel_level[root->join_cur_level] = lappend(root->join_rel_level[root->join_cur_level], joinrel); } return joinrel; } /* + * build_child_join_rel + * Builds RelOptInfo for joining given two child relations from RelOptInfo + * representing the join between their parents. + * + * 'outer_rel' and 'inner_rel' are the RelOptInfos of child relations being + * joined. + * 'parent_joinrel' is the RelOptInfo representing the join between parent + * relations. Most of the members of new RelOptInfo are produced by + * translating corresponding members of this RelOptInfo. + * 'sjinfo': context info for child join + * 'restrictlist': list of RestrictInfo nodes that apply to this particular + * pair of joinable relations. + * 'join_appinfos': list of AppendRelInfo nodes for base child relations involved + * in this join. + */ +RelOptInfo * +build_child_join_rel(PlannerInfo *root, RelOptInfo *outer_rel, + RelOptInfo *inner_rel, RelOptInfo *parent_joinrel, + SpecialJoinInfo *sjinfo, List *restrictlist, + List *join_appinfos) +{ + List *tmp_exprs; + RelOptInfo *joinrel = makeNode(RelOptInfo); + + joinrel->reloptkind = RELOPT_OTHER_JOINREL; + joinrel->relids = bms_union(outer_rel->relids, inner_rel->relids); + joinrel->rows = 0; + /* cheap startup cost is interesting iff not all tuples to be retrieved */ + joinrel->consider_startup = (root->tuple_fraction > 0); + joinrel->consider_param_startup = false; + joinrel->consider_parallel = false; + joinrel->reltarget = create_empty_pathtarget(); + joinrel->pathlist = NIL; + joinrel->ppilist = NIL; + joinrel->partial_pathlist = NIL; + joinrel->cheapest_startup_path = NULL; + joinrel->cheapest_total_path = NULL; + joinrel->cheapest_unique_path = NULL; + joinrel->cheapest_parameterized_paths = NIL; + joinrel->direct_lateral_relids = NULL; + joinrel->lateral_relids = NULL; + joinrel->relid = 0; /* indicates not a baserel */ + joinrel->rtekind = RTE_JOIN; + joinrel->min_attr = 0; + joinrel->max_attr = 0; + joinrel->attr_needed = NULL; + joinrel->attr_widths = NULL; + joinrel->lateral_vars = NIL; + joinrel->lateral_referencers = NULL; + joinrel->indexlist = NIL; + joinrel->pages = 0; + joinrel->tuples = 0; + joinrel->allvisfrac = 0; + joinrel->subroot = NULL; + joinrel->subplan_params = NIL; + joinrel->serverid = InvalidOid; + joinrel->userid = InvalidOid; + joinrel->useridiscurrent = false; + joinrel->fdwroutine = NULL; + joinrel->fdw_private = NULL; + joinrel->baserestrictinfo = NIL; + joinrel->baserestrictcost.startup = 0; + joinrel->baserestrictcost.per_tuple = 0; + joinrel->joininfo = NIL; + joinrel->has_eclass_joins = false; + joinrel->part_scheme = NULL; + joinrel->partexprs = NULL; + joinrel->top_parent_relids = NULL; + joinrel->part_rels = NULL; + + + /* Only joins between other relations land here. */ + Assert(IS_OTHER_REL(outer_rel->reloptkind) && + IS_OTHER_REL(inner_rel->reloptkind)); + + joinrel->top_parent_relids = bms_union(outer_rel->top_parent_relids, + inner_rel->top_parent_relids); + + /* Compute information relevant to foreign relations. */ + set_foreign_rel_properties(joinrel, outer_rel, inner_rel); + + /* Translate targetlist and joininfo. */ + joinrel->reltarget = copy_pathtarget(parent_joinrel->reltarget); + tmp_exprs = joinrel->reltarget->exprs; + joinrel->reltarget->exprs = (List *) adjust_join_appendrel_attrs(root, + (Node *) tmp_exprs, + join_appinfos); + joinrel->joininfo = (List *) adjust_join_appendrel_attrs(root, + (Node *) parent_joinrel->joininfo, + join_appinfos); + + + /* + * Lateral relids referred in child join will be same as that referred in + * the parent relation. + */ + joinrel->direct_lateral_relids = (Relids) bms_copy(parent_joinrel->direct_lateral_relids); + joinrel->lateral_relids = (Relids) bms_copy(parent_joinrel->lateral_relids); + + /* + * If the parent joinrel has pending equivalence classes, so does the + * child. + */ + joinrel->has_eclass_joins = parent_joinrel->has_eclass_joins; + + /* Is the join between partitions itself partitioned? */ + build_joinrel_partition_info(joinrel, outer_rel, inner_rel, sjinfo->jointype); + + /* + * Set estimates of the joinrel's size. + */ + set_joinrel_size_estimates(root, joinrel, outer_rel, inner_rel, sjinfo, + restrictlist); + + /* Child joinrel is parallel safe if parent is parallel safe. */ + joinrel->consider_parallel = parent_joinrel->consider_parallel; + + /* We build the join only once. */ + Assert(!find_join_rel(root, joinrel->relids)); + + /* Add the relation to the PlannerInfo. */ + add_join_rel(root, joinrel); + + return joinrel; +} + +/* * min_join_parameterization * * Determine the minimum possible parameterization of a joinrel, that is, the * set of other rels it contains LATERAL references to. We save this value in * the join's RelOptInfo. This function is split out of build_join_rel() * because join_is_legal() needs the value to check a prospective join. */ Relids min_join_parameterization(PlannerInfo *root, Relids joinrelids, @@ -1313,10 +1462,118 @@ get_appendrel_parampathinfo(RelOptInfo *appendrel, Relids required_outer) /* Else build the ParamPathInfo */ ppi = makeNode(ParamPathInfo); ppi->ppi_req_outer = required_outer; ppi->ppi_rows = 0; ppi->ppi_clauses = NIL; appendrel->ppilist = lappend(appendrel->ppilist, ppi); return ppi; } + +/* + * add_join_rel + * Add given join relation to the list of join relations in the given + * PlannerInfo. Also add it to the auxiliary hashtable if there is one. + */ +void +add_join_rel(PlannerInfo *root, RelOptInfo *joinrel) +{ + /* GEQO requires us to append the new joinrel to the end of the list! */ + root->join_rel_list = lappend(root->join_rel_list, joinrel); + + /* store it into the auxiliary hashtable if there is one. */ + if (root->join_rel_hash) + { + JoinHashEntry *hentry; + bool found; + + hentry = (JoinHashEntry *) hash_search(root->join_rel_hash, + &(joinrel->relids), + HASH_ENTER, + &found); + Assert(!found); + hentry->join_rel = joinrel; + } +} + +/* + * build_joinrel_partition_info + * If the join between given partitioned relations is possibly partitioned + * set the partitioning scheme and partition keys expressions for the + * join. + * + * If the two relations have same partitioning scheme, their join may be + * partitioned and will follow the same partitioning scheme as the joining + * relations. + */ +static void +build_joinrel_partition_info(RelOptInfo *joinrel, RelOptInfo *outer_rel, + RelOptInfo *inner_rel, JoinType jointype) +{ + int num_pks; + int cnt; + + /* Nothing to do if partition-wise join technique is disabled. */ + if (!enable_partition_wise_join) + { + joinrel->part_scheme = NULL; + return; + } + + /* + * The join is not partitioned, if any of the relations being joined are + * not partitioned or they do not have same partitioning scheme. + */ + if (!outer_rel->part_scheme || !inner_rel->part_scheme || + outer_rel->part_scheme != inner_rel->part_scheme) + { + joinrel->part_scheme = NULL; + return; + } + + /* + * This function will be called only once for each joinrel, hence it should + * not have partition scheme, partition key expressions and array for + * storing child relations set. + */ + Assert(!joinrel->part_scheme && !joinrel->partexprs && + !joinrel->part_rels); + + /* + * Join relation is partitioned using same partitioning scheme as the + * joining relations. + */ + joinrel->part_scheme = outer_rel->part_scheme; + num_pks = joinrel->part_scheme->partnatts; + + /* + * Construct partition keys for the join. + * + * An INNER join between two partitioned relations is partition by key + * expressions from both the relations. For tables A and B partitioned by a and b + * respectively, (A INNER JOIN B ON A.a = B.b) is partitioned by both A.a + * and B.b. + * + * An OUTER join like (A LEFT JOIN B ON A.a = B.b) may produce rows with + * B.b NULL. These rows may not fit the partitioning conditions imposed on + * B.b. Hence, strictly speaking, the join is not partitioned by B.b. + * Strictly speaking, partition keys of an OUTER join should include + * partition key expressions from the OUTER side only. Consider a join like + * (A LEFT JOIN B on (A.a = B.b) LEFT JOIN C ON B.b = C.c. If we do not + * include B.b as partition key expression for (AB), it prohibits us from + * using partition-wise join when joining (AB) with C as there is no + * equi-join between partition keys of joining relations. But two NULL + * values are never equal and no two rows from mis-matching partitions can + * join. Hence it's safe to include B.b as partition key expression for + * (AB), even though rows in (AB) are not strictly partitioned by B.b. + */ + joinrel->partexprs = (List **) palloc0(sizeof(List *) * num_pks); + for (cnt = 0; cnt < num_pks; cnt++) + { + List *pkexpr = list_copy(outer_rel->partexprs[cnt]); + + pkexpr = list_concat(pkexpr, + list_copy(inner_rel->partexprs[cnt])); + joinrel->partexprs[cnt] = pkexpr; + } +} diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index 56943f2..16b2eac 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -3405,21 +3405,23 @@ estimate_num_groups(PlannerInfo *root, List *groupExprs, double input_rows, else { /* not time to process varinfo2 yet */ newvarinfos = lcons(varinfo2, newvarinfos); } } /* * Sanity check --- don't divide by zero if empty relation. */ - Assert(rel->reloptkind == RELOPT_BASEREL); + Assert(rel->reloptkind == RELOPT_BASEREL || + rel->reloptkind == RELOPT_OTHER_MEMBER_REL); + if (rel->tuples > 0) { /* * Clamp to size of rel, or size of rel / 10 if multiple Vars. The * fudge factor is because the Vars are probably correlated but we * don't know by how much. We should never clamp to less than the * largest ndistinct value for any of the Vars, though, since * there will surely be at least that many groups. */ double clamp = rel->tuples; diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index ce4eef9..edc7e58 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -870,20 +870,29 @@ static struct config_bool ConfigureNamesBool[] = }, { {"enable_hashjoin", PGC_USERSET, QUERY_TUNING_METHOD, gettext_noop("Enables the planner's use of hash join plans."), NULL }, &enable_hashjoin, true, NULL, NULL, NULL }, + { + {"enable_partition_wise_join", PGC_USERSET, QUERY_TUNING_METHOD, + gettext_noop("Enables partition-wise join."), + NULL + }, + &enable_partition_wise_join, + true, + NULL, NULL, NULL + }, { {"geqo", PGC_USERSET, QUERY_TUNING_GEQO, gettext_noop("Enables genetic query optimization."), gettext_noop("This algorithm attempts to do planning without " "exhaustive searching.") }, &enable_geqo, true, NULL, NULL, NULL diff --git a/src/include/catalog/partition.h b/src/include/catalog/partition.h index 81a4b91..19b7744 100644 --- a/src/include/catalog/partition.h +++ b/src/include/catalog/partition.h @@ -39,22 +39,57 @@ typedef struct BoundCollectionData *BoundCollection; * is list partitioned. Whereas in case of a range partitioned table, they * are ordered to match the ascending order of partition ranges. */ typedef struct PartitionDescData { int nparts; /* Number of partitions */ Oid *oids; /* OIDs of partitions */ BoundCollection bounds; /* collection of list or range bounds */ } PartitionDescData; +/* + * Partitioning scheme + * Structure to hold partitioning scheme for a given relation. + * + * Multiple relations may be partitioned in the same way. The relations + * resulting from joining such relations may be partitioned in the same way as + * the joining relations. Similarly, relations derived from such relations by + * grouping, sorting may be partitioned in the same way as the underlying + * scan relations. All such relations partitioned in the same way share the + * partitioning scheme. + * + * PlannerInfo stores a list of distinct "canonical" partitioning schemes. + * RelOptInfo of a partitioned relation holds the pointer to "canonical" + * partitioning scheme. + */ +typedef struct PartitionSchemeData +{ + /* Information about partitions */ + int nparts; /* number of partitions */ + BoundCollection bounds; /* Partition bounds/lists */ + + /* Information about partition keys */ + char strategy; /* partition strategy */ + int16 partnatts; /* number of partition attributes */ + Oid *partopfamily; /* OIDs of operator families */ + Oid *partopcintype; /* OIDs of opclass declared input data types */ + Oid *key_types; /* OIDs of partition key data types. */ + int32 *key_typmods; /* typmods of partition keys. */ + Oid *key_collations; /* OIDs of collations of partition keys. */ +} PartitionSchemeData; + typedef struct PartitionDescData *PartitionDesc; typedef struct PartitionTreeNodeData *PartitionTreeNode; +typedef struct PartitionSchemeData *PartitionScheme; + +/* Include here to avoid circular dependency with relation.h. */ +struct PlannerInfo; /* relcache support for partition key information */ extern void RelationBuildPartitionKey(Relation relation); /* Partition key inquiry functions */ extern int get_partition_key_strategy(PartitionKey key); extern int get_partition_key_natts(PartitionKey key); extern List *get_partition_key_exprs(PartitionKey key); /* Partition key inquiry functions - for a given column */ @@ -77,11 +112,16 @@ extern List *get_qual_from_partbound(Relation rel, Relation parent, Node *bound) extern List *RelationGetPartitionQual(Relation rel, bool recurse); /* For tuple routing */ extern PartitionTreeNode RelationGetPartitionTreeNode(Relation rel); extern List *get_leaf_partition_oids_v2(PartitionTreeNode ptnode); extern int get_partition_for_tuple(PartitionTreeNode ptnode, TupleTableSlot *slot, EState *estate, Oid *failed_at); +extern List **build_baserel_partition_key_exprs(Relation relation, + Index varno); +extern PartitionScheme find_partition_scheme(struct PlannerInfo *root, + Relation rel); + #endif /* PARTITION_H */ diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h index 3a1255a..e79fb09 100644 --- a/src/include/nodes/relation.h +++ b/src/include/nodes/relation.h @@ -8,20 +8,21 @@ * Portions Copyright (c) 1994, Regents of the University of California * * src/include/nodes/relation.h * *------------------------------------------------------------------------- */ #ifndef RELATION_H #define RELATION_H #include "access/sdir.h" +#include "catalog/partition.h" #include "lib/stringinfo.h" #include "nodes/params.h" #include "nodes/parsenodes.h" #include "storage/block.h" /* * Relids * Set of relation identifiers (indexes into the rangetable). */ @@ -256,20 +257,23 @@ typedef struct PlannerInfo List *query_pathkeys; /* desired pathkeys for query_planner() */ List *group_pathkeys; /* groupClause pathkeys, if any */ List *window_pathkeys; /* pathkeys of bottom window, if any */ List *distinct_pathkeys; /* distinctClause pathkeys, if any */ List *sort_pathkeys; /* sortClause pathkeys, if any */ List *initial_rels; /* RelOptInfos we are now trying to join */ + List *part_schemes; /* Canonicalised partition schemes + * used in the query. */ + /* Use fetch_upper_rel() to get any particular upper rel */ List *upper_rels[UPPERREL_FINAL + 1]; /* upper-rel RelOptInfos */ /* Result tlists chosen by grouping_planner for upper-stage processing */ struct PathTarget *upper_targets[UPPERREL_FINAL + 1]; /* * grouping_planner passes back its final processed targetlist here, for * use in relabeling the topmost tlist of the finished Plan. */ @@ -345,20 +349,25 @@ typedef struct PlannerInfo * is present in the query join tree but the members are not. The member * RTEs and otherrels are used to plan the scans of the individual tables or * subqueries of the append set; then the parent baserel is given Append * and/or MergeAppend paths comprising the best paths for the individual * member rels. (See comments for AppendRelInfo for more information.) * * At one time we also made otherrels to represent join RTEs, for use in * handling join alias Vars. Currently this is not needed because all join * alias Vars are expanded to non-aliased form during preprocess_expression. * + * We also have relations representing joins between child relations of + * different partitioned tables. These relations are not added to + * join_rel_level lists as they are not joined directly by the dynamic + * programming algorithm. + * * There is also a RelOptKind for "upper" relations, which are RelOptInfos * that describe post-scan/join processing steps, such as aggregation. * Many of the fields in these RelOptInfos are meaningless, but their Path * fields always hold Paths showing ways to do that processing step. * * Lastly, there is a RelOptKind for "dead" relations, which are base rels * that we have proven we don't need to join after all. * * Parts of this data structure are specific to various scan and join * mechanisms. It didn't seem worth creating new node types for them. @@ -464,24 +473,33 @@ typedef struct PlannerInfo * We store baserestrictcost in the RelOptInfo (for base relations) because * we know we will need it at least once (to price the sequential scan) * and may need it multiple times to price index scans. *---------- */ typedef enum RelOptKind { RELOPT_BASEREL, RELOPT_JOINREL, RELOPT_OTHER_MEMBER_REL, + RELOPT_OTHER_JOINREL, RELOPT_UPPER_REL, RELOPT_DEADREL } RelOptKind; +#define IS_OTHER_REL(reloptkind) \ + ((reloptkind) == RELOPT_OTHER_MEMBER_REL || \ + (reloptkind) == RELOPT_OTHER_JOINREL) + +#define IS_JOIN_REL(rel) \ + ((rel->reloptkind) == RELOPT_JOINREL || \ + (rel->reloptkind) == RELOPT_OTHER_JOINREL) + typedef struct RelOptInfo { NodeTag type; RelOptKind reloptkind; /* all relations included in this RelOptInfo */ Relids relids; /* set of base relids (rangetable indexes) */ /* size estimates generated by planner */ @@ -535,20 +553,37 @@ typedef struct RelOptInfo struct FdwRoutine *fdwroutine; void *fdw_private; /* used by various scans and joins: */ List *baserestrictinfo; /* RestrictInfo structures (if base * rel) */ QualCost baserestrictcost; /* cost of evaluating the above */ List *joininfo; /* RestrictInfo structures for join clauses * involving this rel */ bool has_eclass_joins; /* T means joininfo is incomplete */ + + /* For partitioned relations. */ + PartitionScheme part_scheme; /* Partitioning scheme. */ + struct RelOptInfo **part_rels; /* Array of RelOptInfos of partitions, + * stored in the same order as bounds + * or lists in PartitionScheme. + */ + List **partexprs; /* Array of list of partition key + * expressions. For base relations + * these are one element lists. For + * join there may be as many elements + * as the number of joining + * relations. + */ + + /* Set only for "other" base or join relations. */ + Relids top_parent_relids; /* Relids of topmost parents. */ } RelOptInfo; /* * IndexOptInfo * Per-index information for planning/optimization * * indexkeys[], indexcollations[], opfamily[], and opcintype[] * each have ncolumns entries. * * sortopfamily[], reverse_sort[], and nulls_first[] likewise have diff --git a/src/include/optimizer/cost.h b/src/include/optimizer/cost.h index 2a4df2f..1069726 100644 --- a/src/include/optimizer/cost.h +++ b/src/include/optimizer/cost.h @@ -59,20 +59,21 @@ extern bool enable_seqscan; extern bool enable_indexscan; extern bool enable_indexonlyscan; extern bool enable_bitmapscan; extern bool enable_tidscan; extern bool enable_sort; extern bool enable_hashagg; extern bool enable_nestloop; extern bool enable_material; extern bool enable_mergejoin; extern bool enable_hashjoin; +extern bool enable_partition_wise_join; extern int constraint_exclusion; extern double clamp_row_est(double nrows); extern double index_pages_fetched(double tuples_fetched, BlockNumber pages, double index_pages, PlannerInfo *root); extern void cost_seqscan(Path *path, PlannerInfo *root, RelOptInfo *baserel, ParamPathInfo *param_info); extern void cost_samplescan(Path *path, PlannerInfo *root, RelOptInfo *baserel, ParamPathInfo *param_info); extern void cost_index(IndexPath *path, PlannerInfo *root, diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h index 71d9154..3c2a72e 100644 --- a/src/include/optimizer/pathnode.h +++ b/src/include/optimizer/pathnode.h @@ -222,20 +222,22 @@ extern ModifyTablePath *create_modifytable_path(PlannerInfo *root, List *rowMarks, OnConflictExpr *onconflict, int epqParam); extern LimitPath *create_limit_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath, Node *limitOffset, Node *limitCount, int64 offset_est, int64 count_est); extern Path *reparameterize_path(PlannerInfo *root, Path *path, Relids required_outer, double loop_count); +extern Path *reparameterize_path_by_child(PlannerInfo *root, Path *path, + RelOptInfo *child_rel); /* * prototypes for relnode.c */ extern void setup_simple_rel_arrays(PlannerInfo *root); extern RelOptInfo *build_simple_rel(PlannerInfo *root, int relid, RelOptKind reloptkind); extern RelOptInfo *find_base_rel(PlannerInfo *root, int relid); extern RelOptInfo *find_join_rel(PlannerInfo *root, Relids relids); extern RelOptInfo *build_join_rel(PlannerInfo *root, @@ -260,12 +262,17 @@ extern ParamPathInfo *get_baserel_parampathinfo(PlannerInfo *root, Relids required_outer); extern ParamPathInfo *get_joinrel_parampathinfo(PlannerInfo *root, RelOptInfo *joinrel, Path *outer_path, Path *inner_path, SpecialJoinInfo *sjinfo, Relids required_outer, List **restrict_clauses); extern ParamPathInfo *get_appendrel_parampathinfo(RelOptInfo *appendrel, Relids required_outer); +extern RelOptInfo *build_child_join_rel(PlannerInfo *root, + RelOptInfo *outer_rel, RelOptInfo *inner_rel, + RelOptInfo *parent_joinrel, SpecialJoinInfo *sjinfo, + List *restrictlist, List *join_appinfos); +extern void add_join_rel(PlannerInfo *root, RelOptInfo *joinrel); #endif /* PATHNODE_H */ diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h index 44abe83..e57a166 100644 --- a/src/include/optimizer/paths.h +++ b/src/include/optimizer/paths.h @@ -212,11 +212,13 @@ extern List *make_inner_pathkeys_for_merge(PlannerInfo *root, List *mergeclauses, List *outer_pathkeys); extern List *truncate_useless_pathkeys(PlannerInfo *root, RelOptInfo *rel, List *pathkeys); extern bool has_useful_pathkeys(PlannerInfo *root, RelOptInfo *rel); extern PathKey *make_canonical_pathkey(PlannerInfo *root, EquivalenceClass *eclass, Oid opfamily, int strategy, bool nulls_first); +extern Relids adjust_child_relids(Relids relids, List *append_rel_infos); + #endif /* PATHS_H */ diff --git a/src/include/optimizer/prep.h b/src/include/optimizer/prep.h index fb35b68..a7f6271 100644 --- a/src/include/optimizer/prep.h +++ b/src/include/optimizer/prep.h @@ -21,20 +21,23 @@ /* * prototypes for prepjointree.c */ extern void pull_up_sublinks(PlannerInfo *root); extern void inline_set_returning_functions(PlannerInfo *root); extern void pull_up_subqueries(PlannerInfo *root); extern void flatten_simple_union_all(PlannerInfo *root); extern void reduce_outer_joins(PlannerInfo *root); extern Relids get_relids_in_jointree(Node *jtnode, bool include_joins); extern Relids get_relids_for_join(PlannerInfo *root, int joinrelid); +extern Node *adjust_join_appendrel_attrs(PlannerInfo *root, Node *node, + List *append_rel_infos); +extern List *find_appinfos_by_relids(PlannerInfo *root, Relids relids); /* * prototypes for prepqual.c */ extern Node *negate_clause(Node *node); extern Expr *canonicalize_qual(Expr *qual); /* * prototypes for prepsecurity.c */ diff --git a/src/test/regress/expected/partition_join.out b/src/test/regress/expected/partition_join.out new file mode 100644 index 0000000..b56eba9 --- /dev/null +++ b/src/test/regress/expected/partition_join.out @@ -0,0 +1,6663 @@ +-- +-- PARTITION_JOIN +-- Test partition-wise join between partitioned tables +-- +-- +-- partitioned by a single column +-- +CREATE TABLE prt1 (a int, b int, c varchar) PARTITION BY RANGE(a); +CREATE TABLE prt1_p1 PARTITION OF prt1 FOR VALUES START (0) END (250); +CREATE TABLE prt1_p3 PARTITION OF prt1 FOR VALUES START (500) END (600); +CREATE TABLE prt1_p2 PARTITION OF prt1 FOR VALUES START (250) END (500); +INSERT INTO prt1 SELECT i, i, to_char(i, 'FM0000') FROM generate_series(0, 599, 2) i; +ANALYZE prt1; +ANALYZE prt1_p1; +ANALYZE prt1_p2; +ANALYZE prt1_p3; +-- TODO: This table is created only for testing the results. Remove once +-- results are tested. +CREATE TABLE uprt1 AS SELECT * FROM prt1; +CREATE TABLE prt2 (a int, b int, c varchar) PARTITION BY RANGE(b); +CREATE TABLE prt2_p1 PARTITION OF prt2 FOR VALUES START (0) END (250); +CREATE TABLE prt2_p2 PARTITION OF prt2 FOR VALUES START (250) END (500); +CREATE TABLE prt2_p3 PARTITION OF prt2 FOR VALUES START (500) END (600); +INSERT INTO prt2 SELECT i, i, to_char(i, 'FM0000') FROM generate_series(0, 599, 3) i; +-- TODO: This table is created only for testing the results. Remove once +-- results are tested. +ANALYZE prt2; +ANALYZE prt2_p1; +ANALYZE prt2_p2; +ANALYZE prt2_p3; +CREATE TABLE uprt2 AS SELECT * FROM prt2; +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.a % 25 = 0 ORDER BY t1.a, t2.b; + QUERY PLAN +--------------------------------------------------------- + Sort + Output: t1.a, t1.c, t2.b, t2.c + Sort Key: t1.a + -> Append + -> Hash Join + Output: t1.a, t1.c, t2.b, t2.c + Hash Cond: (t2.b = t1.a) + -> Seq Scan on public.prt2_p1 t2 + Output: t2.b, t2.c + -> Hash + Output: t1.a, t1.c + -> Seq Scan on public.prt1_p1 t1 + Output: t1.a, t1.c + Filter: ((t1.a % 25) = 0) + -> Hash Join + Output: t1_2.a, t1_2.c, t2_1.b, t2_1.c + Hash Cond: (t2_1.b = t1_2.a) + -> Seq Scan on public.prt2_p2 t2_1 + Output: t2_1.b, t2_1.c + -> Hash + Output: t1_2.a, t1_2.c + -> Seq Scan on public.prt1_p2 t1_2 + Output: t1_2.a, t1_2.c + Filter: ((t1_2.a % 25) = 0) + -> Hash Join + Output: t1_1.a, t1_1.c, t2_2.b, t2_2.c + Hash Cond: (t2_2.b = t1_1.a) + -> Seq Scan on public.prt2_p3 t2_2 + Output: t2_2.b, t2_2.c + -> Hash + Output: t1_1.a, t1_1.c + -> Seq Scan on public.prt1_p3 t1_1 + Output: t1_1.a, t1_1.c + Filter: ((t1_1.a % 25) = 0) +(34 rows) + +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.a % 25 = 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) + +SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1 t1, uprt2 t2 WHERE t1.a = t2.b AND t1.a % 25 = 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) + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b; + QUERY PLAN +--------------------------------------------------------- + Sort + Output: t1.a, t1.c, t2.b, t2.c + Sort Key: t1.a, t2.b + -> Append + -> Hash Right Join + Output: t1.a, t1.c, t2.b, t2.c + Hash Cond: (t2.b = t1.a) + -> Seq Scan on public.prt2_p1 t2 + Output: t2.b, t2.c + -> Hash + Output: t1.a, t1.c + -> Seq Scan on public.prt1_p1 t1 + Output: t1.a, t1.c + Filter: ((t1.a % 25) = 0) + -> Hash Right Join + Output: t1_2.a, t1_2.c, t2_1.b, t2_1.c + Hash Cond: (t2_1.b = t1_2.a) + -> Seq Scan on public.prt2_p2 t2_1 + Output: t2_1.b, t2_1.c + -> Hash + Output: t1_2.a, t1_2.c + -> Seq Scan on public.prt1_p2 t1_2 + Output: t1_2.a, t1_2.c + Filter: ((t1_2.a % 25) = 0) + -> Hash Right Join + Output: t1_1.a, t1_1.c, t2_2.b, t2_2.c + Hash Cond: (t2_2.b = t1_1.a) + -> Seq Scan on public.prt2_p3 t2_2 + Output: t2_2.b, t2_2.c + -> Hash + Output: t1_1.a, t1_1.c + -> Seq Scan on public.prt1_p3 t1_1 + Output: t1_1.a, t1_1.c + Filter: ((t1_1.a % 25) = 0) +(34 rows) + +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b; + a | c | b | c +-----+------+-----+------ + 0 | 0000 | 0 | 0000 + 50 | 0050 | | + 100 | 0100 | | + 150 | 0150 | 150 | 0150 + 200 | 0200 | | + 250 | 0250 | | + 300 | 0300 | 300 | 0300 + 350 | 0350 | | + 400 | 0400 | | + 450 | 0450 | 450 | 0450 + 500 | 0500 | | + 550 | 0550 | | +(12 rows) + +SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1 t1 LEFT JOIN uprt2 t2 ON t1.a = t2.b WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b; + a | c | b | c +-----+------+-----+------ + 0 | 0000 | 0 | 0000 + 50 | 0050 | | + 100 | 0100 | | + 150 | 0150 | 150 | 0150 + 200 | 0200 | | + 250 | 0250 | | + 300 | 0300 | 300 | 0300 + 350 | 0350 | | + 400 | 0400 | | + 450 | 0450 | 450 | 0450 + 500 | 0500 | | + 550 | 0550 | | +(12 rows) + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b; + QUERY PLAN +--------------------------------------------------------------- + Sort + Output: t1.a, t1.c, t2.b, t2.c + Sort Key: t1.a, t2.b + -> Result + Output: t1.a, t1.c, t2.b, t2.c + -> Append + -> Hash Right Join + Output: t2.b, t2.c, t1.a, t1.c + Hash Cond: (t1.a = t2.b) + -> Seq Scan on public.prt1_p1 t1 + Output: t1.a, t1.c + -> Hash + Output: t2.b, t2.c + -> Seq Scan on public.prt2_p1 t2 + Output: t2.b, t2.c + Filter: ((t2.b % 25) = 0) + -> Hash Right Join + Output: t2_1.b, t2_1.c, t1_2.a, t1_2.c + Hash Cond: (t1_2.a = t2_1.b) + -> Seq Scan on public.prt1_p2 t1_2 + Output: t1_2.a, t1_2.c + -> Hash + Output: t2_1.b, t2_1.c + -> Seq Scan on public.prt2_p2 t2_1 + Output: t2_1.b, t2_1.c + Filter: ((t2_1.b % 25) = 0) + -> Hash Right Join + Output: t2_2.b, t2_2.c, t1_1.a, t1_1.c + Hash Cond: (t1_1.a = t2_2.b) + -> Seq Scan on public.prt1_p3 t1_1 + Output: t1_1.a, t1_1.c + -> Hash + Output: t2_2.b, t2_2.c + -> Seq Scan on public.prt2_p3 t2_2 + Output: t2_2.b, t2_2.c + Filter: ((t2_2.b % 25) = 0) +(36 rows) + +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b WHERE t2.b % 25 = 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) + +SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1 t1 RIGHT JOIN uprt2 t2 ON t1.a = t2.b WHERE t2.b % 25 = 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) + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE prt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM prt2 WHERE prt2.b % 25 = 0) t2 ON (t1.a = t2.b) ORDER BY t1.a, t2.b; + QUERY PLAN +------------------------------------------------------------------ + Sort + Output: prt1_p1.a, prt1_p1.c, prt2_p1.b, prt2_p1.c + Sort Key: prt1_p1.a, prt2_p1.b + -> Append + -> Hash Full Join + Output: prt1_p1.a, prt1_p1.c, prt2_p1.b, prt2_p1.c + Hash Cond: (prt1_p1.a = prt2_p1.b) + -> Seq Scan on public.prt1_p1 + Output: prt1_p1.a, prt1_p1.c + Filter: ((prt1_p1.a % 25) = 0) + -> Hash + Output: prt2_p1.b, prt2_p1.c + -> Seq Scan on public.prt2_p1 + Output: prt2_p1.b, prt2_p1.c + Filter: ((prt2_p1.b % 25) = 0) + -> Hash Full Join + Output: prt1_p2.a, prt1_p2.c, prt2_p2.b, prt2_p2.c + Hash Cond: (prt1_p2.a = prt2_p2.b) + -> Seq Scan on public.prt1_p2 + Output: prt1_p2.a, prt1_p2.c + Filter: ((prt1_p2.a % 25) = 0) + -> Hash + Output: prt2_p2.b, prt2_p2.c + -> Seq Scan on public.prt2_p2 + Output: prt2_p2.b, prt2_p2.c + Filter: ((prt2_p2.b % 25) = 0) + -> Hash Full Join + Output: prt1_p3.a, prt1_p3.c, prt2_p3.b, prt2_p3.c + Hash Cond: (prt1_p3.a = prt2_p3.b) + -> Seq Scan on public.prt1_p3 + Output: prt1_p3.a, prt1_p3.c + Filter: ((prt1_p3.a % 25) = 0) + -> Hash + Output: prt2_p3.b, prt2_p3.c + -> Seq Scan on public.prt2_p3 + Output: prt2_p3.b, prt2_p3.c + Filter: ((prt2_p3.b % 25) = 0) +(37 rows) + +SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE prt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM prt2 WHERE prt2.b % 25 = 0) t2 ON (t1.a = t2.b) ORDER BY t1.a, t2.b; + a | c | b | c +-----+------+-----+------ + 0 | 0000 | 0 | 0000 + 50 | 0050 | | + 100 | 0100 | | + 150 | 0150 | 150 | 0150 + 200 | 0200 | | + 250 | 0250 | | + 300 | 0300 | 300 | 0300 + 350 | 0350 | | + 400 | 0400 | | + 450 | 0450 | 450 | 0450 + 500 | 0500 | | + 550 | 0550 | | + | | 75 | 0075 + | | 225 | 0225 + | | 375 | 0375 + | | 525 | 0525 +(16 rows) + +SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM uprt1 t1 WHERE t1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM uprt2 t2 WHERE t2.b % 25 = 0) t2 ON (t1.a = t2.b) ORDER BY t1.a, t2.b; + a | c | b | c +-----+------+-----+------ + 0 | 0000 | 0 | 0000 + 50 | 0050 | | + 100 | 0100 | | + 150 | 0150 | 150 | 0150 + 200 | 0200 | | + 250 | 0250 | | + 300 | 0300 | 300 | 0300 + 350 | 0350 | | + 400 | 0400 | | + 450 | 0450 | 450 | 0450 + 500 | 0500 | | + 550 | 0550 | | + | | 75 | 0075 + | | 225 | 0225 + | | 375 | 0375 + | | 525 | 0525 +(16 rows) + +-- Cases with non-nullable expressions in subquery results; +-- make sure these go to null as expected +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT 50 phv, * FROM prt1 WHERE prt1.a % 25 = 0) t1 FULL JOIN (SELECT 75 phv, * FROM prt2 WHERE prt2.b % 25 = 0) t2 ON (t1.a = t2.b) WHERE t1.phv = t1.b OR t2.phv = t2.b ORDER BY t1.a, t2.b; + QUERY PLAN +------------------------------------------------------------------ + Sort + Output: prt1_p1.a, prt1_p1.c, prt2_p1.b, prt2_p1.c + Sort Key: prt1_p1.a, prt2_p1.b + -> Append + -> Hash Full Join + Output: prt1_p1.a, prt1_p1.c, prt2_p1.b, prt2_p1.c + Hash Cond: (prt1_p1.a = prt2_p1.b) + Filter: (((50) = prt1_p1.b) OR ((75) = prt2_p1.b)) + -> Seq Scan on public.prt1_p1 + Output: prt1_p1.a, prt1_p1.c, prt1_p1.b, 50 + Filter: ((prt1_p1.a % 25) = 0) + -> Hash + Output: prt2_p1.b, prt2_p1.c, (75) + -> Seq Scan on public.prt2_p1 + Output: prt2_p1.b, prt2_p1.c, 75 + Filter: ((prt2_p1.b % 25) = 0) + -> Hash Full Join + Output: prt1_p2.a, prt1_p2.c, prt2_p2.b, prt2_p2.c + Hash Cond: (prt1_p2.a = prt2_p2.b) + Filter: (((50) = prt1_p2.b) OR ((75) = prt2_p2.b)) + -> Seq Scan on public.prt1_p2 + Output: prt1_p2.a, prt1_p2.c, prt1_p2.b, 50 + Filter: ((prt1_p2.a % 25) = 0) + -> Hash + Output: prt2_p2.b, prt2_p2.c, (75) + -> Seq Scan on public.prt2_p2 + Output: prt2_p2.b, prt2_p2.c, 75 + Filter: ((prt2_p2.b % 25) = 0) + -> Hash Full Join + Output: prt1_p3.a, prt1_p3.c, prt2_p3.b, prt2_p3.c + Hash Cond: (prt1_p3.a = prt2_p3.b) + Filter: (((50) = prt1_p3.b) OR ((75) = prt2_p3.b)) + -> Seq Scan on public.prt1_p3 + Output: prt1_p3.a, prt1_p3.c, prt1_p3.b, 50 + Filter: ((prt1_p3.a % 25) = 0) + -> Hash + Output: prt2_p3.b, prt2_p3.c, (75) + -> Seq Scan on public.prt2_p3 + Output: prt2_p3.b, prt2_p3.c, 75 + Filter: ((prt2_p3.b % 25) = 0) +(40 rows) + +SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT 50 phv, * FROM prt1 WHERE prt1.a % 25 = 0) t1 FULL JOIN (SELECT 75 phv, * FROM prt2 WHERE prt2.b % 25 = 0) t2 ON (t1.a = t2.b) WHERE t1.phv = t1.b OR t2.phv = t2.b ORDER BY t1.a, t2.b; + a | c | b | c +----+------+----+------ + 50 | 0050 | | + | | 75 | 0075 +(2 rows) + +SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT 50 phv, * FROM uprt1 WHERE uprt1.a % 25 = 0) t1 FULL JOIN (SELECT 75 phv, * FROM uprt2 WHERE uprt2.b % 25 = 0) t2 ON (t1.a = t2.b) WHERE t1.phv = t1.b OR t2.phv = t2.b ORDER BY t1.a, t2.b; + a | c | b | c +----+------+----+------ + 50 | 0050 | | + | | 75 | 0075 +(2 rows) + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t1.phv, t2.b, t2.c, t2.phv FROM (SELECT 25 phv, * FROM prt1 WHERE prt1.a % 25 = 0) t1 FULL JOIN (SELECT 50 phv, * FROM prt2 WHERE prt2.b % 25 = 0) t2 ON (t1.a = t2.b) ORDER BY t1.a, t2.b; + QUERY PLAN +------------------------------------------------------------------------------------ + Sort + Output: prt1_p1.a, prt1_p1.c, (25), prt2_p1.b, prt2_p1.c, (50) + Sort Key: prt1_p1.a, prt2_p1.b + -> Result + Output: prt1_p1.a, prt1_p1.c, (25), prt2_p1.b, prt2_p1.c, (50) + -> Append + -> Hash Full Join + Output: prt1_p1.a, prt1_p1.c, prt2_p1.b, prt2_p1.c, (25), (50) + Hash Cond: (prt1_p1.a = prt2_p1.b) + -> Seq Scan on public.prt1_p1 + Output: prt1_p1.a, prt1_p1.c, 25 + Filter: ((prt1_p1.a % 25) = 0) + -> Hash + Output: prt2_p1.b, prt2_p1.c, (50) + -> Seq Scan on public.prt2_p1 + Output: prt2_p1.b, prt2_p1.c, 50 + Filter: ((prt2_p1.b % 25) = 0) + -> Hash Full Join + Output: prt1_p2.a, prt1_p2.c, prt2_p2.b, prt2_p2.c, (25), (50) + Hash Cond: (prt1_p2.a = prt2_p2.b) + -> Seq Scan on public.prt1_p2 + Output: prt1_p2.a, prt1_p2.c, 25 + Filter: ((prt1_p2.a % 25) = 0) + -> Hash + Output: prt2_p2.b, prt2_p2.c, (50) + -> Seq Scan on public.prt2_p2 + Output: prt2_p2.b, prt2_p2.c, 50 + Filter: ((prt2_p2.b % 25) = 0) + -> Hash Full Join + Output: prt1_p3.a, prt1_p3.c, prt2_p3.b, prt2_p3.c, (25), (50) + Hash Cond: (prt1_p3.a = prt2_p3.b) + -> Seq Scan on public.prt1_p3 + Output: prt1_p3.a, prt1_p3.c, 25 + Filter: ((prt1_p3.a % 25) = 0) + -> Hash + Output: prt2_p3.b, prt2_p3.c, (50) + -> Seq Scan on public.prt2_p3 + Output: prt2_p3.b, prt2_p3.c, 50 + Filter: ((prt2_p3.b % 25) = 0) +(39 rows) + +SELECT t1.a, t1.c, t1.phv, t2.b, t2.c, t2.phv FROM (SELECT 25 phv, * FROM prt1 WHERE prt1.a % 25 = 0) t1 FULL JOIN (SELECT 50 phv, * FROM prt2 WHERE prt2.b % 25 = 0) t2 ON (t1.a = t2.b) ORDER BY t1.a, t2.b; + a | c | phv | b | c | phv +-----+------+-----+-----+------+----- + 0 | 0000 | 25 | 0 | 0000 | 50 + 50 | 0050 | 25 | | | + 100 | 0100 | 25 | | | + 150 | 0150 | 25 | 150 | 0150 | 50 + 200 | 0200 | 25 | | | + 250 | 0250 | 25 | | | + 300 | 0300 | 25 | 300 | 0300 | 50 + 350 | 0350 | 25 | | | + 400 | 0400 | 25 | | | + 450 | 0450 | 25 | 450 | 0450 | 50 + 500 | 0500 | 25 | | | + 550 | 0550 | 25 | | | + | | | 75 | 0075 | 50 + | | | 225 | 0225 | 50 + | | | 375 | 0375 | 50 + | | | 525 | 0525 | 50 +(16 rows) + +SELECT t1.a, t1.c, t1.phv, t2.b, t2.c, t2.phv FROM (SELECT 25 phv, * FROM uprt1 WHERE uprt1.a % 25 = 0) t1 FULL JOIN (SELECT 50 phv, * FROM uprt2 WHERE uprt2.b % 25 = 0) t2 ON (t1.a = t2.b) ORDER BY t1.a, t2.b; + a | c | phv | b | c | phv +-----+------+-----+-----+------+----- + 0 | 0000 | 25 | 0 | 0000 | 50 + 50 | 0050 | 25 | | | + 100 | 0100 | 25 | | | + 150 | 0150 | 25 | 150 | 0150 | 50 + 200 | 0200 | 25 | | | + 250 | 0250 | 25 | | | + 300 | 0300 | 25 | 300 | 0300 | 50 + 350 | 0350 | 25 | | | + 400 | 0400 | 25 | | | + 450 | 0450 | 25 | 450 | 0450 | 50 + 500 | 0500 | 25 | | | + 550 | 0550 | 25 | | | + | | | 75 | 0075 | 50 + | | | 225 | 0225 | 50 + | | | 375 | 0375 | 50 + | | | 525 | 0525 | 50 +(16 rows) + +-- Join with pruned partitions from joining relations +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.a < 450 AND t2.b > 250 AND t1.a % 25 = 0 ORDER BY t1.a, t2.b; + QUERY PLAN +------------------------------------------------------------------------ + Sort + Output: t1.a, t1.c, t2.b, t2.c + Sort Key: t1.a + -> Append + -> Hash Join + Output: t1.a, t1.c, t2.b, t2.c + Hash Cond: (t2.b = t1.a) + -> Seq Scan on public.prt2_p2 t2 + Output: t2.b, t2.c + Filter: (t2.b > 250) + -> Hash + Output: t1.a, t1.c + -> Seq Scan on public.prt1_p2 t1 + Output: t1.a, t1.c + Filter: ((t1.a < 450) AND ((t1.a % 25) = 0)) +(15 rows) + +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.a < 450 AND t2.b > 250 AND t1.a % 25 = 0 ORDER BY t1.a, t2.b; + a | c | b | c +-----+------+-----+------ + 300 | 0300 | 300 | 0300 +(1 row) + +SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1 t1, uprt2 t2 WHERE t1.a = t2.b AND t1.a < 450 AND t2.b > 250 AND t1.a % 25 = 0 ORDER BY t1.a, t2.b; + a | c | b | c +-----+------+-----+------ + 300 | 0300 | 300 | 0300 +(1 row) + +EXPLAIN (VERBOSE, COSTS OFF) +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.a % 25 = 0 ORDER BY t1.a, t2.b; + QUERY PLAN +---------------------------------------------------------------------------------- + Sort + Output: prt1_p1.a, prt1_p1.c, b, c + Sort Key: prt1_p1.a, b + -> Append + -> Nested Loop Left Join + Output: prt1_p1.a, prt1_p1.c, b, c + Join Filter: (prt1_p1.a = b) + -> Seq Scan on public.prt1_p1 + Output: prt1_p1.a, prt1_p1.c + Filter: ((prt1_p1.a < 450) AND ((prt1_p1.a % 25) = 0)) + -> Result + Output: b, c + One-Time Filter: false + -> Hash Right Join + Output: prt1_p2.a, prt1_p2.c, prt2_p2.b, prt2_p2.c + Hash Cond: (prt2_p2.b = prt1_p2.a) + -> Seq Scan on public.prt2_p2 + Output: prt2_p2.b, prt2_p2.c + Filter: (prt2_p2.b > 250) + -> Hash + Output: prt1_p2.a, prt1_p2.c + -> Seq Scan on public.prt1_p2 + Output: prt1_p2.a, prt1_p2.c + Filter: ((prt1_p2.a < 450) AND ((prt1_p2.a % 25) = 0)) +(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.a % 25 = 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) + +SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM uprt1 WHERE a < 450) t1 LEFT JOIN (SELECT * FROM uprt2 WHERE b > 250) t2 ON t1.a = t2.b WHERE t1.a % 25 = 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) + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a < 450) t1 RIGHT JOIN (SELECT * FROM prt2 WHERE b > 250) t2 ON t1.a = t2.b WHERE t2.a % 25 = 0 ORDER BY t1.a, t2.b; + QUERY PLAN +---------------------------------------------------------------------------------------- + Sort + Output: prt1_p2.a, prt1_p2.c, prt2_p2.b, prt2_p2.c + Sort Key: prt1_p2.a, prt2_p2.b + -> Result + Output: prt1_p2.a, prt1_p2.c, prt2_p2.b, prt2_p2.c + -> Append + -> Hash Right Join + Output: prt2_p2.b, prt2_p2.c, prt1_p2.a, prt1_p2.c + Hash Cond: (prt1_p2.a = prt2_p2.b) + -> Seq Scan on public.prt1_p2 + Output: prt1_p2.a, prt1_p2.c + Filter: (prt1_p2.a < 450) + -> Hash + Output: prt2_p2.b, prt2_p2.c + -> Seq Scan on public.prt2_p2 + Output: prt2_p2.b, prt2_p2.c + Filter: ((prt2_p2.b > 250) AND ((prt2_p2.a % 25) = 0)) + -> Nested Loop Left Join + Output: prt2_p3.b, prt2_p3.c, a, c + Join Filter: (a = prt2_p3.b) + -> Seq Scan on public.prt2_p3 + Output: prt2_p3.b, prt2_p3.c + Filter: ((prt2_p3.b > 250) AND ((prt2_p3.a % 25) = 0)) + -> Result + Output: a, c + One-Time Filter: false +(26 rows) + +SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a < 450) t1 RIGHT JOIN (SELECT * FROM prt2 WHERE b > 250) t2 ON t1.a = t2.b WHERE t2.a % 25 = 0 ORDER BY t1.a, t2.b; + a | c | b | c +-----+------+-----+------ + 300 | 0300 | 300 | 0300 + | | 375 | 0375 + | | 450 | 0450 + | | 525 | 0525 +(4 rows) + +SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM uprt1 WHERE a < 450) t1 RIGHT JOIN (SELECT * FROM uprt2 WHERE b > 250) t2 ON t1.a = t2.b WHERE t2.a % 25 = 0 ORDER BY t1.a, t2.b; + a | c | b | c +-----+------+-----+------ + 300 | 0300 | 300 | 0300 + | | 375 | 0375 + | | 450 | 0450 + | | 525 | 0525 +(4 rows) + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a < 450 AND a % 25 = 0) t1 FULL JOIN (SELECT * FROM prt2 WHERE b > 250 AND b % 25 = 0) t2 ON t1.a = t2.b ORDER BY t1.a, t2.b; + QUERY PLAN +---------------------------------------------------------------------------------- + Sort + Output: prt1_p1.a, prt1_p1.c, b, c + Sort Key: prt1_p1.a, b + -> Append + -> Hash Full Join + Output: prt1_p1.a, prt1_p1.c, b, c + Hash Cond: (prt1_p1.a = b) + -> Seq Scan on public.prt1_p1 + Output: prt1_p1.a, prt1_p1.c + Filter: ((prt1_p1.a < 450) AND ((prt1_p1.a % 25) = 0)) + -> Hash + Output: b, c + -> Result + Output: b, c + One-Time Filter: false + -> Hash Full Join + Output: prt1_p2.a, prt1_p2.c, prt2_p2.b, prt2_p2.c + Hash Cond: (prt1_p2.a = prt2_p2.b) + -> Seq Scan on public.prt1_p2 + Output: prt1_p2.a, prt1_p2.c + Filter: ((prt1_p2.a < 450) AND ((prt1_p2.a % 25) = 0)) + -> Hash + Output: prt2_p2.b, prt2_p2.c + -> Seq Scan on public.prt2_p2 + Output: prt2_p2.b, prt2_p2.c + Filter: ((prt2_p2.b > 250) AND ((prt2_p2.b % 25) = 0)) + -> Hash Full Join + Output: a, c, prt2_p3.b, prt2_p3.c + Hash Cond: (prt2_p3.b = a) + -> Seq Scan on public.prt2_p3 + Output: prt2_p3.b, prt2_p3.c + Filter: ((prt2_p3.b > 250) AND ((prt2_p3.b % 25) = 0)) + -> Hash + Output: a, c + -> Result + Output: a, c + One-Time Filter: false +(37 rows) + +SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a < 450 AND a % 25 = 0) t1 FULL JOIN (SELECT * FROM prt2 WHERE b > 250 AND b % 25 = 0) t2 ON t1.a = t2.b 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) + +SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM uprt1 WHERE a < 450 AND a % 25 = 0) t1 FULL JOIN (SELECT * FROM uprt2 WHERE b > 250 AND b % 25 = 0) t2 ON t1.a = t2.b 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) + +-- Semi-join +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1 WHERE t1.b % 25 = 0) AND t1.a % 25 = 0 ORDER BY t1.a; + QUERY PLAN +--------------------------------------------------------- + Sort + Output: t1.a, t1.b, t1.c + Sort Key: t1.a + -> Append + -> Nested Loop Semi Join + Output: t1.a, t1.b, t1.c + Join Filter: (t1.a = t1_3.b) + -> Seq Scan on public.prt1_p1 t1 + Output: t1.a, t1.b, t1.c + Filter: ((t1.a % 25) = 0) + -> Materialize + Output: t1_3.b + -> Seq Scan on public.prt2_p1 t1_3 + Output: t1_3.b + Filter: ((t1_3.b % 25) = 0) + -> Nested Loop Semi Join + Output: t1_2.a, t1_2.b, t1_2.c + Join Filter: (t1_2.a = t1_4.b) + -> Seq Scan on public.prt1_p2 t1_2 + Output: t1_2.a, t1_2.b, t1_2.c + Filter: ((t1_2.a % 25) = 0) + -> Materialize + Output: t1_4.b + -> Seq Scan on public.prt2_p2 t1_4 + Output: t1_4.b + Filter: ((t1_4.b % 25) = 0) + -> Nested Loop Semi Join + Output: t1_1.a, t1_1.b, t1_1.c + Join Filter: (t1_1.a = t1_5.b) + -> Seq Scan on public.prt1_p3 t1_1 + Output: t1_1.a, t1_1.b, t1_1.c + Filter: ((t1_1.a % 25) = 0) + -> Materialize + Output: t1_5.b + -> Seq Scan on public.prt2_p3 t1_5 + Output: t1_5.b + Filter: ((t1_5.b % 25) = 0) +(37 rows) + +SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1 WHERE t1.b % 25 = 0) AND t1.a % 25 = 0 ORDER BY t1.a; + a | b | c +-----+-----+------ + 0 | 0 | 0000 + 150 | 150 | 0150 + 300 | 300 | 0300 + 450 | 450 | 0450 +(4 rows) + +SELECT t1.* FROM uprt1 t1 WHERE t1.a IN (SELECT t1.b FROM uprt2 t1 WHERE t1.b % 25 = 0) AND t1.a % 25 = 0 ORDER BY t1.a; + a | b | c +-----+-----+------ + 0 | 0 | 0000 + 150 | 150 | 0150 + 300 | 300 | 0300 + 450 | 450 | 0450 +(4 rows) + +-- lateral reference +EXPLAIN (VERBOSE, COSTS OFF) +SELECT * FROM prt1 t1 LEFT JOIN LATERAL + (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM prt1 t2 JOIN prt2 t3 ON (t2.a = t3.b)) ss + ON t1.a = ss.t2a WHERE t1.a % 25 = 0 ORDER BY t1.a; + QUERY PLAN +----------------------------------------------------------------------------------------------------- + Sort + Output: t1.a, t1.b, t1.c, t2.a, t3.a, (LEAST(t1.a, t2.a, t3.a)), t1.a + Sort Key: t1.a + -> Result + Output: t1.a, t1.b, t1.c, t2.a, t3.a, (LEAST(t1.a, t2.a, t3.a)), t1.a + -> Append + -> Nested Loop Left Join + Output: t1.a, t1.b, t1.c, t2.a, t3.a, (LEAST(t1.a, t2.a, t3.a)) + -> Seq Scan on public.prt1_p1 t1 + Output: t1.a, t1.b, t1.c + Filter: ((t1.a % 25) = 0) + -> Hash Join + Output: t2.a, t3.a, LEAST(t1.a, t2.a, t3.a) + Hash Cond: (t3.b = t2.a) + -> Seq Scan on public.prt2_p1 t3 + Output: t3.a, t3.b + -> Hash + Output: t2.a + -> Seq Scan on public.prt1_p1 t2 + Output: t2.a + Filter: (t1.a = t2.a) + -> Nested Loop Left Join + Output: t1_2.a, t1_2.b, t1_2.c, t2_2.a, t3_1.a, (LEAST(t1_2.a, t2_2.a, t3_1.a)) + -> Seq Scan on public.prt1_p2 t1_2 + Output: t1_2.a, t1_2.b, t1_2.c + Filter: ((t1_2.a % 25) = 0) + -> Hash Join + Output: t2_2.a, t3_1.a, LEAST(t1_2.a, t2_2.a, t3_1.a) + Hash Cond: (t3_1.b = t2_2.a) + -> Seq Scan on public.prt2_p2 t3_1 + Output: t3_1.a, t3_1.b + -> Hash + Output: t2_2.a + -> Seq Scan on public.prt1_p2 t2_2 + Output: t2_2.a + Filter: (t1_2.a = t2_2.a) + -> Nested Loop Left Join + Output: t1_1.a, t1_1.b, t1_1.c, t2_1.a, t3_2.a, (LEAST(t1_1.a, t2_1.a, t3_2.a)) + -> Seq Scan on public.prt1_p3 t1_1 + Output: t1_1.a, t1_1.b, t1_1.c + Filter: ((t1_1.a % 25) = 0) + -> Hash Join + Output: t2_1.a, t3_2.a, LEAST(t1_1.a, t2_1.a, t3_2.a) + Hash Cond: (t3_2.b = t2_1.a) + -> Seq Scan on public.prt2_p3 t3_2 + Output: t3_2.a, t3_2.b + -> Hash + Output: t2_1.a + -> Seq Scan on public.prt1_p3 t2_1 + Output: t2_1.a + Filter: (t1_1.a = t2_1.a) +(51 rows) + +SELECT * FROM prt1 t1 LEFT JOIN LATERAL + (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM prt1 t2 JOIN prt2 t3 ON (t2.a = t3.b)) ss + ON t1.a = ss.t2a WHERE t1.a % 25 = 0 ORDER BY t1.a; + a | b | c | t2a | t3a | least +-----+-----+------+-----+-----+------- + 0 | 0 | 0000 | 0 | 0 | 0 + 50 | 50 | 0050 | | | + 100 | 100 | 0100 | | | + 150 | 150 | 0150 | 150 | 150 | 150 + 200 | 200 | 0200 | | | + 250 | 250 | 0250 | | | + 300 | 300 | 0300 | 300 | 300 | 300 + 350 | 350 | 0350 | | | + 400 | 400 | 0400 | | | + 450 | 450 | 0450 | 450 | 450 | 450 + 500 | 500 | 0500 | | | + 550 | 550 | 0550 | | | +(12 rows) + +SELECT * FROM uprt1 t1 LEFT JOIN LATERAL + (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM uprt1 t2 JOIN uprt2 t3 ON (t2.a = t3.b)) ss + ON t1.a = ss.t2a WHERE t1.a % 25 = 0 ORDER BY t1.a; + a | b | c | t2a | t3a | least +-----+-----+------+-----+-----+------- + 0 | 0 | 0000 | 0 | 0 | 0 + 50 | 50 | 0050 | | | + 100 | 100 | 0100 | | | + 150 | 150 | 0150 | 150 | 150 | 150 + 200 | 200 | 0200 | | | + 250 | 250 | 0250 | | | + 300 | 300 | 0300 | 300 | 300 | 300 + 350 | 350 | 0350 | | | + 400 | 400 | 0400 | | | + 450 | 450 | 0450 | 450 | 450 | 450 + 500 | 500 | 0500 | | | + 550 | 550 | 0550 | | | +(12 rows) + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT * FROM prt1 t1 LEFT JOIN LATERAL + (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM prt1 t2 JOIN prt2 t3 ON (t2.a = t3.b)) ss + ON t1.b = ss.t2a WHERE t1.a % 25 = 0 ORDER BY t1.a; + QUERY PLAN +------------------------------------------------------------------------------- + Sort + Output: t1.a, t1.b, t1.c, t2.a, t3.a, (LEAST(t1.a, t2.a, t3.a)), t1.a + Sort Key: t1.a + -> Nested Loop Left Join + Output: t1.a, t1.b, t1.c, t2.a, t3.a, (LEAST(t1.a, t2.a, t3.a)), t1.a + -> Append + -> Seq Scan on public.prt1 t1 + Output: t1.a, t1.b, t1.c + Filter: ((t1.a % 25) = 0) + -> Seq Scan on public.prt1_p1 t1_1 + Output: t1_1.a, t1_1.b, t1_1.c + Filter: ((t1_1.a % 25) = 0) + -> Seq Scan on public.prt1_p3 t1_2 + Output: t1_2.a, t1_2.b, t1_2.c + Filter: ((t1_2.a % 25) = 0) + -> Seq Scan on public.prt1_p2 t1_3 + Output: t1_3.a, t1_3.b, t1_3.c + Filter: ((t1_3.a % 25) = 0) + -> Append + -> Hash Join + Output: t2.a, t3.a, LEAST(t1.a, t2.a, t3.a) + Hash Cond: (t3.b = t2.a) + -> Seq Scan on public.prt2_p1 t3 + Output: t3.a, t3.b + -> Hash + Output: t2.a + -> Seq Scan on public.prt1_p1 t2 + Output: t2.a + Filter: (t1.b = t2.a) + -> Hash Join + Output: t2_2.a, t3_1.a, LEAST(t1.a, t2_2.a, t3_1.a) + Hash Cond: (t3_1.b = t2_2.a) + -> Seq Scan on public.prt2_p2 t3_1 + Output: t3_1.a, t3_1.b + -> Hash + Output: t2_2.a + -> Seq Scan on public.prt1_p2 t2_2 + Output: t2_2.a + Filter: (t1.b = t2_2.a) + -> Hash Join + Output: t2_1.a, t3_2.a, LEAST(t1.a, t2_1.a, t3_2.a) + Hash Cond: (t3_2.b = t2_1.a) + -> Seq Scan on public.prt2_p3 t3_2 + Output: t3_2.a, t3_2.b + -> Hash + Output: t2_1.a + -> Seq Scan on public.prt1_p3 t2_1 + Output: t2_1.a + Filter: (t1.b = t2_1.a) +(49 rows) + +SELECT * FROM prt1 t1 LEFT JOIN LATERAL + (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM prt1 t2 JOIN prt2 t3 ON (t2.a = t3.b)) ss + ON t1.b = ss.t2a WHERE t1.a % 25 = 0 ORDER BY t1.a; + a | b | c | t2a | t3a | least +-----+-----+------+-----+-----+------- + 0 | 0 | 0000 | 0 | 0 | 0 + 50 | 50 | 0050 | | | + 100 | 100 | 0100 | | | + 150 | 150 | 0150 | 150 | 150 | 150 + 200 | 200 | 0200 | | | + 250 | 250 | 0250 | | | + 300 | 300 | 0300 | 300 | 300 | 300 + 350 | 350 | 0350 | | | + 400 | 400 | 0400 | | | + 450 | 450 | 0450 | 450 | 450 | 450 + 500 | 500 | 0500 | | | + 550 | 550 | 0550 | | | +(12 rows) + +SELECT * FROM uprt1 t1 LEFT JOIN LATERAL + (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM uprt1 t2 JOIN uprt2 t3 ON (t2.a = t3.b)) ss + ON t1.b = ss.t2a WHERE t1.a % 25 = 0 ORDER BY t1.a; + a | b | c | t2a | t3a | least +-----+-----+------+-----+-----+------- + 0 | 0 | 0000 | 0 | 0 | 0 + 50 | 50 | 0050 | | | + 100 | 100 | 0100 | | | + 150 | 150 | 0150 | 150 | 150 | 150 + 200 | 200 | 0200 | | | + 250 | 250 | 0250 | | | + 300 | 300 | 0300 | 300 | 300 | 300 + 350 | 350 | 0350 | | | + 400 | 400 | 0400 | | | + 450 | 450 | 0450 | 450 | 450 | 450 + 500 | 500 | 0500 | | | + 550 | 550 | 0550 | | | +(12 rows) + +-- +-- partitioned by expression +-- +CREATE TABLE prt1_e (a int, b int, c varchar) PARTITION BY RANGE(((a + b)/2)); +CREATE TABLE prt1_e_p1 PARTITION OF prt1_e FOR VALUES START (0) END (250); +CREATE TABLE prt1_e_p2 PARTITION OF prt1_e FOR VALUES START (250) END (500); +CREATE TABLE prt1_e_p3 PARTITION OF prt1_e FOR VALUES START (500) END (600); +INSERT INTO prt1_e SELECT i, i, to_char(i, 'FM0000') FROM generate_series(0, 599, 2) i; +ANALYZE prt1_e; +ANALYZE prt1_e_p1; +ANALYZE prt1_e_p2; +ANALYZE prt1_e_p3; +-- TODO: This table is created only for testing the results. Remove once +-- results are tested. +CREATE TABLE uprt1_e AS SELECT * FROM prt1_e; +CREATE TABLE prt2_e (a int, b int, c varchar) PARTITION BY RANGE(((b + a)/2)); +CREATE TABLE prt2_e_p1 PARTITION OF prt2_e FOR VALUES START (0) END (250); +CREATE TABLE prt2_e_p2 PARTITION OF prt2_e FOR VALUES START (250) END (500); +CREATE TABLE prt2_e_p3 PARTITION OF prt2_e FOR VALUES START (500) END (600); +INSERT INTO prt2_e SELECT i, i, to_char(i, 'FM0000') FROM generate_series(0, 599, 3) i; +ANALYZE prt2_e; +ANALYZE prt2_e_p1; +ANALYZE prt2_e_p2; +ANALYZE prt2_e_p3; +-- TODO: This table is created only for testing the results. Remove once +-- results are tested. +CREATE TABLE uprt2_e AS SELECT * FROM prt2_e; +EXPLAIN (VERBOSE, 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.a % 25 = 0 ORDER BY t1.a, t2.b; + QUERY PLAN +------------------------------------------------------------------------------ + Sort + Output: t1.a, t1.c, t2.b, t2.c + Sort Key: t1.a, t2.b + -> Append + -> Hash Join + Output: t1.a, t1.c, t2.b, t2.c + Hash Cond: (((t2.b + t2.a) / 2) = ((t1.a + t1.b) / 2)) + -> Seq Scan on public.prt2_e_p1 t2 + Output: t2.b, t2.c, t2.a + -> Hash + Output: t1.a, t1.c, t1.b + -> Seq Scan on public.prt1_e_p1 t1 + Output: t1.a, t1.c, t1.b + Filter: ((t1.a % 25) = 0) + -> Hash Join + Output: t1_1.a, t1_1.c, t2_1.b, t2_1.c + Hash Cond: (((t2_1.b + t2_1.a) / 2) = ((t1_1.a + t1_1.b) / 2)) + -> Seq Scan on public.prt2_e_p2 t2_1 + Output: t2_1.b, t2_1.c, t2_1.a + -> Hash + Output: t1_1.a, t1_1.c, t1_1.b + -> Seq Scan on public.prt1_e_p2 t1_1 + Output: t1_1.a, t1_1.c, t1_1.b + Filter: ((t1_1.a % 25) = 0) + -> Hash Join + Output: t1_2.a, t1_2.c, t2_2.b, t2_2.c + Hash Cond: (((t2_2.b + t2_2.a) / 2) = ((t1_2.a + t1_2.b) / 2)) + -> Seq Scan on public.prt2_e_p3 t2_2 + Output: t2_2.b, t2_2.c, t2_2.a + -> Hash + Output: t1_2.a, t1_2.c, t1_2.b + -> Seq Scan on public.prt1_e_p3 t1_2 + Output: t1_2.a, t1_2.c, t1_2.b + Filter: ((t1_2.a % 25) = 0) +(34 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.a % 25 = 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) + +SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1_e t1, uprt2_e t2 WHERE (t1.a + t1.b)/2 = (t2.b + t2.a)/2 AND t1.a % 25 = 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) + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_e t1 LEFT JOIN prt2_e t2 ON (t1.a + t1.b)/2 = (t2.b + t2.a)/2 WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b; + QUERY PLAN +------------------------------------------------------------------------------ + Sort + Output: t1.a, t1.c, t2.b, t2.c + Sort Key: t1.a, t2.b + -> Append + -> Hash Right Join + Output: t1.a, t1.c, t2.b, t2.c + Hash Cond: (((t2.b + t2.a) / 2) = ((t1.a + t1.b) / 2)) + -> Seq Scan on public.prt2_e_p1 t2 + Output: t2.b, t2.c, t2.a + -> Hash + Output: t1.a, t1.c, t1.b + -> Seq Scan on public.prt1_e_p1 t1 + Output: t1.a, t1.c, t1.b + Filter: ((t1.a % 25) = 0) + -> Hash Right Join + Output: t1_1.a, t1_1.c, t2_1.b, t2_1.c + Hash Cond: (((t2_1.b + t2_1.a) / 2) = ((t1_1.a + t1_1.b) / 2)) + -> Seq Scan on public.prt2_e_p2 t2_1 + Output: t2_1.b, t2_1.c, t2_1.a + -> Hash + Output: t1_1.a, t1_1.c, t1_1.b + -> Seq Scan on public.prt1_e_p2 t1_1 + Output: t1_1.a, t1_1.c, t1_1.b + Filter: ((t1_1.a % 25) = 0) + -> Hash Right Join + Output: t1_2.a, t1_2.c, t2_2.b, t2_2.c + Hash Cond: (((t2_2.b + t2_2.a) / 2) = ((t1_2.a + t1_2.b) / 2)) + -> Seq Scan on public.prt2_e_p3 t2_2 + Output: t2_2.b, t2_2.c, t2_2.a + -> Hash + Output: t1_2.a, t1_2.c, t1_2.b + -> Seq Scan on public.prt1_e_p3 t1_2 + Output: t1_2.a, t1_2.c, t1_2.b + Filter: ((t1_2.a % 25) = 0) +(34 rows) + +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_e t1 LEFT JOIN prt2_e t2 ON (t1.a + t1.b)/2 = (t2.b + t2.a)/2 WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b; + a | c | b | c +-----+------+-----+------ + 0 | 0000 | 0 | 0000 + 50 | 0050 | | + 100 | 0100 | | + 150 | 0150 | 150 | 0150 + 200 | 0200 | | + 250 | 0250 | | + 300 | 0300 | 300 | 0300 + 350 | 0350 | | + 400 | 0400 | | + 450 | 0450 | 450 | 0450 + 500 | 0500 | | + 550 | 0550 | | +(12 rows) + +SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1_e t1 LEFT JOIN uprt2_e t2 ON (t1.a + t1.b)/2 = (t2.b + t2.a)/2 WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b; + a | c | b | c +-----+------+-----+------ + 0 | 0000 | 0 | 0000 + 50 | 0050 | | + 100 | 0100 | | + 150 | 0150 | 150 | 0150 + 200 | 0200 | | + 250 | 0250 | | + 300 | 0300 | 300 | 0300 + 350 | 0350 | | + 400 | 0400 | | + 450 | 0450 | 450 | 0450 + 500 | 0500 | | + 550 | 0550 | | +(12 rows) + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_e t1 RIGHT JOIN prt2_e t2 ON (t1.a + t1.b)/2 = (t2.b + t2.a)/2 WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b; + QUERY PLAN +------------------------------------------------------------------------------------ + Sort + Output: t1.a, t1.c, t2.b, t2.c + Sort Key: t1.a, t2.b + -> Result + Output: t1.a, t1.c, t2.b, t2.c + -> Append + -> Hash Right Join + Output: t2.b, t2.c, t1.a, t1.c + Hash Cond: (((t1.a + t1.b) / 2) = ((t2.b + t2.a) / 2)) + -> Seq Scan on public.prt1_e_p1 t1 + Output: t1.a, t1.c, t1.b + -> Hash + Output: t2.b, t2.c, t2.a + -> Seq Scan on public.prt2_e_p1 t2 + Output: t2.b, t2.c, t2.a + Filter: ((t2.b % 25) = 0) + -> Hash Right Join + Output: t2_1.b, t2_1.c, t1_1.a, t1_1.c + Hash Cond: (((t1_1.a + t1_1.b) / 2) = ((t2_1.b + t2_1.a) / 2)) + -> Seq Scan on public.prt1_e_p2 t1_1 + Output: t1_1.a, t1_1.c, t1_1.b + -> Hash + Output: t2_1.b, t2_1.c, t2_1.a + -> Seq Scan on public.prt2_e_p2 t2_1 + Output: t2_1.b, t2_1.c, t2_1.a + Filter: ((t2_1.b % 25) = 0) + -> Hash Right Join + Output: t2_2.b, t2_2.c, t1_2.a, t1_2.c + Hash Cond: (((t1_2.a + t1_2.b) / 2) = ((t2_2.b + t2_2.a) / 2)) + -> Seq Scan on public.prt1_e_p3 t1_2 + Output: t1_2.a, t1_2.c, t1_2.b + -> Hash + Output: t2_2.b, t2_2.c, t2_2.a + -> Seq Scan on public.prt2_e_p3 t2_2 + Output: t2_2.b, t2_2.c, t2_2.a + Filter: ((t2_2.b % 25) = 0) +(36 rows) + +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_e t1 RIGHT JOIN prt2_e t2 ON (t1.a + t1.b)/2 = (t2.b + t2.a)/2 WHERE t2.b % 25 = 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) + +SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1_e t1 RIGHT JOIN uprt2_e t2 ON (t1.a + t1.b)/2 = (t2.b + t2.a)/2 WHERE t2.b % 25 = 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) + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_e WHERE prt1_e.a % 25 = 0) t1 FULL JOIN (SELECT * FROM prt2_e WHERE prt2_e.b % 25 = 0) t2 ON ((t1.a + t1.b)/2 = (t2.b + t2.a)/2) ORDER BY t1.a, t2.b; + QUERY PLAN +-------------------------------------------------------------------------------------------------- + Sort + Output: prt1_e_p1.a, prt1_e_p1.c, prt2_e_p1.b, prt2_e_p1.c + Sort Key: prt1_e_p1.a, prt2_e_p1.b + -> Append + -> Hash Full Join + Output: prt1_e_p1.a, prt1_e_p1.c, prt2_e_p1.b, prt2_e_p1.c + Hash Cond: (((prt1_e_p1.a + prt1_e_p1.b) / 2) = ((prt2_e_p1.b + prt2_e_p1.a) / 2)) + -> Seq Scan on public.prt1_e_p1 + Output: prt1_e_p1.a, prt1_e_p1.c, prt1_e_p1.b + Filter: ((prt1_e_p1.a % 25) = 0) + -> Hash + Output: prt2_e_p1.b, prt2_e_p1.c, prt2_e_p1.a + -> Seq Scan on public.prt2_e_p1 + Output: prt2_e_p1.b, prt2_e_p1.c, prt2_e_p1.a + Filter: ((prt2_e_p1.b % 25) = 0) + -> Hash Full Join + Output: prt1_e_p2.a, prt1_e_p2.c, prt2_e_p2.b, prt2_e_p2.c + Hash Cond: (((prt1_e_p2.a + prt1_e_p2.b) / 2) = ((prt2_e_p2.b + prt2_e_p2.a) / 2)) + -> Seq Scan on public.prt1_e_p2 + Output: prt1_e_p2.a, prt1_e_p2.c, prt1_e_p2.b + Filter: ((prt1_e_p2.a % 25) = 0) + -> Hash + Output: prt2_e_p2.b, prt2_e_p2.c, prt2_e_p2.a + -> Seq Scan on public.prt2_e_p2 + Output: prt2_e_p2.b, prt2_e_p2.c, prt2_e_p2.a + Filter: ((prt2_e_p2.b % 25) = 0) + -> Hash Full Join + Output: prt1_e_p3.a, prt1_e_p3.c, prt2_e_p3.b, prt2_e_p3.c + Hash Cond: (((prt1_e_p3.a + prt1_e_p3.b) / 2) = ((prt2_e_p3.b + prt2_e_p3.a) / 2)) + -> Seq Scan on public.prt1_e_p3 + Output: prt1_e_p3.a, prt1_e_p3.c, prt1_e_p3.b + Filter: ((prt1_e_p3.a % 25) = 0) + -> Hash + Output: prt2_e_p3.b, prt2_e_p3.c, prt2_e_p3.a + -> Seq Scan on public.prt2_e_p3 + Output: prt2_e_p3.b, prt2_e_p3.c, prt2_e_p3.a + Filter: ((prt2_e_p3.b % 25) = 0) +(37 rows) + +SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_e WHERE prt1_e.a % 25 = 0) t1 FULL JOIN (SELECT * FROM prt2_e WHERE prt2_e.b % 25 = 0) t2 ON ((t1.a + t1.b)/2 = (t2.b + t2.a)/2) ORDER BY t1.a, t2.b; + a | c | b | c +-----+------+-----+------ + 0 | 0000 | 0 | 0000 + 50 | 0050 | | + 100 | 0100 | | + 150 | 0150 | 150 | 0150 + 200 | 0200 | | + 250 | 0250 | | + 300 | 0300 | 300 | 0300 + 350 | 0350 | | + 400 | 0400 | | + 450 | 0450 | 450 | 0450 + 500 | 0500 | | + 550 | 0550 | | + | | 75 | 0075 + | | 225 | 0225 + | | 375 | 0375 + | | 525 | 0525 +(16 rows) + +SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM uprt1_e t1 WHERE t1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM uprt2_e t2 WHERE t2.b % 25 = 0) t2 ON ((t1.a + t1.b)/2 = (t2.b + t2.a)/2) ORDER BY t1.a, t2.b; + a | c | b | c +-----+------+-----+------ + 0 | 0000 | 0 | 0000 + 50 | 0050 | | + 100 | 0100 | | + 150 | 0150 | 150 | 0150 + 200 | 0200 | | + 250 | 0250 | | + 300 | 0300 | 300 | 0300 + 350 | 0350 | | + 400 | 0400 | | + 450 | 0450 | 450 | 0450 + 500 | 0500 | | + 550 | 0550 | | + | | 75 | 0075 + | | 225 | 0225 + | | 375 | 0375 + | | 525 | 0525 +(16 rows) + +-- +-- N-way join +-- +EXPLAIN (VERBOSE, COSTS OFF) +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.a % 25 = 0 ORDER BY t1.a, t2.b; + QUERY PLAN +------------------------------------------------------------------------------------ + Sort + Output: t1.a, t1.c, t2.b, t2.c, ((t3.a + t3.b)), t3.c + Sort Key: t1.a + -> Result + Output: t1.a, t1.c, t2.b, t2.c, (t3.a + t3.b), t3.c + -> Append + -> Hash Join + Output: t1.a, t1.c, t2.b, t2.c, t3.a, t3.b, t3.c + Hash Cond: (((t3.a + t3.b) / 2) = t1.a) + -> Seq Scan on public.prt1_e_p1 t3 + Output: t3.a, t3.b, t3.c + -> Hash + Output: t1.a, t1.c, t2.b, t2.c + -> Hash Join + Output: t1.a, t1.c, t2.b, t2.c + Hash Cond: (t2.b = t1.a) + -> Seq Scan on public.prt2_p1 t2 + Output: t2.b, t2.c + -> Hash + Output: t1.a, t1.c + -> Seq Scan on public.prt1_p1 t1 + Output: t1.a, t1.c + Filter: ((t1.a % 25) = 0) + -> Hash Join + Output: t1_2.a, t1_2.c, t2_1.b, t2_1.c, t3_1.a, t3_1.b, t3_1.c + Hash Cond: (((t3_1.a + t3_1.b) / 2) = t1_2.a) + -> Seq Scan on public.prt1_e_p2 t3_1 + Output: t3_1.a, t3_1.b, t3_1.c + -> Hash + Output: t1_2.a, t1_2.c, t2_1.b, t2_1.c + -> Hash Join + Output: t1_2.a, t1_2.c, t2_1.b, t2_1.c + Hash Cond: (t2_1.b = t1_2.a) + -> Seq Scan on public.prt2_p2 t2_1 + Output: t2_1.b, t2_1.c + -> Hash + Output: t1_2.a, t1_2.c + -> Seq Scan on public.prt1_p2 t1_2 + Output: t1_2.a, t1_2.c + Filter: ((t1_2.a % 25) = 0) + -> Hash Join + Output: t1_1.a, t1_1.c, t2_2.b, t2_2.c, t3_2.a, t3_2.b, t3_2.c + Hash Cond: (((t3_2.a + t3_2.b) / 2) = t1_1.a) + -> Seq Scan on public.prt1_e_p3 t3_2 + Output: t3_2.a, t3_2.b, t3_2.c + -> Hash + Output: t1_1.a, t1_1.c, t2_2.b, t2_2.c + -> Hash Join + Output: t1_1.a, t1_1.c, t2_2.b, t2_2.c + Hash Cond: (t2_2.b = t1_1.a) + -> Seq Scan on public.prt2_p3 t2_2 + Output: t2_2.b, t2_2.c + -> Hash + Output: t1_1.a, t1_1.c + -> Seq Scan on public.prt1_p3 t1_1 + Output: t1_1.a, t1_1.c + Filter: ((t1_1.a % 25) = 0) +(57 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.a % 25 = 0 ORDER BY t1.a, t2.b; + a | c | b | c | ?column? | c +-----+------+-----+------+----------+------ + 0 | 0000 | 0 | 0000 | 0 | 0000 + 150 | 0150 | 150 | 0150 | 300 | 0150 + 300 | 0300 | 300 | 0300 | 600 | 0300 + 450 | 0450 | 450 | 0450 | 900 | 0450 +(4 rows) + +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM uprt1 t1, uprt2 t2, uprt1_e t3 WHERE t1.a = t2.b AND t1.a = (t3.a + t3.b)/2 AND t1.a % 25 = 0 ORDER BY t1.a, t2.b; + a | c | b | c | ?column? | c +-----+------+-----+------+----------+------ + 0 | 0000 | 0 | 0000 | 0 | 0000 + 150 | 0150 | 150 | 0150 | 300 | 0150 + 300 | 0300 | 300 | 0300 | 600 | 0300 + 450 | 0450 | 450 | 0450 | 900 | 0450 +(4 rows) + +EXPLAIN (VERBOSE, 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.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; + QUERY PLAN +------------------------------------------------------------------------------------ + Sort + Output: t1.a, t1.c, t2.b, t2.c, ((t3.a + t3.b)), t3.c + Sort Key: t1.a, t2.b, ((t3.a + t3.b)) + -> Result + Output: t1.a, t1.c, t2.b, t2.c, (t3.a + t3.b), t3.c + -> Append + -> Hash Right Join + Output: t1.a, t1.c, t2.b, t2.c, t3.a, t3.b, t3.c + Hash Cond: (((t3.a + t3.b) / 2) = t1.a) + -> Seq Scan on public.prt1_e_p1 t3 + Output: t3.a, t3.b, t3.c + -> Hash + Output: t1.a, t1.c, t2.b, t2.c + -> Hash Right Join + Output: t1.a, t1.c, t2.b, t2.c + Hash Cond: (t2.b = t1.a) + -> Seq Scan on public.prt2_p1 t2 + Output: t2.b, t2.c + -> Hash + Output: t1.a, t1.c + -> Seq Scan on public.prt1_p1 t1 + Output: t1.a, t1.c + Filter: ((t1.a % 25) = 0) + -> Hash Right Join + Output: t1_2.a, t1_2.c, t2_1.b, t2_1.c, t3_1.a, t3_1.b, t3_1.c + Hash Cond: (((t3_1.a + t3_1.b) / 2) = t1_2.a) + -> Seq Scan on public.prt1_e_p2 t3_1 + Output: t3_1.a, t3_1.b, t3_1.c + -> Hash + Output: t1_2.a, t1_2.c, t2_1.b, t2_1.c + -> Hash Right Join + Output: t1_2.a, t1_2.c, t2_1.b, t2_1.c + Hash Cond: (t2_1.b = t1_2.a) + -> Seq Scan on public.prt2_p2 t2_1 + Output: t2_1.b, t2_1.c + -> Hash + Output: t1_2.a, t1_2.c + -> Seq Scan on public.prt1_p2 t1_2 + Output: t1_2.a, t1_2.c + Filter: ((t1_2.a % 25) = 0) + -> Hash Right Join + Output: t1_1.a, t1_1.c, t2_2.b, t2_2.c, t3_2.a, t3_2.b, t3_2.c + Hash Cond: (((t3_2.a + t3_2.b) / 2) = t1_1.a) + -> Seq Scan on public.prt1_e_p3 t3_2 + Output: t3_2.a, t3_2.b, t3_2.c + -> Hash + Output: t1_1.a, t1_1.c, t2_2.b, t2_2.c + -> Hash Right Join + Output: t1_1.a, t1_1.c, t2_2.b, t2_2.c + Hash Cond: (t2_2.b = t1_1.a) + -> Seq Scan on public.prt2_p3 t2_2 + Output: t2_2.b, t2_2.c + -> Hash + Output: t1_1.a, t1_1.c + -> Seq Scan on public.prt1_p3 t1_1 + Output: t1_1.a, t1_1.c + Filter: ((t1_1.a % 25) = 0) +(57 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.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; + a | c | b | c | ?column? | c +-----+------+-----+------+----------+------ + 0 | 0000 | 0 | 0000 | 0 | 0000 + 50 | 0050 | | | 100 | 0050 + 100 | 0100 | | | 200 | 0100 + 150 | 0150 | 150 | 0150 | 300 | 0150 + 200 | 0200 | | | 400 | 0200 + 250 | 0250 | | | 500 | 0250 + 300 | 0300 | 300 | 0300 | 600 | 0300 + 350 | 0350 | | | 700 | 0350 + 400 | 0400 | | | 800 | 0400 + 450 | 0450 | 450 | 0450 | 900 | 0450 + 500 | 0500 | | | 1000 | 0500 + 550 | 0550 | | | 1100 | 0550 +(12 rows) + +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uprt1 t1 LEFT JOIN uprt2 t2 ON t1.a = t2.b) LEFT JOIN uprt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; + a | c | b | c | ?column? | c +-----+------+-----+------+----------+------ + 0 | 0000 | 0 | 0000 | 0 | 0000 + 50 | 0050 | | | 100 | 0050 + 100 | 0100 | | | 200 | 0100 + 150 | 0150 | 150 | 0150 | 300 | 0150 + 200 | 0200 | | | 400 | 0200 + 250 | 0250 | | | 500 | 0250 + 300 | 0300 | 300 | 0300 | 600 | 0300 + 350 | 0350 | | | 700 | 0350 + 400 | 0400 | | | 800 | 0400 + 450 | 0450 | 450 | 0450 | 900 | 0450 + 500 | 0500 | | | 1000 | 0500 + 550 | 0550 | | | 1100 | 0550 +(12 rows) + +EXPLAIN (VERBOSE, 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 (t2.b = (t3.a + t3.b)/2) WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; + QUERY PLAN +------------------------------------------------------------------------------------ + Sort + Output: t1.a, t1.c, t2.b, t2.c, ((t3.a + t3.b)), t3.c + Sort Key: t1.a, t2.b, ((t3.a + t3.b)) + -> Result + Output: t1.a, t1.c, t2.b, t2.c, (t3.a + t3.b), t3.c + -> Append + -> Hash Right Join + Output: t1.a, t1.c, t2.b, t2.c, t3.a, t3.b, t3.c + Hash Cond: (((t3.a + t3.b) / 2) = t2.b) + -> Seq Scan on public.prt1_e_p1 t3 + Output: t3.a, t3.b, t3.c + -> Hash + Output: t1.a, t1.c, t2.b, t2.c + -> Hash Right Join + Output: t1.a, t1.c, t2.b, t2.c + Hash Cond: (t2.b = t1.a) + -> Seq Scan on public.prt2_p1 t2 + Output: t2.b, t2.c + -> Hash + Output: t1.a, t1.c + -> Seq Scan on public.prt1_p1 t1 + Output: t1.a, t1.c + Filter: ((t1.a % 25) = 0) + -> Hash Right Join + Output: t1_2.a, t1_2.c, t2_1.b, t2_1.c, t3_1.a, t3_1.b, t3_1.c + Hash Cond: (((t3_1.a + t3_1.b) / 2) = t2_1.b) + -> Seq Scan on public.prt1_e_p2 t3_1 + Output: t3_1.a, t3_1.b, t3_1.c + -> Hash + Output: t1_2.a, t1_2.c, t2_1.b, t2_1.c + -> Hash Right Join + Output: t1_2.a, t1_2.c, t2_1.b, t2_1.c + Hash Cond: (t2_1.b = t1_2.a) + -> Seq Scan on public.prt2_p2 t2_1 + Output: t2_1.b, t2_1.c + -> Hash + Output: t1_2.a, t1_2.c + -> Seq Scan on public.prt1_p2 t1_2 + Output: t1_2.a, t1_2.c + Filter: ((t1_2.a % 25) = 0) + -> Hash Right Join + Output: t1_1.a, t1_1.c, t2_2.b, t2_2.c, t3_2.a, t3_2.b, t3_2.c + Hash Cond: (((t3_2.a + t3_2.b) / 2) = t2_2.b) + -> Seq Scan on public.prt1_e_p3 t3_2 + Output: t3_2.a, t3_2.b, t3_2.c + -> Hash + Output: t1_1.a, t1_1.c, t2_2.b, t2_2.c + -> Hash Right Join + Output: t1_1.a, t1_1.c, t2_2.b, t2_2.c + Hash Cond: (t2_2.b = t1_1.a) + -> Seq Scan on public.prt2_p3 t2_2 + Output: t2_2.b, t2_2.c + -> Hash + Output: t1_1.a, t1_1.c + -> Seq Scan on public.prt1_p3 t1_1 + Output: t1_1.a, t1_1.c + Filter: ((t1_1.a % 25) = 0) +(57 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 (t2.b = (t3.a + t3.b)/2) WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; + a | c | b | c | ?column? | c +-----+------+-----+------+----------+------ + 0 | 0000 | 0 | 0000 | 0 | 0000 + 50 | 0050 | | | | + 100 | 0100 | | | | + 150 | 0150 | 150 | 0150 | 300 | 0150 + 200 | 0200 | | | | + 250 | 0250 | | | | + 300 | 0300 | 300 | 0300 | 600 | 0300 + 350 | 0350 | | | | + 400 | 0400 | | | | + 450 | 0450 | 450 | 0450 | 900 | 0450 + 500 | 0500 | | | | + 550 | 0550 | | | | +(12 rows) + +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uprt1 t1 LEFT JOIN uprt2 t2 ON t1.a = t2.b) LEFT JOIN uprt1_e t3 ON (t2.b = (t3.a + t3.b)/2) WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; + a | c | b | c | ?column? | c +-----+------+-----+------+----------+------ + 0 | 0000 | 0 | 0000 | 0 | 0000 + 50 | 0050 | | | | + 100 | 0100 | | | | + 150 | 0150 | 150 | 0150 | 300 | 0150 + 200 | 0200 | | | | + 250 | 0250 | | | | + 300 | 0300 | 300 | 0300 | 600 | 0300 + 350 | 0350 | | | | + 400 | 0400 | | | | + 450 | 0450 | 450 | 0450 | 900 | 0450 + 500 | 0500 | | | | + 550 | 0550 | | | | +(12 rows) + +EXPLAIN (VERBOSE, 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.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; + QUERY PLAN +------------------------------------------------------------------------------------ + Sort + Output: t1.a, t1.c, t2.b, t2.c, ((t3.a + t3.b)), t3.c + Sort Key: t1.a, t2.b, ((t3.a + t3.b)) + -> Result + Output: t1.a, t1.c, t2.b, t2.c, (t3.a + t3.b), t3.c + -> Append + -> Hash Right Join + Output: t3.a, t3.b, t3.c, t1.a, t1.c, t2.b, t2.c + Hash Cond: (t2.b = t1.a) + -> Seq Scan on public.prt2_p1 t2 + Output: t2.b, t2.c + -> Hash + Output: t3.a, t3.b, t3.c, t1.a, t1.c + -> Hash Right Join + Output: t3.a, t3.b, t3.c, t1.a, t1.c + Hash Cond: (t1.a = ((t3.a + t3.b) / 2)) + -> Seq Scan on public.prt1_p1 t1 + Output: t1.a, t1.c + -> Hash + Output: t3.a, t3.b, t3.c + -> Seq Scan on public.prt1_e_p1 t3 + Output: t3.a, t3.b, t3.c + Filter: ((t3.a % 25) = 0) + -> Hash Right Join + Output: t3_1.a, t3_1.b, t3_1.c, t1_2.a, t1_2.c, t2_1.b, t2_1.c + Hash Cond: (t2_1.b = t1_2.a) + -> Seq Scan on public.prt2_p2 t2_1 + Output: t2_1.b, t2_1.c + -> Hash + Output: t3_1.a, t3_1.b, t3_1.c, t1_2.a, t1_2.c + -> Hash Right Join + Output: t3_1.a, t3_1.b, t3_1.c, t1_2.a, t1_2.c + Hash Cond: (t1_2.a = ((t3_1.a + t3_1.b) / 2)) + -> Seq Scan on public.prt1_p2 t1_2 + Output: t1_2.a, t1_2.c + -> Hash + Output: t3_1.a, t3_1.b, t3_1.c + -> Seq Scan on public.prt1_e_p2 t3_1 + Output: t3_1.a, t3_1.b, t3_1.c + Filter: ((t3_1.a % 25) = 0) + -> Hash Right Join + Output: t3_2.a, t3_2.b, t3_2.c, t1_1.a, t1_1.c, t2_2.b, t2_2.c + Hash Cond: (t2_2.b = t1_1.a) + -> Seq Scan on public.prt2_p3 t2_2 + Output: t2_2.b, t2_2.c + -> Hash + Output: t3_2.a, t3_2.b, t3_2.c, t1_1.a, t1_1.c + -> Hash Right Join + Output: t3_2.a, t3_2.b, t3_2.c, t1_1.a, t1_1.c + Hash Cond: (t1_1.a = ((t3_2.a + t3_2.b) / 2)) + -> Seq Scan on public.prt1_p3 t1_1 + Output: t1_1.a, t1_1.c + -> Hash + Output: t3_2.a, t3_2.b, t3_2.c + -> Seq Scan on public.prt1_e_p3 t3_2 + Output: t3_2.a, t3_2.b, t3_2.c + Filter: ((t3_2.a % 25) = 0) +(57 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.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; + a | c | b | c | ?column? | c +-----+------+-----+------+----------+------ + 0 | 0000 | 0 | 0000 | 0 | 0000 + 50 | 0050 | | | 100 | 0050 + 100 | 0100 | | | 200 | 0100 + 150 | 0150 | 150 | 0150 | 300 | 0150 + 200 | 0200 | | | 400 | 0200 + 250 | 0250 | | | 500 | 0250 + 300 | 0300 | 300 | 0300 | 600 | 0300 + 350 | 0350 | | | 700 | 0350 + 400 | 0400 | | | 800 | 0400 + 450 | 0450 | 450 | 0450 | 900 | 0450 + 500 | 0500 | | | 1000 | 0500 + 550 | 0550 | | | 1100 | 0550 +(12 rows) + +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uprt1 t1 LEFT JOIN uprt2 t2 ON t1.a = t2.b) RIGHT JOIN uprt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; + a | c | b | c | ?column? | c +-----+------+-----+------+----------+------ + 0 | 0000 | 0 | 0000 | 0 | 0000 + 50 | 0050 | | | 100 | 0050 + 100 | 0100 | | | 200 | 0100 + 150 | 0150 | 150 | 0150 | 300 | 0150 + 200 | 0200 | | | 400 | 0200 + 250 | 0250 | | | 500 | 0250 + 300 | 0300 | 300 | 0300 | 600 | 0300 + 350 | 0350 | | | 700 | 0350 + 400 | 0400 | | | 800 | 0400 + 450 | 0450 | 450 | 0450 | 900 | 0450 + 500 | 0500 | | | 1000 | 0500 + 550 | 0550 | | | 1100 | 0550 +(12 rows) + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t2.b = (t3.a + t3.b)/2) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; + QUERY PLAN +------------------------------------------------------------------------------------ + Sort + Output: t1.a, t1.c, t2.b, t2.c, ((t3.a + t3.b)), t3.c + Sort Key: t1.a, t2.b, ((t3.a + t3.b)) + -> Result + Output: t1.a, t1.c, t2.b, t2.c, (t3.a + t3.b), t3.c + -> Append + -> Hash Right Join + Output: t3.a, t3.b, t3.c, t2.b, t2.c, t1.a, t1.c + Hash Cond: (t1.a = t2.b) + -> Seq Scan on public.prt1_p1 t1 + Output: t1.a, t1.c + -> Hash + Output: t3.a, t3.b, t3.c, t2.b, t2.c + -> Hash Right Join + Output: t3.a, t3.b, t3.c, t2.b, t2.c + Hash Cond: (t2.b = ((t3.a + t3.b) / 2)) + -> Seq Scan on public.prt2_p1 t2 + Output: t2.b, t2.c + -> Hash + Output: t3.a, t3.b, t3.c + -> Seq Scan on public.prt1_e_p1 t3 + Output: t3.a, t3.b, t3.c + Filter: ((t3.a % 25) = 0) + -> Hash Right Join + Output: t3_1.a, t3_1.b, t3_1.c, t2_1.b, t2_1.c, t1_2.a, t1_2.c + Hash Cond: (t1_2.a = t2_1.b) + -> Seq Scan on public.prt1_p2 t1_2 + Output: t1_2.a, t1_2.c + -> Hash + Output: t3_1.a, t3_1.b, t3_1.c, t2_1.b, t2_1.c + -> Hash Right Join + Output: t3_1.a, t3_1.b, t3_1.c, t2_1.b, t2_1.c + Hash Cond: (t2_1.b = ((t3_1.a + t3_1.b) / 2)) + -> Seq Scan on public.prt2_p2 t2_1 + Output: t2_1.b, t2_1.c + -> Hash + Output: t3_1.a, t3_1.b, t3_1.c + -> Seq Scan on public.prt1_e_p2 t3_1 + Output: t3_1.a, t3_1.b, t3_1.c + Filter: ((t3_1.a % 25) = 0) + -> Hash Right Join + Output: t3_2.a, t3_2.b, t3_2.c, t2_2.b, t2_2.c, t1_1.a, t1_1.c + Hash Cond: (t1_1.a = t2_2.b) + -> Seq Scan on public.prt1_p3 t1_1 + Output: t1_1.a, t1_1.c + -> Hash + Output: t3_2.a, t3_2.b, t3_2.c, t2_2.b, t2_2.c + -> Hash Right Join + Output: t3_2.a, t3_2.b, t3_2.c, t2_2.b, t2_2.c + Hash Cond: (t2_2.b = ((t3_2.a + t3_2.b) / 2)) + -> Seq Scan on public.prt2_p3 t2_2 + Output: t2_2.b, t2_2.c + -> Hash + Output: t3_2.a, t3_2.b, t3_2.c + -> Seq Scan on public.prt1_e_p3 t3_2 + Output: t3_2.a, t3_2.b, t3_2.c + Filter: ((t3_2.a % 25) = 0) +(57 rows) + +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t2.b = (t3.a + t3.b)/2) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; + a | c | b | c | ?column? | c +-----+------+-----+------+----------+------ + 0 | 0000 | 0 | 0000 | 0 | 0000 + 150 | 0150 | 150 | 0150 | 300 | 0150 + 300 | 0300 | 300 | 0300 | 600 | 0300 + 450 | 0450 | 450 | 0450 | 900 | 0450 + | | | | 100 | 0050 + | | | | 200 | 0100 + | | | | 400 | 0200 + | | | | 500 | 0250 + | | | | 700 | 0350 + | | | | 800 | 0400 + | | | | 1000 | 0500 + | | | | 1100 | 0550 +(12 rows) + +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uprt1 t1 RIGHT JOIN uprt2 t2 ON t1.a = t2.b) RIGHT JOIN uprt1_e t3 ON (t2.b = (t3.a + t3.b)/2) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; + a | c | b | c | ?column? | c +-----+------+-----+------+----------+------ + 0 | 0000 | 0 | 0000 | 0 | 0000 + 150 | 0150 | 150 | 0150 | 300 | 0150 + 300 | 0300 | 300 | 0300 | 600 | 0300 + 450 | 0450 | 450 | 0450 | 900 | 0450 + | | | | 100 | 0050 + | | | | 200 | 0100 + | | | | 400 | 0200 + | | | | 500 | 0250 + | | | | 700 | 0350 + | | | | 800 | 0400 + | | | | 1000 | 0500 + | | | | 1100 | 0550 +(12 rows) + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM ((SELECT * FROM prt1 WHERE prt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM prt2 WHERE prt2.b % 25 = 0) t2 ON (t1.a = t2.b)) FULL JOIN (SELECT * FROM prt1_e WHERE prt1_e.a % 25 = 0) t3 ON (t1.a = (t3.a + t3.b)/2) ORDER BY t1.a, t2.b, t3.a + t3.b; + QUERY PLAN +--------------------------------------------------------------------------------------------------------------- + Sort + Output: prt1_p1.a, prt1_p1.c, prt2_p1.b, prt2_p1.c, ((prt1_e_p1.a + prt1_e_p1.b)), prt1_e_p1.c + Sort Key: prt1_p1.a, prt2_p1.b, ((prt1_e_p1.a + prt1_e_p1.b)) + -> Result + Output: prt1_p1.a, prt1_p1.c, prt2_p1.b, prt2_p1.c, (prt1_e_p1.a + prt1_e_p1.b), prt1_e_p1.c + -> Append + -> Hash Full Join + Output: prt1_p1.a, prt1_p1.c, prt2_p1.b, prt2_p1.c, prt1_e_p1.a, prt1_e_p1.b, prt1_e_p1.c + Hash Cond: (prt1_p1.a = ((prt1_e_p1.a + prt1_e_p1.b) / 2)) + -> Hash Full Join + Output: prt1_p1.a, prt1_p1.c, prt2_p1.b, prt2_p1.c + Hash Cond: (prt1_p1.a = prt2_p1.b) + -> Seq Scan on public.prt1_p1 + Output: prt1_p1.a, prt1_p1.c + Filter: ((prt1_p1.a % 25) = 0) + -> Hash + Output: prt2_p1.b, prt2_p1.c + -> Seq Scan on public.prt2_p1 + Output: prt2_p1.b, prt2_p1.c + Filter: ((prt2_p1.b % 25) = 0) + -> Hash + Output: prt1_e_p1.a, prt1_e_p1.b, prt1_e_p1.c + -> Seq Scan on public.prt1_e_p1 + Output: prt1_e_p1.a, prt1_e_p1.b, prt1_e_p1.c + Filter: ((prt1_e_p1.a % 25) = 0) + -> Hash Full Join + Output: prt1_p2.a, prt1_p2.c, prt2_p2.b, prt2_p2.c, prt1_e_p2.a, prt1_e_p2.b, prt1_e_p2.c + Hash Cond: (prt1_p2.a = ((prt1_e_p2.a + prt1_e_p2.b) / 2)) + -> Hash Full Join + Output: prt1_p2.a, prt1_p2.c, prt2_p2.b, prt2_p2.c + Hash Cond: (prt1_p2.a = prt2_p2.b) + -> Seq Scan on public.prt1_p2 + Output: prt1_p2.a, prt1_p2.c + Filter: ((prt1_p2.a % 25) = 0) + -> Hash + Output: prt2_p2.b, prt2_p2.c + -> Seq Scan on public.prt2_p2 + Output: prt2_p2.b, prt2_p2.c + Filter: ((prt2_p2.b % 25) = 0) + -> Hash + Output: prt1_e_p2.a, prt1_e_p2.b, prt1_e_p2.c + -> Seq Scan on public.prt1_e_p2 + Output: prt1_e_p2.a, prt1_e_p2.b, prt1_e_p2.c + Filter: ((prt1_e_p2.a % 25) = 0) + -> Hash Full Join + Output: prt1_p3.a, prt1_p3.c, prt2_p3.b, prt2_p3.c, prt1_e_p3.a, prt1_e_p3.b, prt1_e_p3.c + Hash Cond: (prt1_p3.a = ((prt1_e_p3.a + prt1_e_p3.b) / 2)) + -> Hash Full Join + Output: prt1_p3.a, prt1_p3.c, prt2_p3.b, prt2_p3.c + Hash Cond: (prt1_p3.a = prt2_p3.b) + -> Seq Scan on public.prt1_p3 + Output: prt1_p3.a, prt1_p3.c + Filter: ((prt1_p3.a % 25) = 0) + -> Hash + Output: prt2_p3.b, prt2_p3.c + -> Seq Scan on public.prt2_p3 + Output: prt2_p3.b, prt2_p3.c + Filter: ((prt2_p3.b % 25) = 0) + -> Hash + Output: prt1_e_p3.a, prt1_e_p3.b, prt1_e_p3.c + -> Seq Scan on public.prt1_e_p3 + Output: prt1_e_p3.a, prt1_e_p3.b, prt1_e_p3.c + Filter: ((prt1_e_p3.a % 25) = 0) +(63 rows) + +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM ((SELECT * FROM prt1 WHERE prt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM prt2 WHERE prt2.b % 25 = 0) t2 ON (t1.a = t2.b)) FULL JOIN (SELECT * FROM prt1_e WHERE prt1_e.a % 25 = 0) t3 ON (t1.a = (t3.a + t3.b)/2) ORDER BY t1.a, t2.b, t3.a + t3.b; + a | c | b | c | ?column? | c +-----+------+-----+------+----------+------ + 0 | 0000 | 0 | 0000 | 0 | 0000 + 50 | 0050 | | | 100 | 0050 + 100 | 0100 | | | 200 | 0100 + 150 | 0150 | 150 | 0150 | 300 | 0150 + 200 | 0200 | | | 400 | 0200 + 250 | 0250 | | | 500 | 0250 + 300 | 0300 | 300 | 0300 | 600 | 0300 + 350 | 0350 | | | 700 | 0350 + 400 | 0400 | | | 800 | 0400 + 450 | 0450 | 450 | 0450 | 900 | 0450 + 500 | 0500 | | | 1000 | 0500 + 550 | 0550 | | | 1100 | 0550 + | | 75 | 0075 | | + | | 225 | 0225 | | + | | 375 | 0375 | | + | | 525 | 0525 | | +(16 rows) + +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM ((SELECT * FROM uprt1 WHERE uprt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM uprt2 WHERE uprt2.b % 25 = 0) t2 ON (t1.a = t2.b)) FULL JOIN (SELECT * FROM uprt1_e WHERE uprt1_e.a % 25 = 0) t3 ON (t1.a = (t3.a + t3.b)/2) ORDER BY t1.a, t2.b, t3.a + t3.b; + a | c | b | c | ?column? | c +-----+------+-----+------+----------+------ + 0 | 0000 | 0 | 0000 | 0 | 0000 + 50 | 0050 | | | 100 | 0050 + 100 | 0100 | | | 200 | 0100 + 150 | 0150 | 150 | 0150 | 300 | 0150 + 200 | 0200 | | | 400 | 0200 + 250 | 0250 | | | 500 | 0250 + 300 | 0300 | 300 | 0300 | 600 | 0300 + 350 | 0350 | | | 700 | 0350 + 400 | 0400 | | | 800 | 0400 + 450 | 0450 | 450 | 0450 | 900 | 0450 + 500 | 0500 | | | 1000 | 0500 + 550 | 0550 | | | 1100 | 0550 + | | 75 | 0075 | | + | | 225 | 0225 | | + | | 375 | 0375 | | + | | 525 | 0525 | | +(16 rows) + +-- Cases with non-nullable expressions in subquery results; +-- make sure these go to null as expected +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.phv, t2.b, t2.phv, t3.a + t3.b, t3.phv FROM ((SELECT 50 phv, * FROM prt1 WHERE prt1.a % 25 = 0) t1 FULL JOIN (SELECT 75 phv, * FROM prt2 WHERE prt2.b % 25 = 0) t2 ON (t1.a = t2.b)) FULL JOIN (SELECT 50 phv, * FROM prt1_e WHERE prt1_e.a % 25 = 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; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------- + Sort + Output: prt1_p1.a, (50), prt2_p1.b, (75), ((prt1_e_p1.a + prt1_e_p1.b)), (50) + Sort Key: prt1_p1.a, prt2_p1.b, ((prt1_e_p1.a + prt1_e_p1.b)) + -> Result + Output: prt1_p1.a, (50), prt2_p1.b, (75), (prt1_e_p1.a + prt1_e_p1.b), (50) + -> Append + -> Hash Full Join + Output: prt1_p1.a, prt2_p1.b, prt1_e_p1.a, prt1_e_p1.b, (50), (75), (50) + 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 Full Join + Output: prt1_p1.a, prt2_p1.b, (50), (75) + Hash Cond: (prt1_p1.a = prt2_p1.b) + -> Seq Scan on public.prt1_p1 + Output: prt1_p1.a, 50 + Filter: ((prt1_p1.a % 25) = 0) + -> Hash + Output: prt2_p1.b, (75) + -> Seq Scan on public.prt2_p1 + Output: prt2_p1.b, 75 + Filter: ((prt2_p1.b % 25) = 0) + -> Hash + Output: prt1_e_p1.a, prt1_e_p1.b, (50) + -> Seq Scan on public.prt1_e_p1 + Output: prt1_e_p1.a, prt1_e_p1.b, 50 + Filter: ((prt1_e_p1.a % 25) = 0) + -> Hash Full Join + Output: prt1_p2.a, prt2_p2.b, prt1_e_p2.a, prt1_e_p2.b, (50), (75), (50) + 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))) + -> Hash Full Join + Output: prt1_p2.a, prt2_p2.b, (50), (75) + Hash Cond: (prt1_p2.a = prt2_p2.b) + -> Seq Scan on public.prt1_p2 + Output: prt1_p2.a, 50 + Filter: ((prt1_p2.a % 25) = 0) + -> Hash + Output: prt2_p2.b, (75) + -> Seq Scan on public.prt2_p2 + Output: prt2_p2.b, 75 + Filter: ((prt2_p2.b % 25) = 0) + -> Hash + Output: prt1_e_p2.a, prt1_e_p2.b, (50) + -> Seq Scan on public.prt1_e_p2 + Output: prt1_e_p2.a, prt1_e_p2.b, 50 + Filter: ((prt1_e_p2.a % 25) = 0) + -> Hash Full Join + Output: prt1_p3.a, prt2_p3.b, prt1_e_p3.a, prt1_e_p3.b, (50), (75), (50) + Hash Cond: (prt1_p3.a = ((prt1_e_p3.a + prt1_e_p3.b) / 2)) + Filter: ((prt1_p3.a = (50)) OR (prt2_p3.b = (75)) OR (((prt1_e_p3.a + prt1_e_p3.b) / 2) = (50))) + -> Hash Full Join + Output: prt1_p3.a, prt2_p3.b, (50), (75) + Hash Cond: (prt1_p3.a = prt2_p3.b) + -> Seq Scan on public.prt1_p3 + Output: prt1_p3.a, 50 + Filter: ((prt1_p3.a % 25) = 0) + -> Hash + Output: prt2_p3.b, (75) + -> Seq Scan on public.prt2_p3 + Output: prt2_p3.b, 75 + Filter: ((prt2_p3.b % 25) = 0) + -> Hash + Output: prt1_e_p3.a, prt1_e_p3.b, (50) + -> Seq Scan on public.prt1_e_p3 + Output: prt1_e_p3.a, prt1_e_p3.b, 50 + Filter: ((prt1_e_p3.a % 25) = 0) +(66 rows) + +SELECT t1.a, t1.phv, t2.b, t2.phv, t3.a + t3.b, t3.phv FROM ((SELECT 50 phv, * FROM prt1 WHERE prt1.a % 25 = 0) t1 FULL JOIN (SELECT 75 phv, * FROM prt2 WHERE prt2.b % 25 = 0) t2 ON (t1.a = t2.b)) FULL JOIN (SELECT 50 phv, * FROM prt1_e WHERE prt1_e.a % 25 = 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 +----+-----+----+-----+----------+----- + 50 | 50 | | | 100 | 50 + | | 75 | 75 | | +(2 rows) + +SELECT t1.a, t1.phv, t2.b, t2.phv, t3.a + t3.b, t3.phv FROM ((SELECT 50 phv, * FROM uprt1 WHERE uprt1.a % 25 = 0) t1 FULL JOIN (SELECT 75 phv, * FROM uprt2 WHERE uprt2.b % 25 = 0) t2 ON (t1.a = t2.b)) FULL JOIN (SELECT 50 phv, * FROM uprt1_e WHERE uprt1_e.a % 25 = 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 +----+-----+----+-----+----------+----- + 50 | 50 | | | 100 | 50 + | | 75 | 75 | | +(2 rows) + +-- Semi-join +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1, prt1_e t2 WHERE t1.b % 25 = 0 AND t1.b = (t2.a + t2.b)/2) AND t1.a % 25 = 0 ORDER BY t1.a; + QUERY PLAN +------------------------------------------------------------------------- + Sort + Output: t1.a, t1.b, t1.c + Sort Key: t1.a + -> Append + -> Nested Loop Semi Join + Output: t1.a, t1.b, t1.c + Join Filter: (t1.a = t1_3.b) + -> Seq Scan on public.prt1_p1 t1 + Output: t1.a, t1.b, t1.c + Filter: ((t1.a % 25) = 0) + -> Materialize + Output: t1_3.b, t2.a, t2.b + -> Hash Join + Output: t1_3.b, t2.a, t2.b + Hash Cond: (((t2.a + t2.b) / 2) = t1_3.b) + -> Seq Scan on public.prt1_e_p1 t2 + Output: t2.a, t2.b + -> Hash + Output: t1_3.b + -> Seq Scan on public.prt2_p1 t1_3 + Output: t1_3.b + Filter: ((t1_3.b % 25) = 0) + -> Nested Loop Semi Join + Output: t1_2.a, t1_2.b, t1_2.c + Join Filter: (t1_2.a = t1_4.b) + -> Seq Scan on public.prt1_p2 t1_2 + Output: t1_2.a, t1_2.b, t1_2.c + Filter: ((t1_2.a % 25) = 0) + -> Materialize + Output: t1_4.b, t2_1.a, t2_1.b + -> Hash Join + Output: t1_4.b, t2_1.a, t2_1.b + Hash Cond: (((t2_1.a + t2_1.b) / 2) = t1_4.b) + -> Seq Scan on public.prt1_e_p2 t2_1 + Output: t2_1.a, t2_1.b + -> Hash + Output: t1_4.b + -> Seq Scan on public.prt2_p2 t1_4 + Output: t1_4.b + Filter: ((t1_4.b % 25) = 0) + -> Nested Loop Semi Join + Output: t1_1.a, t1_1.b, t1_1.c + Join Filter: (t1_1.a = t1_5.b) + -> Seq Scan on public.prt1_p3 t1_1 + Output: t1_1.a, t1_1.b, t1_1.c + Filter: ((t1_1.a % 25) = 0) + -> Materialize + Output: t1_5.b, t2_2.a, t2_2.b + -> Hash Join + Output: t1_5.b, t2_2.a, t2_2.b + Hash Cond: (((t2_2.a + t2_2.b) / 2) = t1_5.b) + -> Seq Scan on public.prt1_e_p3 t2_2 + Output: t2_2.a, t2_2.b + -> Hash + Output: t1_5.b + -> Seq Scan on public.prt2_p3 t1_5 + Output: t1_5.b + Filter: ((t1_5.b % 25) = 0) +(58 rows) + +SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1, prt1_e t2 WHERE t1.b % 25 = 0 AND t1.b = (t2.a + t2.b)/2) AND t1.a % 25 = 0 ORDER BY t1.a; + a | b | c +-----+-----+------ + 0 | 0 | 0000 + 150 | 150 | 0150 + 300 | 300 | 0300 + 450 | 450 | 0450 +(4 rows) + +SELECT t1.* FROM uprt1 t1 WHERE t1.a IN (SELECT t1.b FROM uprt2 t1, uprt1_e t2 WHERE t1.b % 25 = 0 AND t1.b = (t2.a + t2.b)/2) AND t1.a % 25 = 0 ORDER BY t1.a; + a | b | c +-----+-----+------ + 0 | 0 | 0000 + 150 | 150 | 0150 + 300 | 300 | 0300 + 450 | 450 | 0450 +(4 rows) + +EXPLAIN (VERBOSE, 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.a %25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a; + QUERY PLAN +------------------------------------------------------------------------- + Sort + Output: t1.a, t1.b, t1.c + Sort Key: t1.a + -> Append + -> Nested Loop Semi Join + Output: t1.a, t1.b, t1.c + Join Filter: (t1.a = t1_3.b) + -> Seq Scan on public.prt1_p1 t1 + Output: t1.a, t1.b, t1.c + Filter: ((t1.a % 25) = 0) + -> Materialize + Output: t1_3.b, t1_6.a, t1_6.b + -> Hash Semi Join + Output: t1_3.b, t1_6.a, t1_6.b + Hash Cond: (t1_3.b = ((t1_6.a + t1_6.b) / 2)) + -> Seq Scan on public.prt2_p1 t1_3 + Output: t1_3.b + -> Hash + Output: t1_6.a, t1_6.b + -> Seq Scan on public.prt1_e_p1 t1_6 + Output: t1_6.a, t1_6.b + Filter: ((t1_6.a % 25) = 0) + -> Nested Loop Semi Join + Output: t1_2.a, t1_2.b, t1_2.c + Join Filter: (t1_2.a = t1_4.b) + -> Seq Scan on public.prt1_p2 t1_2 + Output: t1_2.a, t1_2.b, t1_2.c + Filter: ((t1_2.a % 25) = 0) + -> Materialize + Output: t1_4.b, t1_7.a, t1_7.b + -> Hash Semi Join + Output: t1_4.b, t1_7.a, t1_7.b + Hash Cond: (t1_4.b = ((t1_7.a + t1_7.b) / 2)) + -> Seq Scan on public.prt2_p2 t1_4 + Output: t1_4.b + -> Hash + Output: t1_7.a, t1_7.b + -> Seq Scan on public.prt1_e_p2 t1_7 + Output: t1_7.a, t1_7.b + Filter: ((t1_7.a % 25) = 0) + -> Nested Loop Semi Join + Output: t1_1.a, t1_1.b, t1_1.c + Join Filter: (t1_1.a = t1_5.b) + -> Seq Scan on public.prt1_p3 t1_1 + Output: t1_1.a, t1_1.b, t1_1.c + Filter: ((t1_1.a % 25) = 0) + -> Materialize + Output: t1_5.b, t1_8.a, t1_8.b + -> Hash Semi Join + Output: t1_5.b, t1_8.a, t1_8.b + Hash Cond: (t1_5.b = ((t1_8.a + t1_8.b) / 2)) + -> Seq Scan on public.prt2_p3 t1_5 + Output: t1_5.b + -> Hash + Output: t1_8.a, t1_8.b + -> Seq Scan on public.prt1_e_p3 t1_8 + Output: t1_8.a, t1_8.b + Filter: ((t1_8.a % 25) = 0) +(58 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.a %25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a; + a | b | c +-----+-----+------ + 0 | 0 | 0000 + 150 | 150 | 0150 + 300 | 300 | 0300 + 450 | 450 | 0450 +(4 rows) + +SELECT t1.* FROM uprt1 t1 WHERE t1.a IN (SELECT t1.b FROM uprt2 t1 WHERE t1.b IN (SELECT (t1.a + t1.b)/2 FROM uprt1_e t1 WHERE t1.a %25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a; + a | b | c +-----+-----+------ + 0 | 0 | 0000 + 150 | 150 | 0150 + 300 | 300 | 0300 + 450 | 450 | 0450 +(4 rows) + +-- test merge joins with and without using indexes +SET enable_hashjoin TO off; +SET enable_nestloop TO off; +CREATE INDEX iprt1_a on prt1(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 iprt2_b on prt2(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 iprt1_e_ab2 on prt1_e(((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)); +ANALYZE prt1; +ANALYZE prt1_p1; +ANALYZE prt1_p2; +ANALYZE prt1_p3; +ANALYZE prt2; +ANALYZE prt2_p1; +ANALYZE prt2_p2; +ANALYZE prt2_p3; +ANALYZE prt1_e; +ANALYZE prt1_e_p1; +ANALYZE prt1_e_p2; +ANALYZE prt1_e_p3; +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t2.b = (t3.a + t3.b)/2) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; + QUERY PLAN +----------------------------------------------------------------------------------------------------- + Sort + Output: t1.a, t1.c, t2.b, t2.c, ((t3.a + t3.b)), t3.c + Sort Key: t1.a, t2.b, ((t3.a + t3.b)) + -> Result + Output: t1.a, t1.c, t2.b, t2.c, (t3.a + t3.b), t3.c + -> Append + -> Merge Left Join + Output: t3.a, t3.b, t3.c, t2.b, t2.c, t1.a, t1.c + Merge Cond: (t2.b = t1.a) + -> Sort + Output: t3.a, t3.b, t3.c, t2.b, t2.c + Sort Key: t2.b + -> Merge Left Join + Output: t3.a, t3.b, t3.c, t2.b, t2.c + Merge Cond: ((((t3.a + t3.b) / 2)) = t2.b) + -> Sort + Output: t3.a, t3.b, t3.c, (((t3.a + t3.b) / 2)) + Sort Key: (((t3.a + t3.b) / 2)) + -> Seq Scan on public.prt1_e_p1 t3 + Output: t3.a, t3.b, t3.c, ((t3.a + t3.b) / 2) + Filter: ((t3.a % 25) = 0) + -> Sort + Output: t2.b, t2.c + Sort Key: t2.b + -> Seq Scan on public.prt2_p1 t2 + Output: t2.b, t2.c + -> Sort + Output: t1.a, t1.c + Sort Key: t1.a + -> Seq Scan on public.prt1_p1 t1 + Output: t1.a, t1.c + -> Merge Left Join + Output: t3_1.a, t3_1.b, t3_1.c, t2_1.b, t2_1.c, t1_2.a, t1_2.c + Merge Cond: (t2_1.b = t1_2.a) + -> Sort + Output: t3_1.a, t3_1.b, t3_1.c, t2_1.b, t2_1.c + Sort Key: t2_1.b + -> Merge Left Join + Output: t3_1.a, t3_1.b, t3_1.c, t2_1.b, t2_1.c + Merge Cond: ((((t3_1.a + t3_1.b) / 2)) = t2_1.b) + -> Sort + Output: t3_1.a, t3_1.b, t3_1.c, (((t3_1.a + t3_1.b) / 2)) + Sort Key: (((t3_1.a + t3_1.b) / 2)) + -> Seq Scan on public.prt1_e_p2 t3_1 + Output: t3_1.a, t3_1.b, t3_1.c, ((t3_1.a + t3_1.b) / 2) + Filter: ((t3_1.a % 25) = 0) + -> Sort + Output: t2_1.b, t2_1.c + Sort Key: t2_1.b + -> Seq Scan on public.prt2_p2 t2_1 + Output: t2_1.b, t2_1.c + -> Sort + Output: t1_2.a, t1_2.c + Sort Key: t1_2.a + -> Seq Scan on public.prt1_p2 t1_2 + Output: t1_2.a, t1_2.c + -> Merge Left Join + Output: t3_2.a, t3_2.b, t3_2.c, t2_2.b, t2_2.c, t1_1.a, t1_1.c + Merge Cond: (t2_2.b = t1_1.a) + -> Sort + Output: t3_2.a, t3_2.b, t3_2.c, t2_2.b, t2_2.c + Sort Key: t2_2.b + -> Merge Left Join + Output: t3_2.a, t3_2.b, t3_2.c, t2_2.b, t2_2.c + Merge Cond: ((((t3_2.a + t3_2.b) / 2)) = t2_2.b) + -> Sort + Output: t3_2.a, t3_2.b, t3_2.c, (((t3_2.a + t3_2.b) / 2)) + Sort Key: (((t3_2.a + t3_2.b) / 2)) + -> Seq Scan on public.prt1_e_p3 t3_2 + Output: t3_2.a, t3_2.b, t3_2.c, ((t3_2.a + t3_2.b) / 2) + Filter: ((t3_2.a % 25) = 0) + -> Sort + Output: t2_2.b, t2_2.c + Sort Key: t2_2.b + -> Seq Scan on public.prt2_p3 t2_2 + Output: t2_2.b, t2_2.c + -> Sort + Output: t1_1.a, t1_1.c + Sort Key: t1_1.a + -> Seq Scan on public.prt1_p3 t1_1 + Output: t1_1.a, t1_1.c +(81 rows) + +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t2.b = (t3.a + t3.b)/2) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; + a | c | b | c | ?column? | c +-----+------+-----+------+----------+------ + 0 | 0000 | 0 | 0000 | 0 | 0000 + 150 | 0150 | 150 | 0150 | 300 | 0150 + 300 | 0300 | 300 | 0300 | 600 | 0300 + 450 | 0450 | 450 | 0450 | 900 | 0450 + | | | | 100 | 0050 + | | | | 200 | 0100 + | | | | 400 | 0200 + | | | | 500 | 0250 + | | | | 700 | 0350 + | | | | 800 | 0400 + | | | | 1000 | 0500 + | | | | 1100 | 0550 +(12 rows) + +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uprt1 t1 RIGHT JOIN uprt2 t2 ON t1.a = t2.b) RIGHT JOIN uprt1_e t3 ON (t2.b = (t3.a + t3.b)/2) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; + a | c | b | c | ?column? | c +-----+------+-----+------+----------+------ + 0 | 0000 | 0 | 0000 | 0 | 0000 + 150 | 0150 | 150 | 0150 | 300 | 0150 + 300 | 0300 | 300 | 0300 | 600 | 0300 + 450 | 0450 | 450 | 0450 | 900 | 0450 + | | | | 100 | 0050 + | | | | 200 | 0100 + | | | | 400 | 0200 + | | | | 500 | 0250 + | | | | 700 | 0350 + | | | | 800 | 0400 + | | | | 1000 | 0500 + | | | | 1100 | 0550 +(12 rows) + +EXPLAIN (VERBOSE, 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.a %25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a; + QUERY PLAN +--------------------------------------------------------------------------------- + Merge Append + Sort Key: t1.a + -> Merge Semi Join + Output: t1.a, t1.b, t1.c + Merge Cond: (t1.a = t1_3.b) + -> Sort + Output: t1.a, t1.b, t1.c + Sort Key: t1.a + -> Seq Scan on public.prt1_p1 t1 + Output: t1.a, t1.b, t1.c + Filter: ((t1.a % 25) = 0) + -> Materialize + Output: t1_3.b, t1_6.a, t1_6.b + -> Merge Semi Join + Output: t1_3.b, t1_6.a, t1_6.b + Merge Cond: (t1_3.b = (((t1_6.a + t1_6.b) / 2))) + -> Sort + Output: t1_3.b + Sort Key: t1_3.b + -> Seq Scan on public.prt2_p1 t1_3 + Output: t1_3.b + -> Sort + Output: t1_6.a, t1_6.b, (((t1_6.a + t1_6.b) / 2)) + Sort Key: (((t1_6.a + t1_6.b) / 2)) + -> Seq Scan on public.prt1_e_p1 t1_6 + Output: t1_6.a, t1_6.b, ((t1_6.a + t1_6.b) / 2) + Filter: ((t1_6.a % 25) = 0) + -> Merge Semi Join + Output: t1_2.a, t1_2.b, t1_2.c + Merge Cond: (t1_2.a = t1_4.b) + -> Sort + Output: t1_2.a, t1_2.b, t1_2.c + Sort Key: t1_2.a + -> Seq Scan on public.prt1_p2 t1_2 + Output: t1_2.a, t1_2.b, t1_2.c + Filter: ((t1_2.a % 25) = 0) + -> Materialize + Output: t1_4.b, t1_7.a, t1_7.b + -> Merge Semi Join + Output: t1_4.b, t1_7.a, t1_7.b + Merge Cond: (t1_4.b = (((t1_7.a + t1_7.b) / 2))) + -> Sort + Output: t1_4.b + Sort Key: t1_4.b + -> Seq Scan on public.prt2_p2 t1_4 + Output: t1_4.b + -> Sort + Output: t1_7.a, t1_7.b, (((t1_7.a + t1_7.b) / 2)) + Sort Key: (((t1_7.a + t1_7.b) / 2)) + -> Seq Scan on public.prt1_e_p2 t1_7 + Output: t1_7.a, t1_7.b, ((t1_7.a + t1_7.b) / 2) + Filter: ((t1_7.a % 25) = 0) + -> Merge Semi Join + Output: t1_1.a, t1_1.b, t1_1.c + Merge Cond: (t1_1.a = t1_5.b) + -> Sort + Output: t1_1.a, t1_1.b, t1_1.c + Sort Key: t1_1.a + -> Seq Scan on public.prt1_p3 t1_1 + Output: t1_1.a, t1_1.b, t1_1.c + Filter: ((t1_1.a % 25) = 0) + -> Materialize + Output: t1_5.b, t1_8.a, t1_8.b + -> Merge Semi Join + Output: t1_5.b, t1_8.a, t1_8.b + Merge Cond: (t1_5.b = (((t1_8.a + t1_8.b) / 2))) + -> Sort + Output: t1_5.b + Sort Key: t1_5.b + -> Seq Scan on public.prt2_p3 t1_5 + Output: t1_5.b + -> Sort + Output: t1_8.a, t1_8.b, (((t1_8.a + t1_8.b) / 2)) + Sort Key: (((t1_8.a + t1_8.b) / 2)) + -> Seq Scan on public.prt1_e_p3 t1_8 + Output: t1_8.a, t1_8.b, ((t1_8.a + t1_8.b) / 2) + Filter: ((t1_8.a % 25) = 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.a %25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a; + a | b | c +-----+-----+------ + 0 | 0 | 0000 + 150 | 150 | 0150 + 300 | 300 | 0300 + 450 | 450 | 0450 +(4 rows) + +SELECT t1.* FROM uprt1 t1 WHERE t1.a IN (SELECT t1.b FROM uprt2 t1 WHERE t1.b IN (SELECT (t1.a + t1.b)/2 FROM uprt1_e t1 WHERE t1.a %25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a; + a | b | c +-----+-----+------ + 0 | 0 | 0000 + 150 | 150 | 0150 + 300 | 300 | 0300 + 450 | 450 | 0450 +(4 rows) + +EXPLAIN (VERBOSE, 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.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; + QUERY PLAN +----------------------------------------------------------------------------------------------------- + Sort + Output: t1.a, t1.c, t2.b, t2.c, ((t3.a + t3.b)), t3.c + Sort Key: t1.a, t2.b, ((t3.a + t3.b)) + -> Result + Output: t1.a, t1.c, t2.b, t2.c, (t3.a + t3.b), t3.c + -> Append + -> Merge Left Join + Output: t3.a, t3.b, t3.c, t1.a, t1.c, t2.b, t2.c + Merge Cond: (t1.a = t2.b) + -> Sort + Output: t3.a, t3.b, t3.c, t1.a, t1.c + Sort Key: t1.a + -> Merge Left Join + Output: t3.a, t3.b, t3.c, t1.a, t1.c + Merge Cond: ((((t3.a + t3.b) / 2)) = t1.a) + -> Sort + Output: t3.a, t3.b, t3.c, (((t3.a + t3.b) / 2)) + Sort Key: (((t3.a + t3.b) / 2)) + -> Seq Scan on public.prt1_e_p1 t3 + Output: t3.a, t3.b, t3.c, ((t3.a + t3.b) / 2) + Filter: ((t3.a % 25) = 0) + -> Sort + Output: t1.a, t1.c + Sort Key: t1.a + -> Seq Scan on public.prt1_p1 t1 + Output: t1.a, t1.c + -> Sort + Output: t2.b, t2.c + Sort Key: t2.b + -> Seq Scan on public.prt2_p1 t2 + Output: t2.b, t2.c + -> Merge Left Join + Output: t3_1.a, t3_1.b, t3_1.c, t1_2.a, t1_2.c, t2_1.b, t2_1.c + Merge Cond: (t1_2.a = t2_1.b) + -> Sort + Output: t3_1.a, t3_1.b, t3_1.c, t1_2.a, t1_2.c + Sort Key: t1_2.a + -> Merge Left Join + Output: t3_1.a, t3_1.b, t3_1.c, t1_2.a, t1_2.c + Merge Cond: ((((t3_1.a + t3_1.b) / 2)) = t1_2.a) + -> Sort + Output: t3_1.a, t3_1.b, t3_1.c, (((t3_1.a + t3_1.b) / 2)) + Sort Key: (((t3_1.a + t3_1.b) / 2)) + -> Seq Scan on public.prt1_e_p2 t3_1 + Output: t3_1.a, t3_1.b, t3_1.c, ((t3_1.a + t3_1.b) / 2) + Filter: ((t3_1.a % 25) = 0) + -> Sort + Output: t1_2.a, t1_2.c + Sort Key: t1_2.a + -> Seq Scan on public.prt1_p2 t1_2 + Output: t1_2.a, t1_2.c + -> Sort + Output: t2_1.b, t2_1.c + Sort Key: t2_1.b + -> Seq Scan on public.prt2_p2 t2_1 + Output: t2_1.b, t2_1.c + -> Merge Left Join + Output: t3_2.a, t3_2.b, t3_2.c, t1_1.a, t1_1.c, t2_2.b, t2_2.c + Merge Cond: (t1_1.a = t2_2.b) + -> Sort + Output: t3_2.a, t3_2.b, t3_2.c, t1_1.a, t1_1.c + Sort Key: t1_1.a + -> Merge Left Join + Output: t3_2.a, t3_2.b, t3_2.c, t1_1.a, t1_1.c + Merge Cond: ((((t3_2.a + t3_2.b) / 2)) = t1_1.a) + -> Sort + Output: t3_2.a, t3_2.b, t3_2.c, (((t3_2.a + t3_2.b) / 2)) + Sort Key: (((t3_2.a + t3_2.b) / 2)) + -> Seq Scan on public.prt1_e_p3 t3_2 + Output: t3_2.a, t3_2.b, t3_2.c, ((t3_2.a + t3_2.b) / 2) + Filter: ((t3_2.a % 25) = 0) + -> Sort + Output: t1_1.a, t1_1.c + Sort Key: t1_1.a + -> Seq Scan on public.prt1_p3 t1_1 + Output: t1_1.a, t1_1.c + -> Sort + Output: t2_2.b, t2_2.c + Sort Key: t2_2.b + -> Seq Scan on public.prt2_p3 t2_2 + Output: t2_2.b, t2_2.c +(81 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.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; + a | c | b | c | ?column? | c +-----+------+-----+------+----------+------ + 0 | 0000 | 0 | 0000 | 0 | 0000 + 50 | 0050 | | | 100 | 0050 + 100 | 0100 | | | 200 | 0100 + 150 | 0150 | 150 | 0150 | 300 | 0150 + 200 | 0200 | | | 400 | 0200 + 250 | 0250 | | | 500 | 0250 + 300 | 0300 | 300 | 0300 | 600 | 0300 + 350 | 0350 | | | 700 | 0350 + 400 | 0400 | | | 800 | 0400 + 450 | 0450 | 450 | 0450 | 900 | 0450 + 500 | 0500 | | | 1000 | 0500 + 550 | 0550 | | | 1100 | 0550 +(12 rows) + +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uprt1 t1 LEFT JOIN uprt2 t2 ON t1.a = t2.b) RIGHT JOIN uprt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; + a | c | b | c | ?column? | c +-----+------+-----+------+----------+------ + 0 | 0000 | 0 | 0000 | 0 | 0000 + 50 | 0050 | | | 100 | 0050 + 100 | 0100 | | | 200 | 0100 + 150 | 0150 | 150 | 0150 | 300 | 0150 + 200 | 0200 | | | 400 | 0200 + 250 | 0250 | | | 500 | 0250 + 300 | 0300 | 300 | 0300 | 600 | 0300 + 350 | 0350 | | | 700 | 0350 + 400 | 0400 | | | 800 | 0400 + 450 | 0450 | 450 | 0450 | 900 | 0450 + 500 | 0500 | | | 1000 | 0500 + 550 | 0550 | | | 1100 | 0550 +(12 rows) + +SET enable_seqscan TO off; +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t2.b = (t3.a + t3.b)/2) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; + QUERY PLAN +---------------------------------------------------------------------------------------------- + Sort + Output: t1.a, t1.c, t2.b, t2.c, ((t3.a + t3.b)), t3.c + Sort Key: t1.a, t2.b, ((t3.a + t3.b)) + -> Result + Output: t1.a, t1.c, t2.b, t2.c, (t3.a + t3.b), t3.c + -> Append + -> Merge Left Join + Output: t3.a, t3.b, t3.c, t2.b, t2.c, t1.a, t1.c + Merge Cond: (t2.b = t1.a) + -> Sort + Output: t3.a, t3.b, t3.c, t2.b, t2.c + Sort Key: t2.b + -> Merge Left Join + Output: t3.a, t3.b, t3.c, t2.b, t2.c + Merge Cond: (((t3.a + t3.b) / 2) = t2.b) + -> Index Scan using iprt1_e_p1_ab2 on public.prt1_e_p1 t3 + Output: t3.a, t3.b, t3.c + Filter: ((t3.a % 25) = 0) + -> Index Scan using iprt2_p1_b on public.prt2_p1 t2 + Output: t2.b, t2.c + -> Index Scan using iprt1_p1_a on public.prt1_p1 t1 + Output: t1.a, t1.c + -> Merge Left Join + Output: t3_1.a, t3_1.b, t3_1.c, t2_1.b, t2_1.c, t1_2.a, t1_2.c + Merge Cond: (t2_1.b = t1_2.a) + -> Sort + Output: t3_1.a, t3_1.b, t3_1.c, t2_1.b, t2_1.c + Sort Key: t2_1.b + -> Merge Left Join + Output: t3_1.a, t3_1.b, t3_1.c, t2_1.b, t2_1.c + Merge Cond: (((t3_1.a + t3_1.b) / 2) = t2_1.b) + -> Index Scan using iprt1_e_p2_ab2 on public.prt1_e_p2 t3_1 + Output: t3_1.a, t3_1.b, t3_1.c + Filter: ((t3_1.a % 25) = 0) + -> Index Scan using iprt2_p2_b on public.prt2_p2 t2_1 + Output: t2_1.b, t2_1.c + -> Index Scan using iprt1_p2_a on public.prt1_p2 t1_2 + Output: t1_2.a, t1_2.c + -> Merge Right Join + Output: t3_2.a, t3_2.b, t3_2.c, t2_2.b, t2_2.c, t1_1.a, t1_1.c + Merge Cond: (t2_2.b = ((t3_2.a + t3_2.b) / 2)) + -> Merge Left Join + Output: t2_2.b, t2_2.c, t1_1.a, t1_1.c + Merge Cond: (t2_2.b = t1_1.a) + -> Index Scan using iprt2_p3_b on public.prt2_p3 t2_2 + Output: t2_2.b, t2_2.c + -> Index Scan using iprt1_p3_a on public.prt1_p3 t1_1 + Output: t1_1.a, t1_1.c + -> Index Scan using iprt1_e_p3_ab2 on public.prt1_e_p3 t3_2 + Output: t3_2.a, t3_2.b, t3_2.c + Filter: ((t3_2.a % 25) = 0) +(51 rows) + +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t2.b = (t3.a + t3.b)/2) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; + a | c | b | c | ?column? | c +-----+------+-----+------+----------+------ + 0 | 0000 | 0 | 0000 | 0 | 0000 + 150 | 0150 | 150 | 0150 | 300 | 0150 + 300 | 0300 | 300 | 0300 | 600 | 0300 + 450 | 0450 | 450 | 0450 | 900 | 0450 + | | | | 100 | 0050 + | | | | 200 | 0100 + | | | | 400 | 0200 + | | | | 500 | 0250 + | | | | 700 | 0350 + | | | | 800 | 0400 + | | | | 1000 | 0500 + | | | | 1100 | 0550 +(12 rows) + +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uprt1 t1 RIGHT JOIN uprt2 t2 ON t1.a = t2.b) RIGHT JOIN uprt1_e t3 ON (t2.b = (t3.a + t3.b)/2) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; + a | c | b | c | ?column? | c +-----+------+-----+------+----------+------ + 0 | 0000 | 0 | 0000 | 0 | 0000 + 150 | 0150 | 150 | 0150 | 300 | 0150 + 300 | 0300 | 300 | 0300 | 600 | 0300 + 450 | 0450 | 450 | 0450 | 900 | 0450 + | | | | 100 | 0050 + | | | | 200 | 0100 + | | | | 400 | 0200 + | | | | 500 | 0250 + | | | | 700 | 0350 + | | | | 800 | 0400 + | | | | 1000 | 0500 + | | | | 1100 | 0550 +(12 rows) + +EXPLAIN (VERBOSE, 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.a %25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a; + QUERY PLAN +---------------------------------------------------------------------------------- + Merge Append + Sort Key: t1.a + -> Merge Semi Join + Output: t1.a, t1.b, t1.c + Merge Cond: (t1.a = t1_3.b) + -> Index Scan using iprt1_p1_a on public.prt1_p1 t1 + Output: t1.a, t1.b, t1.c + Filter: ((t1.a % 25) = 0) + -> Materialize + Output: t1_3.b, t1_6.a, t1_6.b + -> Merge Semi Join + Output: t1_3.b, t1_6.a, t1_6.b + Merge Cond: (t1_3.b = ((t1_6.a + t1_6.b) / 2)) + -> Index Only Scan using iprt2_p1_b on public.prt2_p1 t1_3 + Output: t1_3.b + -> Index Scan using iprt1_e_p1_ab2 on public.prt1_e_p1 t1_6 + Output: t1_6.a, t1_6.b + Filter: ((t1_6.a % 25) = 0) + -> Merge Semi Join + Output: t1_2.a, t1_2.b, t1_2.c + Merge Cond: (t1_2.a = t1_4.b) + -> Index Scan using iprt1_p2_a on public.prt1_p2 t1_2 + Output: t1_2.a, t1_2.b, t1_2.c + Filter: ((t1_2.a % 25) = 0) + -> Materialize + Output: t1_4.b, t1_7.a, t1_7.b + -> Merge Semi Join + Output: t1_4.b, t1_7.a, t1_7.b + Merge Cond: (t1_4.b = ((t1_7.a + t1_7.b) / 2)) + -> Index Only Scan using iprt2_p2_b on public.prt2_p2 t1_4 + Output: t1_4.b + -> Index Scan using iprt1_e_p2_ab2 on public.prt1_e_p2 t1_7 + Output: t1_7.a, t1_7.b + Filter: ((t1_7.a % 25) = 0) + -> Merge Semi Join + Output: t1_1.a, t1_1.b, t1_1.c + Merge Cond: (t1_1.a = t1_5.b) + -> Index Scan using iprt1_p3_a on public.prt1_p3 t1_1 + Output: t1_1.a, t1_1.b, t1_1.c + Filter: ((t1_1.a % 25) = 0) + -> Materialize + Output: t1_5.b, t1_8.a, t1_8.b + -> Merge Semi Join + Output: t1_5.b, t1_8.a, t1_8.b + Merge Cond: (t1_5.b = ((t1_8.a + t1_8.b) / 2)) + -> Index Only Scan using iprt2_p3_b on public.prt2_p3 t1_5 + Output: t1_5.b + -> Index Scan using iprt1_e_p3_ab2 on public.prt1_e_p3 t1_8 + Output: t1_8.a, t1_8.b + Filter: ((t1_8.a % 25) = 0) +(50 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.a %25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a; + a | b | c +-----+-----+------ + 0 | 0 | 0000 + 150 | 150 | 0150 + 300 | 300 | 0300 + 450 | 450 | 0450 +(4 rows) + +SELECT t1.* FROM uprt1 t1 WHERE t1.a IN (SELECT t1.b FROM uprt2 t1 WHERE t1.b IN (SELECT (t1.a + t1.b)/2 FROM uprt1_e t1 WHERE t1.a %25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a; + a | b | c +-----+-----+------ + 0 | 0 | 0000 + 150 | 150 | 0150 + 300 | 300 | 0300 + 450 | 450 | 0450 +(4 rows) + +EXPLAIN (VERBOSE, 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.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; + QUERY PLAN +---------------------------------------------------------------------------------------------- + Sort + Output: t1.a, t1.c, t2.b, t2.c, ((t3.a + t3.b)), t3.c + Sort Key: t1.a, t2.b, ((t3.a + t3.b)) + -> Result + Output: t1.a, t1.c, t2.b, t2.c, (t3.a + t3.b), t3.c + -> Append + -> Merge Left Join + Output: t3.a, t3.b, t3.c, t1.a, t1.c, t2.b, t2.c + Merge Cond: (t1.a = t2.b) + -> Sort + Output: t3.a, t3.b, t3.c, t1.a, t1.c + Sort Key: t1.a + -> Merge Left Join + Output: t3.a, t3.b, t3.c, t1.a, t1.c + Merge Cond: (((t3.a + t3.b) / 2) = t1.a) + -> Index Scan using iprt1_e_p1_ab2 on public.prt1_e_p1 t3 + Output: t3.a, t3.b, t3.c + Filter: ((t3.a % 25) = 0) + -> Index Scan using iprt1_p1_a on public.prt1_p1 t1 + Output: t1.a, t1.c + -> Index Scan using iprt2_p1_b on public.prt2_p1 t2 + Output: t2.b, t2.c + -> Merge Left Join + Output: t3_1.a, t3_1.b, t3_1.c, t1_2.a, t1_2.c, t2_1.b, t2_1.c + Merge Cond: (t1_2.a = t2_1.b) + -> Sort + Output: t3_1.a, t3_1.b, t3_1.c, t1_2.a, t1_2.c + Sort Key: t1_2.a + -> Merge Left Join + Output: t3_1.a, t3_1.b, t3_1.c, t1_2.a, t1_2.c + Merge Cond: (((t3_1.a + t3_1.b) / 2) = t1_2.a) + -> Index Scan using iprt1_e_p2_ab2 on public.prt1_e_p2 t3_1 + Output: t3_1.a, t3_1.b, t3_1.c + Filter: ((t3_1.a % 25) = 0) + -> Index Scan using iprt1_p2_a on public.prt1_p2 t1_2 + Output: t1_2.a, t1_2.c + -> Index Scan using iprt2_p2_b on public.prt2_p2 t2_1 + Output: t2_1.b, t2_1.c + -> Merge Left Join + Output: t3_2.a, t3_2.b, t3_2.c, t1_1.a, t1_1.c, t2_2.b, t2_2.c + Merge Cond: (t1_1.a = t2_2.b) + -> Sort + Output: t3_2.a, t3_2.b, t3_2.c, t1_1.a, t1_1.c + Sort Key: t1_1.a + -> Merge Left Join + Output: t3_2.a, t3_2.b, t3_2.c, t1_1.a, t1_1.c + Merge Cond: (((t3_2.a + t3_2.b) / 2) = t1_1.a) + -> Index Scan using iprt1_e_p3_ab2 on public.prt1_e_p3 t3_2 + Output: t3_2.a, t3_2.b, t3_2.c + Filter: ((t3_2.a % 25) = 0) + -> Index Scan using iprt1_p3_a on public.prt1_p3 t1_1 + Output: t1_1.a, t1_1.c + -> Index Scan using iprt2_p3_b on public.prt2_p3 t2_2 + Output: t2_2.b, t2_2.c +(54 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.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; + a | c | b | c | ?column? | c +-----+------+-----+------+----------+------ + 0 | 0000 | 0 | 0000 | 0 | 0000 + 50 | 0050 | | | 100 | 0050 + 100 | 0100 | | | 200 | 0100 + 150 | 0150 | 150 | 0150 | 300 | 0150 + 200 | 0200 | | | 400 | 0200 + 250 | 0250 | | | 500 | 0250 + 300 | 0300 | 300 | 0300 | 600 | 0300 + 350 | 0350 | | | 700 | 0350 + 400 | 0400 | | | 800 | 0400 + 450 | 0450 | 450 | 0450 | 900 | 0450 + 500 | 0500 | | | 1000 | 0500 + 550 | 0550 | | | 1100 | 0550 +(12 rows) + +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uprt1 t1 LEFT JOIN uprt2 t2 ON t1.a = t2.b) RIGHT JOIN uprt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; + a | c | b | c | ?column? | c +-----+------+-----+------+----------+------ + 0 | 0000 | 0 | 0000 | 0 | 0000 + 50 | 0050 | | | 100 | 0050 + 100 | 0100 | | | 200 | 0100 + 150 | 0150 | 150 | 0150 | 300 | 0150 + 200 | 0200 | | | 400 | 0200 + 250 | 0250 | | | 500 | 0250 + 300 | 0300 | 300 | 0300 | 600 | 0300 + 350 | 0350 | | | 700 | 0350 + 400 | 0400 | | | 800 | 0400 + 450 | 0450 | 450 | 0450 | 900 | 0450 + 500 | 0500 | | | 1000 | 0500 + 550 | 0550 | | | 1100 | 0550 +(12 rows) + +-- lateral references and parameterized paths +EXPLAIN (VERBOSE, COSTS OFF) +SELECT * FROM prt1 t1 LEFT JOIN LATERAL + (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM prt1 t2 JOIN prt2 t3 ON (t2.a = t3.b)) ss + ON t1.a = ss.t2a WHERE t1.a % 25 = 0 ORDER BY t1.a; + QUERY PLAN +--------------------------------------------------------------------------- + Nested Loop Left Join + Output: t1.a, t1.b, t1.c, t2.a, t3.a, (LEAST(t1.a, t2.a, t3.a)), t1.a + -> Merge Append + Sort Key: t1.a + -> Index Scan using iprt1_a on public.prt1 t1 + Output: t1.a, t1.b, t1.c + Filter: ((t1.a % 25) = 0) + -> Index Scan using iprt1_p1_a on public.prt1_p1 t1_1 + Output: t1_1.a, t1_1.b, t1_1.c + Filter: ((t1_1.a % 25) = 0) + -> Index Scan using iprt1_p3_a on public.prt1_p3 t1_2 + Output: t1_2.a, t1_2.b, t1_2.c + Filter: ((t1_2.a % 25) = 0) + -> Index Scan using iprt1_p2_a on public.prt1_p2 t1_3 + Output: t1_3.a, t1_3.b, t1_3.c + Filter: ((t1_3.a % 25) = 0) + -> Append + -> Merge Join + Output: t2.a, t3.a, LEAST(t1.a, t2.a, t3.a) + Merge Cond: (t2.a = t3.b) + -> Index Only Scan using iprt1_p1_a on public.prt1_p1 t2 + Output: t2.a + Index Cond: (t2.a = t1.a) + -> Index Scan using iprt2_p1_b on public.prt2_p1 t3 + Output: t3.a, t3.b + -> Merge Join + Output: t2_2.a, t3_1.a, LEAST(t1.a, t2_2.a, t3_1.a) + Merge Cond: (t2_2.a = t3_1.b) + -> Index Only Scan using iprt1_p2_a on public.prt1_p2 t2_2 + Output: t2_2.a + Index Cond: (t2_2.a = t1.a) + -> Index Scan using iprt2_p2_b on public.prt2_p2 t3_1 + Output: t3_1.a, t3_1.b + -> Merge Join + Output: t2_1.a, t3_2.a, LEAST(t1.a, t2_1.a, t3_2.a) + Merge Cond: (t2_1.a = t3_2.b) + -> Index Only Scan using iprt1_p3_a on public.prt1_p3 t2_1 + Output: t2_1.a + Index Cond: (t2_1.a = t1.a) + -> Index Scan using iprt2_p3_b on public.prt2_p3 t3_2 + Output: t3_2.a, t3_2.b +(41 rows) + +SELECT * FROM prt1 t1 LEFT JOIN LATERAL + (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM prt1 t2 JOIN prt2 t3 ON (t2.a = t3.b)) ss + ON t1.a = ss.t2a WHERE t1.a % 25 = 0 ORDER BY t1.a; + a | b | c | t2a | t3a | least +-----+-----+------+-----+-----+------- + 0 | 0 | 0000 | 0 | 0 | 0 + 50 | 50 | 0050 | | | + 100 | 100 | 0100 | | | + 150 | 150 | 0150 | 150 | 150 | 150 + 200 | 200 | 0200 | | | + 250 | 250 | 0250 | | | + 300 | 300 | 0300 | 300 | 300 | 300 + 350 | 350 | 0350 | | | + 400 | 400 | 0400 | | | + 450 | 450 | 0450 | 450 | 450 | 450 + 500 | 500 | 0500 | | | + 550 | 550 | 0550 | | | +(12 rows) + +SELECT * FROM uprt1 t1 LEFT JOIN LATERAL + (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM uprt1 t2 JOIN uprt2 t3 ON (t2.a = t3.b)) ss + ON t1.a = ss.t2a WHERE t1.a % 25 = 0 ORDER BY t1.a; + a | b | c | t2a | t3a | least +-----+-----+------+-----+-----+------- + 0 | 0 | 0000 | 0 | 0 | 0 + 50 | 50 | 0050 | | | + 100 | 100 | 0100 | | | + 150 | 150 | 0150 | 150 | 150 | 150 + 200 | 200 | 0200 | | | + 250 | 250 | 0250 | | | + 300 | 300 | 0300 | 300 | 300 | 300 + 350 | 350 | 0350 | | | + 400 | 400 | 0400 | | | + 450 | 450 | 0450 | 450 | 450 | 450 + 500 | 500 | 0500 | | | + 550 | 550 | 0550 | | | +(12 rows) + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT * FROM prt1 t1 LEFT JOIN LATERAL + (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM prt1 t2 JOIN prt2 t3 ON (t2.a = t3.b)) ss + ON t1.b = ss.t2a WHERE t1.a % 25 = 0 ORDER BY t1.a; + QUERY PLAN +--------------------------------------------------------------------------- + Nested Loop Left Join + Output: t1.a, t1.b, t1.c, t2.a, t3.a, (LEAST(t1.a, t2.a, t3.a)), t1.a + -> Merge Append + Sort Key: t1.a + -> Index Scan using iprt1_a on public.prt1 t1 + Output: t1.a, t1.b, t1.c + Filter: ((t1.a % 25) = 0) + -> Index Scan using iprt1_p1_a on public.prt1_p1 t1_1 + Output: t1_1.a, t1_1.b, t1_1.c + Filter: ((t1_1.a % 25) = 0) + -> Index Scan using iprt1_p3_a on public.prt1_p3 t1_2 + Output: t1_2.a, t1_2.b, t1_2.c + Filter: ((t1_2.a % 25) = 0) + -> Index Scan using iprt1_p2_a on public.prt1_p2 t1_3 + Output: t1_3.a, t1_3.b, t1_3.c + Filter: ((t1_3.a % 25) = 0) + -> Append + -> Merge Join + Output: t2.a, t3.a, LEAST(t1.a, t2.a, t3.a) + Merge Cond: (t2.a = t3.b) + -> Index Only Scan using iprt1_p1_a on public.prt1_p1 t2 + Output: t2.a + Index Cond: (t2.a = t1.b) + -> Index Scan using iprt2_p1_b on public.prt2_p1 t3 + Output: t3.a, t3.b + -> Merge Join + Output: t2_2.a, t3_1.a, LEAST(t1.a, t2_2.a, t3_1.a) + Merge Cond: (t2_2.a = t3_1.b) + -> Index Only Scan using iprt1_p2_a on public.prt1_p2 t2_2 + Output: t2_2.a + Index Cond: (t2_2.a = t1.b) + -> Index Scan using iprt2_p2_b on public.prt2_p2 t3_1 + Output: t3_1.a, t3_1.b + -> Merge Join + Output: t2_1.a, t3_2.a, LEAST(t1.a, t2_1.a, t3_2.a) + Merge Cond: (t2_1.a = t3_2.b) + -> Index Only Scan using iprt1_p3_a on public.prt1_p3 t2_1 + Output: t2_1.a + Index Cond: (t2_1.a = t1.b) + -> Index Scan using iprt2_p3_b on public.prt2_p3 t3_2 + Output: t3_2.a, t3_2.b +(41 rows) + +SELECT * FROM prt1 t1 LEFT JOIN LATERAL + (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM prt1 t2 JOIN prt2 t3 ON (t2.a = t3.b)) ss + ON t1.b = ss.t2a WHERE t1.a % 25 = 0 ORDER BY t1.a; + a | b | c | t2a | t3a | least +-----+-----+------+-----+-----+------- + 0 | 0 | 0000 | 0 | 0 | 0 + 50 | 50 | 0050 | | | + 100 | 100 | 0100 | | | + 150 | 150 | 0150 | 150 | 150 | 150 + 200 | 200 | 0200 | | | + 250 | 250 | 0250 | | | + 300 | 300 | 0300 | 300 | 300 | 300 + 350 | 350 | 0350 | | | + 400 | 400 | 0400 | | | + 450 | 450 | 0450 | 450 | 450 | 450 + 500 | 500 | 0500 | | | + 550 | 550 | 0550 | | | +(12 rows) + +SELECT * FROM uprt1 t1 LEFT JOIN LATERAL + (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM uprt1 t2 JOIN uprt2 t3 ON (t2.a = t3.b)) ss + ON t1.b = ss.t2a WHERE t1.a % 25 = 0 ORDER BY t1.a; + a | b | c | t2a | t3a | least +-----+-----+------+-----+-----+------- + 0 | 0 | 0000 | 0 | 0 | 0 + 50 | 50 | 0050 | | | + 100 | 100 | 0100 | | | + 150 | 150 | 0150 | 150 | 150 | 150 + 200 | 200 | 0200 | | | + 250 | 250 | 0250 | | | + 300 | 300 | 0300 | 300 | 300 | 300 + 350 | 350 | 0350 | | | + 400 | 400 | 0400 | | | + 450 | 450 | 0450 | 450 | 450 | 450 + 500 | 500 | 0500 | | | + 550 | 550 | 0550 | | | +(12 rows) + +RESET enable_hashjoin; +RESET enable_nestloop; +RESET enable_seqscan; +-- +-- partitioned by multiple columns +-- +CREATE TABLE prt1_m (a int, b int, c varchar) PARTITION BY RANGE(a, ((a + b)/2)); +CREATE TABLE prt1_m_p1 PARTITION OF prt1_m FOR VALUES START (0, 0) END (250, 250); +CREATE TABLE prt1_m_p2 PARTITION OF prt1_m FOR VALUES START (250, 250) END (500, 500); +CREATE TABLE prt1_m_p3 PARTITION OF prt1_m FOR VALUES START (500, 500) END (600, 600); +INSERT INTO prt1_m SELECT i, i, to_char(i, 'FM0000') FROM generate_series(0, 599, 2) i; +ANALYZE prt1_m; +ANALYZE prt1_m_p1; +ANALYZE prt1_m_p2; +ANALYZE prt1_m_p3; +-- TODO: This table is created only for testing the results. Remove once +-- results are tested. +CREATE TABLE uprt1_m AS SELECT * FROM prt1_m; +CREATE TABLE prt2_m (a int, b int, c varchar) PARTITION BY RANGE(((b + a)/2), b); +CREATE TABLE prt2_m_p1 PARTITION OF prt2_m FOR VALUES START (0, 0) END (250, 250); +CREATE TABLE prt2_m_p2 PARTITION OF prt2_m FOR VALUES START (250, 250) END (500, 500); +CREATE TABLE prt2_m_p3 PARTITION OF prt2_m FOR VALUES START (500, 500) END (600, 600); +INSERT INTO prt2_m SELECT i, i, to_char(i, 'FM0000') FROM generate_series(0, 599, 3) i; +ANALYZE prt2_m; +ANALYZE prt2_m_p1; +ANALYZE prt2_m_p2; +ANALYZE prt2_m_p3; +-- TODO: This table is created only for testing the results. Remove once +-- results are tested. +CREATE TABLE uprt2_m AS SELECT * FROM prt2_m; +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_m t1, prt2_m t2 WHERE t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2 AND t1.a % 25 = 0 ORDER BY t1.a, t2.b; + QUERY PLAN +------------------------------------------------------------------------------------------------------ + Sort + Output: t1.a, t1.c, t2.b, t2.c + Sort Key: t1.a, t2.b + -> Append + -> Hash Join + Output: t1.a, t1.c, t2.b, t2.c + Hash Cond: ((((t2.b + t2.a) / 2) = t1.a) AND (t2.b = ((t1.a + t1.b) / 2))) + -> Seq Scan on public.prt2_m_p1 t2 + Output: t2.b, t2.c, t2.a + -> Hash + Output: t1.a, t1.c, t1.b + -> Seq Scan on public.prt1_m_p1 t1 + Output: t1.a, t1.c, t1.b + Filter: ((t1.a % 25) = 0) + -> Hash Join + Output: t1_1.a, t1_1.c, t2_1.b, t2_1.c + Hash Cond: ((((t2_1.b + t2_1.a) / 2) = t1_1.a) AND (t2_1.b = ((t1_1.a + t1_1.b) / 2))) + -> Seq Scan on public.prt2_m_p2 t2_1 + Output: t2_1.b, t2_1.c, t2_1.a + -> Hash + Output: t1_1.a, t1_1.c, t1_1.b + -> Seq Scan on public.prt1_m_p2 t1_1 + Output: t1_1.a, t1_1.c, t1_1.b + Filter: ((t1_1.a % 25) = 0) + -> Hash Join + Output: t1_2.a, t1_2.c, t2_2.b, t2_2.c + Hash Cond: ((((t2_2.b + t2_2.a) / 2) = t1_2.a) AND (t2_2.b = ((t1_2.a + t1_2.b) / 2))) + -> Seq Scan on public.prt2_m_p3 t2_2 + Output: t2_2.b, t2_2.c, t2_2.a + -> Hash + Output: t1_2.a, t1_2.c, t1_2.b + -> Seq Scan on public.prt1_m_p3 t1_2 + Output: t1_2.a, t1_2.c, t1_2.b + Filter: ((t1_2.a % 25) = 0) +(34 rows) + +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_m t1, prt2_m t2 WHERE t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2 AND t1.a % 25 = 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) + +SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1_m t1, uprt2_m t2 WHERE t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2 AND t1.a % 25 = 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) + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_m t1 LEFT JOIN prt2_m t2 ON t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2 WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b; + QUERY PLAN +------------------------------------------------------------------------------------------------------ + Sort + Output: t1.a, t1.c, t2.b, t2.c + Sort Key: t1.a, t2.b + -> Append + -> Hash Right Join + Output: t1.a, t1.c, t2.b, t2.c + Hash Cond: ((((t2.b + t2.a) / 2) = t1.a) AND (t2.b = ((t1.a + t1.b) / 2))) + -> Seq Scan on public.prt2_m_p1 t2 + Output: t2.b, t2.c, t2.a + -> Hash + Output: t1.a, t1.c, t1.b + -> Seq Scan on public.prt1_m_p1 t1 + Output: t1.a, t1.c, t1.b + Filter: ((t1.a % 25) = 0) + -> Hash Right Join + Output: t1_1.a, t1_1.c, t2_1.b, t2_1.c + Hash Cond: ((((t2_1.b + t2_1.a) / 2) = t1_1.a) AND (t2_1.b = ((t1_1.a + t1_1.b) / 2))) + -> Seq Scan on public.prt2_m_p2 t2_1 + Output: t2_1.b, t2_1.c, t2_1.a + -> Hash + Output: t1_1.a, t1_1.c, t1_1.b + -> Seq Scan on public.prt1_m_p2 t1_1 + Output: t1_1.a, t1_1.c, t1_1.b + Filter: ((t1_1.a % 25) = 0) + -> Hash Right Join + Output: t1_2.a, t1_2.c, t2_2.b, t2_2.c + Hash Cond: ((((t2_2.b + t2_2.a) / 2) = t1_2.a) AND (t2_2.b = ((t1_2.a + t1_2.b) / 2))) + -> Seq Scan on public.prt2_m_p3 t2_2 + Output: t2_2.b, t2_2.c, t2_2.a + -> Hash + Output: t1_2.a, t1_2.c, t1_2.b + -> Seq Scan on public.prt1_m_p3 t1_2 + Output: t1_2.a, t1_2.c, t1_2.b + Filter: ((t1_2.a % 25) = 0) +(34 rows) + +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_m t1 LEFT JOIN prt2_m t2 ON t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2 WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b; + a | c | b | c +-----+------+-----+------ + 0 | 0000 | 0 | 0000 + 50 | 0050 | | + 100 | 0100 | | + 150 | 0150 | 150 | 0150 + 200 | 0200 | | + 250 | 0250 | | + 300 | 0300 | 300 | 0300 + 350 | 0350 | | + 400 | 0400 | | + 450 | 0450 | 450 | 0450 + 500 | 0500 | | + 550 | 0550 | | +(12 rows) + +SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1_m t1 LEFT JOIN uprt2_m t2 ON t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2 WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b; + a | c | b | c +-----+------+-----+------ + 0 | 0000 | 0 | 0000 + 50 | 0050 | | + 100 | 0100 | | + 150 | 0150 | 150 | 0150 + 200 | 0200 | | + 250 | 0250 | | + 300 | 0300 | 300 | 0300 + 350 | 0350 | | + 400 | 0400 | | + 450 | 0450 | 450 | 0450 + 500 | 0500 | | + 550 | 0550 | | +(12 rows) + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_m t1 RIGHT JOIN prt2_m t2 ON t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2 WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b; + QUERY PLAN +------------------------------------------------------------------------------------------------------------ + Sort + Output: t1.a, t1.c, t2.b, t2.c + Sort Key: t1.a, t2.b + -> Result + Output: t1.a, t1.c, t2.b, t2.c + -> Append + -> Hash Right Join + Output: t2.b, t2.c, t1.a, t1.c + Hash Cond: ((((t1.a + t1.b) / 2) = t2.b) AND (t1.a = ((t2.b + t2.a) / 2))) + -> Seq Scan on public.prt1_m_p1 t1 + Output: t1.a, t1.c, t1.b + -> Hash + Output: t2.b, t2.c, t2.a + -> Seq Scan on public.prt2_m_p1 t2 + Output: t2.b, t2.c, t2.a + Filter: ((t2.b % 25) = 0) + -> Hash Right Join + Output: t2_1.b, t2_1.c, t1_1.a, t1_1.c + Hash Cond: ((((t1_1.a + t1_1.b) / 2) = t2_1.b) AND (t1_1.a = ((t2_1.b + t2_1.a) / 2))) + -> Seq Scan on public.prt1_m_p2 t1_1 + Output: t1_1.a, t1_1.c, t1_1.b + -> Hash + Output: t2_1.b, t2_1.c, t2_1.a + -> Seq Scan on public.prt2_m_p2 t2_1 + Output: t2_1.b, t2_1.c, t2_1.a + Filter: ((t2_1.b % 25) = 0) + -> Hash Right Join + Output: t2_2.b, t2_2.c, t1_2.a, t1_2.c + Hash Cond: ((((t1_2.a + t1_2.b) / 2) = t2_2.b) AND (t1_2.a = ((t2_2.b + t2_2.a) / 2))) + -> Seq Scan on public.prt1_m_p3 t1_2 + Output: t1_2.a, t1_2.c, t1_2.b + -> Hash + Output: t2_2.b, t2_2.c, t2_2.a + -> Seq Scan on public.prt2_m_p3 t2_2 + Output: t2_2.b, t2_2.c, t2_2.a + Filter: ((t2_2.b % 25) = 0) +(36 rows) + +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_m t1 RIGHT JOIN prt2_m t2 ON t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2 WHERE t2.b % 25 = 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) + +SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1_m t1 RIGHT JOIN uprt2_m t2 ON t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2 WHERE t2.b % 25 = 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) + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_m WHERE prt1_m.a % 25 = 0) t1 FULL JOIN (SELECT * FROM prt2_m WHERE prt2_m.b % 25 = 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 + Output: prt1_m_p1.a, prt1_m_p1.c, prt2_m_p1.b, prt2_m_p1.c + Sort Key: prt1_m_p1.a, prt2_m_p1.b + -> Append + -> Hash Full Join + Output: prt1_m_p1.a, prt1_m_p1.c, prt2_m_p1.b, prt2_m_p1.c + 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 public.prt1_m_p1 + Output: prt1_m_p1.a, prt1_m_p1.c, prt1_m_p1.b + Filter: ((prt1_m_p1.a % 25) = 0) + -> Hash + Output: prt2_m_p1.b, prt2_m_p1.c, prt2_m_p1.a + -> Seq Scan on public.prt2_m_p1 + Output: prt2_m_p1.b, prt2_m_p1.c, prt2_m_p1.a + Filter: ((prt2_m_p1.b % 25) = 0) + -> Hash Full Join + Output: prt1_m_p2.a, prt1_m_p2.c, prt2_m_p2.b, prt2_m_p2.c + 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 public.prt1_m_p2 + Output: prt1_m_p2.a, prt1_m_p2.c, prt1_m_p2.b + Filter: ((prt1_m_p2.a % 25) = 0) + -> Hash + Output: prt2_m_p2.b, prt2_m_p2.c, prt2_m_p2.a + -> Seq Scan on public.prt2_m_p2 + Output: prt2_m_p2.b, prt2_m_p2.c, prt2_m_p2.a + Filter: ((prt2_m_p2.b % 25) = 0) + -> Hash Full Join + Output: prt1_m_p3.a, prt1_m_p3.c, prt2_m_p3.b, prt2_m_p3.c + 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 public.prt1_m_p3 + Output: prt1_m_p3.a, prt1_m_p3.c, prt1_m_p3.b + Filter: ((prt1_m_p3.a % 25) = 0) + -> Hash + Output: prt2_m_p3.b, prt2_m_p3.c, prt2_m_p3.a + -> Seq Scan on public.prt2_m_p3 + Output: prt2_m_p3.b, prt2_m_p3.c, prt2_m_p3.a + Filter: ((prt2_m_p3.b % 25) = 0) +(37 rows) + +SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_m WHERE prt1_m.a % 25 = 0) t1 FULL JOIN (SELECT * FROM prt2_m WHERE prt2_m.b % 25 = 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 | 0000 | 0 | 0000 + 50 | 0050 | | + 100 | 0100 | | + 150 | 0150 | 150 | 0150 + 200 | 0200 | | + 250 | 0250 | | + 300 | 0300 | 300 | 0300 + 350 | 0350 | | + 400 | 0400 | | + 450 | 0450 | 450 | 0450 + 500 | 0500 | | + 550 | 0550 | | + | | 75 | 0075 + | | 225 | 0225 + | | 375 | 0375 + | | 525 | 0525 +(16 rows) + +SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM uprt1_m t1 WHERE t1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM uprt2_m t2 WHERE t2.b % 25 = 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 | 0000 | 0 | 0000 + 50 | 0050 | | + 100 | 0100 | | + 150 | 0150 | 150 | 0150 + 200 | 0200 | | + 250 | 0250 | | + 300 | 0300 | 300 | 0300 + 350 | 0350 | | + 400 | 0400 | | + 450 | 0450 | 450 | 0450 + 500 | 0500 | | + 550 | 0550 | | + | | 75 | 0075 + | | 225 | 0225 + | | 375 | 0375 + | | 525 | 0525 +(16 rows) + +-- +-- multi-leveled partitions +-- +CREATE TABLE prt1_l (a int, b int, c varchar) PARTITION BY RANGE(a); +CREATE TABLE prt1_l_p1 PARTITION OF prt1_l FOR VALUES START (0) END (250) PARTITION BY RANGE (b); +CREATE TABLE prt1_l_p1_p1 PARTITION OF prt1_l_p1 FOR VALUES START (0) END (100); +CREATE TABLE prt1_l_p1_p2 PARTITION OF prt1_l_p1 FOR VALUES START (100) END (250); +CREATE TABLE prt1_l_p2 PARTITION OF prt1_l FOR VALUES START (250) END (500) PARTITION BY RANGE (c); +CREATE TABLE prt1_l_p2_p1 PARTITION OF prt1_l_p2 FOR VALUES START ('0250') END ('0400'); +CREATE TABLE prt1_l_p2_p2 PARTITION OF prt1_l_p2 FOR VALUES START ('0400') END ('0500'); +CREATE TABLE prt1_l_p3 PARTITION OF prt1_l FOR VALUES START (500) END (600) PARTITION BY RANGE ((b + a)); +CREATE TABLE prt1_l_p3_p1 PARTITION OF prt1_l_p3 FOR VALUES START (1000) END (1100); +CREATE TABLE prt1_l_p3_p2 PARTITION OF prt1_l_p3 FOR VALUES START (1100) END (1200); +INSERT INTO prt1_l SELECT i, i, to_char(i, 'FM0000') FROM generate_series(0, 599, 2) i; +ANALYZE prt1_l; +ANALYZE prt1_l_p1; +ANALYZE prt1_l_p1_p1; +ANALYZE prt1_l_p1_p2; +ANALYZE prt1_l_p2; +ANALYZE prt1_l_p2_p1; +ANALYZE prt1_l_p2_p2; +ANALYZE prt1_l_p3; +ANALYZE prt1_l_p3_p1; +ANALYZE prt1_l_p3_p2; +-- TODO: This table is created only for testing the results. Remove once +-- results are tested. +CREATE TABLE uprt1_l AS SELECT * FROM prt1_l; +CREATE TABLE prt2_l (a int, b int, c varchar) PARTITION BY RANGE(b); +CREATE TABLE prt2_l_p1 PARTITION OF prt2_l FOR VALUES START (0) END (250) PARTITION BY RANGE (a); +CREATE TABLE prt2_l_p1_p1 PARTITION OF prt2_l_p1 FOR VALUES START (0) END (100); +CREATE TABLE prt2_l_p1_p2 PARTITION OF prt2_l_p1 FOR VALUES START (100) END (250); +CREATE TABLE prt2_l_p2 PARTITION OF prt2_l FOR VALUES START (250) END (500) PARTITION BY RANGE (c); +CREATE TABLE prt2_l_p2_p1 PARTITION OF prt2_l_p2 FOR VALUES START ('0250') END ('0400'); +CREATE TABLE prt2_l_p2_p2 PARTITION OF prt2_l_p2 FOR VALUES START ('0400') END ('0500'); +CREATE TABLE prt2_l_p3 PARTITION OF prt2_l FOR VALUES START (500) END (600) PARTITION BY RANGE ((a + b)); +CREATE TABLE prt2_l_p3_p1 PARTITION OF prt2_l_p3 FOR VALUES START (1000) END (1100); +CREATE TABLE prt2_l_p3_p2 PARTITION OF prt2_l_p3 FOR VALUES START (1100) END (1200); +INSERT INTO prt2_l SELECT i, i, to_char(i, 'FM0000') FROM generate_series(0, 599, 3) i; +ANALYZE prt2_l; +ANALYZE prt2_l_p1; +ANALYZE prt2_l_p1_p1; +ANALYZE prt2_l_p1_p2; +ANALYZE prt2_l_p2; +ANALYZE prt2_l_p2_p1; +ANALYZE prt2_l_p2_p2; +ANALYZE prt2_l_p3; +ANALYZE prt2_l_p3_p1; +ANALYZE prt2_l_p3_p2; +-- TODO: This table is created only for testing the results. Remove once +-- results are tested. +CREATE TABLE uprt2_l AS SELECT * FROM prt2_l; +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_l t1, prt2_l t2 WHERE t1.a = t2.b AND t1.b = t2.a AND t1.c = t2.c AND t1.b + t1.a = t2.a + t2.b AND t1.a % 25 = 0 ORDER BY t1.a, t2.b; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------ + Sort + Output: t1.a, t1.c, t2.b, t2.c + Sort Key: t1.a + -> Append + -> Hash Join + Output: t1.a, t1.c, t2.b, t2.c + Hash Cond: ((t2.b = t1.a) AND (t2.a = t1.b) AND ((t2.c)::text = (t1.c)::text) AND ((t2.a + t2.b) = (t1.b + t1.a))) + -> Seq Scan on public.prt2_l_p1_p1 t2 + Output: t2.b, t2.c, t2.a + -> Hash + Output: t1.a, t1.c, t1.b + -> Seq Scan on public.prt1_l_p1_p1 t1 + Output: t1.a, t1.c, t1.b + Filter: ((t1.a % 25) = 0) + -> Hash Join + Output: t1_1.a, t1_1.c, t2_1.b, t2_1.c + Hash Cond: ((t2_1.b = t1_1.a) AND (t2_1.a = t1_1.b) AND ((t2_1.c)::text = (t1_1.c)::text) AND ((t2_1.a + t2_1.b) = (t1_1.b + t1_1.a))) + -> Seq Scan on public.prt2_l_p1_p2 t2_1 + Output: t2_1.b, t2_1.c, t2_1.a + -> Hash + Output: t1_1.a, t1_1.c, t1_1.b + -> Seq Scan on public.prt1_l_p1_p2 t1_1 + Output: t1_1.a, t1_1.c, t1_1.b + Filter: ((t1_1.a % 25) = 0) + -> Hash Join + Output: t1_2.a, t1_2.c, t2_2.b, t2_2.c + Hash Cond: ((t2_2.b = t1_2.a) AND (t2_2.a = t1_2.b) AND ((t2_2.c)::text = (t1_2.c)::text) AND ((t2_2.a + t2_2.b) = (t1_2.b + t1_2.a))) + -> Seq Scan on public.prt2_l_p2_p1 t2_2 + Output: t2_2.b, t2_2.c, t2_2.a + -> Hash + Output: t1_2.a, t1_2.c, t1_2.b + -> Seq Scan on public.prt1_l_p2_p1 t1_2 + Output: t1_2.a, t1_2.c, t1_2.b + Filter: ((t1_2.a % 25) = 0) + -> Hash Join + Output: t1_3.a, t1_3.c, t2_3.b, t2_3.c + Hash Cond: ((t2_3.b = t1_3.a) AND (t2_3.a = t1_3.b) AND ((t2_3.c)::text = (t1_3.c)::text) AND ((t2_3.a + t2_3.b) = (t1_3.b + t1_3.a))) + -> Seq Scan on public.prt2_l_p2_p2 t2_3 + Output: t2_3.b, t2_3.c, t2_3.a + -> Hash + Output: t1_3.a, t1_3.c, t1_3.b + -> Seq Scan on public.prt1_l_p2_p2 t1_3 + Output: t1_3.a, t1_3.c, t1_3.b + Filter: ((t1_3.a % 25) = 0) + -> Hash Join + Output: t1_4.a, t1_4.c, t2_4.b, t2_4.c + Hash Cond: ((t2_4.b = t1_4.a) AND (t2_4.a = t1_4.b) AND ((t2_4.c)::text = (t1_4.c)::text) AND ((t2_4.a + t2_4.b) = (t1_4.b + t1_4.a))) + -> Seq Scan on public.prt2_l_p3_p1 t2_4 + Output: t2_4.b, t2_4.c, t2_4.a + -> Hash + Output: t1_4.a, t1_4.c, t1_4.b + -> Seq Scan on public.prt1_l_p3_p1 t1_4 + Output: t1_4.a, t1_4.c, t1_4.b + Filter: ((t1_4.a % 25) = 0) + -> Hash Join + Output: t1_5.a, t1_5.c, t2_5.b, t2_5.c + Hash Cond: ((t2_5.b = t1_5.a) AND (t2_5.a = t1_5.b) AND ((t2_5.c)::text = (t1_5.c)::text) AND ((t2_5.a + t2_5.b) = (t1_5.b + t1_5.a))) + -> Seq Scan on public.prt2_l_p3_p2 t2_5 + Output: t2_5.b, t2_5.c, t2_5.a + -> Hash + Output: t1_5.a, t1_5.c, t1_5.b + -> Seq Scan on public.prt1_l_p3_p2 t1_5 + Output: t1_5.a, t1_5.c, t1_5.b + Filter: ((t1_5.a % 25) = 0) +(64 rows) + +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_l t1, prt2_l t2 WHERE t1.a = t2.b AND t1.b = t2.a AND t1.c = t2.c AND t1.b + t1.a = t2.a + t2.b AND t1.a % 25 = 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) + +SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1_l t1, uprt2_l t2 WHERE t1.a = t2.b AND t1.b = t2.a AND t1.c = t2.c AND t1.b + t1.a = t2.a + t2.b AND t1.a % 25 = 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) + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_l t1 LEFT JOIN prt2_l t2 ON t1.a = t2.b AND t1.b = t2.a AND t1.c = t2.c AND t1.b + t1.a = t2.a + t2.b WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------ + Sort + Output: t1.a, t1.c, t2.b, t2.c + Sort Key: t1.a, t2.b + -> Append + -> Hash Right Join + Output: t1.a, t1.c, t2.b, t2.c + Hash Cond: ((t2.b = t1.a) AND (t2.a = t1.b) AND ((t2.c)::text = (t1.c)::text) AND ((t2.a + t2.b) = (t1.b + t1.a))) + -> Seq Scan on public.prt2_l_p1_p1 t2 + Output: t2.b, t2.c, t2.a + -> Hash + Output: t1.a, t1.c, t1.b + -> Seq Scan on public.prt1_l_p1_p1 t1 + Output: t1.a, t1.c, t1.b + Filter: ((t1.a % 25) = 0) + -> Hash Right Join + Output: t1_1.a, t1_1.c, t2_1.b, t2_1.c + Hash Cond: ((t2_1.b = t1_1.a) AND (t2_1.a = t1_1.b) AND ((t2_1.c)::text = (t1_1.c)::text) AND ((t2_1.a + t2_1.b) = (t1_1.b + t1_1.a))) + -> Seq Scan on public.prt2_l_p1_p2 t2_1 + Output: t2_1.b, t2_1.c, t2_1.a + -> Hash + Output: t1_1.a, t1_1.c, t1_1.b + -> Seq Scan on public.prt1_l_p1_p2 t1_1 + Output: t1_1.a, t1_1.c, t1_1.b + Filter: ((t1_1.a % 25) = 0) + -> Hash Right Join + Output: t1_2.a, t1_2.c, t2_2.b, t2_2.c + Hash Cond: ((t2_2.b = t1_2.a) AND (t2_2.a = t1_2.b) AND ((t2_2.c)::text = (t1_2.c)::text) AND ((t2_2.a + t2_2.b) = (t1_2.b + t1_2.a))) + -> Seq Scan on public.prt2_l_p2_p1 t2_2 + Output: t2_2.b, t2_2.c, t2_2.a + -> Hash + Output: t1_2.a, t1_2.c, t1_2.b + -> Seq Scan on public.prt1_l_p2_p1 t1_2 + Output: t1_2.a, t1_2.c, t1_2.b + Filter: ((t1_2.a % 25) = 0) + -> Hash Right Join + Output: t1_3.a, t1_3.c, t2_3.b, t2_3.c + Hash Cond: ((t2_3.b = t1_3.a) AND (t2_3.a = t1_3.b) AND ((t2_3.c)::text = (t1_3.c)::text) AND ((t2_3.a + t2_3.b) = (t1_3.b + t1_3.a))) + -> Seq Scan on public.prt2_l_p2_p2 t2_3 + Output: t2_3.b, t2_3.c, t2_3.a + -> Hash + Output: t1_3.a, t1_3.c, t1_3.b + -> Seq Scan on public.prt1_l_p2_p2 t1_3 + Output: t1_3.a, t1_3.c, t1_3.b + Filter: ((t1_3.a % 25) = 0) + -> Hash Right Join + Output: t1_4.a, t1_4.c, t2_4.b, t2_4.c + Hash Cond: ((t2_4.b = t1_4.a) AND (t2_4.a = t1_4.b) AND ((t2_4.c)::text = (t1_4.c)::text) AND ((t2_4.a + t2_4.b) = (t1_4.b + t1_4.a))) + -> Seq Scan on public.prt2_l_p3_p1 t2_4 + Output: t2_4.b, t2_4.c, t2_4.a + -> Hash + Output: t1_4.a, t1_4.c, t1_4.b + -> Seq Scan on public.prt1_l_p3_p1 t1_4 + Output: t1_4.a, t1_4.c, t1_4.b + Filter: ((t1_4.a % 25) = 0) + -> Hash Right Join + Output: t1_5.a, t1_5.c, t2_5.b, t2_5.c + Hash Cond: ((t2_5.b = t1_5.a) AND (t2_5.a = t1_5.b) AND ((t2_5.c)::text = (t1_5.c)::text) AND ((t2_5.a + t2_5.b) = (t1_5.b + t1_5.a))) + -> Seq Scan on public.prt2_l_p3_p2 t2_5 + Output: t2_5.b, t2_5.c, t2_5.a + -> Hash + Output: t1_5.a, t1_5.c, t1_5.b + -> Seq Scan on public.prt1_l_p3_p2 t1_5 + Output: t1_5.a, t1_5.c, t1_5.b + Filter: ((t1_5.a % 25) = 0) +(64 rows) + +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_l t1 LEFT JOIN prt2_l t2 ON t1.a = t2.b AND t1.b = t2.a AND t1.c = t2.c AND t1.b + t1.a = t2.a + t2.b WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b; + a | c | b | c +-----+------+-----+------ + 0 | 0000 | 0 | 0000 + 50 | 0050 | | + 100 | 0100 | | + 150 | 0150 | 150 | 0150 + 200 | 0200 | | + 250 | 0250 | | + 300 | 0300 | 300 | 0300 + 350 | 0350 | | + 400 | 0400 | | + 450 | 0450 | 450 | 0450 + 500 | 0500 | | + 550 | 0550 | | +(12 rows) + +SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1_l t1 LEFT JOIN uprt2_l t2 ON t1.a = t2.b AND t1.b = t2.a AND t1.c = t2.c AND t1.b + t1.a = t2.a + t2.b WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b; + a | c | b | c +-----+------+-----+------ + 0 | 0000 | 0 | 0000 + 50 | 0050 | | + 100 | 0100 | | + 150 | 0150 | 150 | 0150 + 200 | 0200 | | + 250 | 0250 | | + 300 | 0300 | 300 | 0300 + 350 | 0350 | | + 400 | 0400 | | + 450 | 0450 | 450 | 0450 + 500 | 0500 | | + 550 | 0550 | | +(12 rows) + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_l t1 RIGHT JOIN prt2_l t2 ON t1.a = t2.b AND t1.b = t2.a AND t1.c = t2.c AND t1.b + t1.a = t2.a + t2.b WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------------ + Sort + Output: t1.a, t1.c, t2.b, t2.c + Sort Key: t1.a, t2.b + -> Result + Output: t1.a, t1.c, t2.b, t2.c + -> Append + -> Hash Right Join + Output: t2.b, t2.c, t1.a, t1.c + Hash Cond: ((t1.a = t2.b) AND (t1.b = t2.a) AND ((t1.c)::text = (t2.c)::text) AND ((t1.b + t1.a) = (t2.a + t2.b))) + -> Seq Scan on public.prt1_l_p1_p1 t1 + Output: t1.a, t1.c, t1.b + -> Hash + Output: t2.b, t2.c, t2.a + -> Seq Scan on public.prt2_l_p1_p1 t2 + Output: t2.b, t2.c, t2.a + Filter: ((t2.b % 25) = 0) + -> Hash Right Join + Output: t2_1.b, t2_1.c, t1_1.a, t1_1.c + Hash Cond: ((t1_1.a = t2_1.b) AND (t1_1.b = t2_1.a) AND ((t1_1.c)::text = (t2_1.c)::text) AND ((t1_1.b + t1_1.a) = (t2_1.a + t2_1.b))) + -> Seq Scan on public.prt1_l_p1_p2 t1_1 + Output: t1_1.a, t1_1.c, t1_1.b + -> Hash + Output: t2_1.b, t2_1.c, t2_1.a + -> Seq Scan on public.prt2_l_p1_p2 t2_1 + Output: t2_1.b, t2_1.c, t2_1.a + Filter: ((t2_1.b % 25) = 0) + -> Hash Right Join + Output: t2_2.b, t2_2.c, t1_2.a, t1_2.c + Hash Cond: ((t1_2.a = t2_2.b) AND (t1_2.b = t2_2.a) AND ((t1_2.c)::text = (t2_2.c)::text) AND ((t1_2.b + t1_2.a) = (t2_2.a + t2_2.b))) + -> Seq Scan on public.prt1_l_p2_p1 t1_2 + Output: t1_2.a, t1_2.c, t1_2.b + -> Hash + Output: t2_2.b, t2_2.c, t2_2.a + -> Seq Scan on public.prt2_l_p2_p1 t2_2 + Output: t2_2.b, t2_2.c, t2_2.a + Filter: ((t2_2.b % 25) = 0) + -> Hash Right Join + Output: t2_3.b, t2_3.c, t1_3.a, t1_3.c + Hash Cond: ((t1_3.a = t2_3.b) AND (t1_3.b = t2_3.a) AND ((t1_3.c)::text = (t2_3.c)::text) AND ((t1_3.b + t1_3.a) = (t2_3.a + t2_3.b))) + -> Seq Scan on public.prt1_l_p2_p2 t1_3 + Output: t1_3.a, t1_3.c, t1_3.b + -> Hash + Output: t2_3.b, t2_3.c, t2_3.a + -> Seq Scan on public.prt2_l_p2_p2 t2_3 + Output: t2_3.b, t2_3.c, t2_3.a + Filter: ((t2_3.b % 25) = 0) + -> Hash Right Join + Output: t2_4.b, t2_4.c, t1_4.a, t1_4.c + Hash Cond: ((t1_4.a = t2_4.b) AND (t1_4.b = t2_4.a) AND ((t1_4.c)::text = (t2_4.c)::text) AND ((t1_4.b + t1_4.a) = (t2_4.a + t2_4.b))) + -> Seq Scan on public.prt1_l_p3_p1 t1_4 + Output: t1_4.a, t1_4.c, t1_4.b + -> Hash + Output: t2_4.b, t2_4.c, t2_4.a + -> Seq Scan on public.prt2_l_p3_p1 t2_4 + Output: t2_4.b, t2_4.c, t2_4.a + Filter: ((t2_4.b % 25) = 0) + -> Hash Right Join + Output: t2_5.b, t2_5.c, t1_5.a, t1_5.c + Hash Cond: ((t1_5.a = t2_5.b) AND (t1_5.b = t2_5.a) AND ((t1_5.c)::text = (t2_5.c)::text) AND ((t1_5.b + t1_5.a) = (t2_5.a + t2_5.b))) + -> Seq Scan on public.prt1_l_p3_p2 t1_5 + Output: t1_5.a, t1_5.c, t1_5.b + -> Hash + Output: t2_5.b, t2_5.c, t2_5.a + -> Seq Scan on public.prt2_l_p3_p2 t2_5 + Output: t2_5.b, t2_5.c, t2_5.a + Filter: ((t2_5.b % 25) = 0) +(66 rows) + +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_l t1 RIGHT JOIN prt2_l t2 ON t1.a = t2.b AND t1.b = t2.a AND t1.c = t2.c AND t1.b + t1.a = t2.a + t2.b WHERE t2.b % 25 = 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) + +SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1_l t1 RIGHT JOIN uprt2_l t2 ON t1.a = t2.b AND t1.b = t2.a AND t1.c = t2.c AND t1.b + t1.a = t2.a + t2.b WHERE t2.b % 25 = 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) + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_l WHERE prt1_l.a % 25 = 0) t1 FULL JOIN (SELECT * FROM prt2_l WHERE prt2_l.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.b = t2.a AND t1.c = t2.c AND t1.b + t1.a = t2.a + t2.b) ORDER BY t1.a, t2.b; + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Sort + Output: prt1_l_p1_p1.a, prt1_l_p1_p1.c, prt2_l_p1_p1.b, prt2_l_p1_p1.c + Sort Key: prt1_l_p1_p1.a, prt2_l_p1_p1.b + -> Append + -> Hash Full Join + Output: prt1_l_p1_p1.a, prt1_l_p1_p1.c, prt2_l_p1_p1.b, prt2_l_p1_p1.c + Hash Cond: ((prt1_l_p1_p1.a = prt2_l_p1_p1.b) AND (prt1_l_p1_p1.b = prt2_l_p1_p1.a) AND ((prt1_l_p1_p1.c)::text = (prt2_l_p1_p1.c)::text) AND ((prt1_l_p1_p1.b + prt1_l_p1_p1.a) = (prt2_l_p1_p1.a + prt2_l_p1_p1.b))) + -> Seq Scan on public.prt1_l_p1_p1 + Output: prt1_l_p1_p1.a, prt1_l_p1_p1.c, prt1_l_p1_p1.b + Filter: ((prt1_l_p1_p1.a % 25) = 0) + -> Hash + Output: prt2_l_p1_p1.b, prt2_l_p1_p1.c, prt2_l_p1_p1.a + -> Seq Scan on public.prt2_l_p1_p1 + Output: prt2_l_p1_p1.b, prt2_l_p1_p1.c, prt2_l_p1_p1.a + Filter: ((prt2_l_p1_p1.b % 25) = 0) + -> Hash Full Join + Output: prt1_l_p1_p2.a, prt1_l_p1_p2.c, prt2_l_p1_p2.b, prt2_l_p1_p2.c + Hash Cond: ((prt1_l_p1_p2.a = prt2_l_p1_p2.b) AND (prt1_l_p1_p2.b = prt2_l_p1_p2.a) AND ((prt1_l_p1_p2.c)::text = (prt2_l_p1_p2.c)::text) AND ((prt1_l_p1_p2.b + prt1_l_p1_p2.a) = (prt2_l_p1_p2.a + prt2_l_p1_p2.b))) + -> Seq Scan on public.prt1_l_p1_p2 + Output: prt1_l_p1_p2.a, prt1_l_p1_p2.c, prt1_l_p1_p2.b + Filter: ((prt1_l_p1_p2.a % 25) = 0) + -> Hash + Output: prt2_l_p1_p2.b, prt2_l_p1_p2.c, prt2_l_p1_p2.a + -> Seq Scan on public.prt2_l_p1_p2 + Output: prt2_l_p1_p2.b, prt2_l_p1_p2.c, prt2_l_p1_p2.a + Filter: ((prt2_l_p1_p2.b % 25) = 0) + -> Hash Full Join + Output: prt1_l_p2_p1.a, prt1_l_p2_p1.c, prt2_l_p2_p1.b, prt2_l_p2_p1.c + Hash Cond: ((prt1_l_p2_p1.a = prt2_l_p2_p1.b) AND (prt1_l_p2_p1.b = prt2_l_p2_p1.a) AND ((prt1_l_p2_p1.c)::text = (prt2_l_p2_p1.c)::text) AND ((prt1_l_p2_p1.b + prt1_l_p2_p1.a) = (prt2_l_p2_p1.a + prt2_l_p2_p1.b))) + -> Seq Scan on public.prt1_l_p2_p1 + Output: prt1_l_p2_p1.a, prt1_l_p2_p1.c, prt1_l_p2_p1.b + Filter: ((prt1_l_p2_p1.a % 25) = 0) + -> Hash + Output: prt2_l_p2_p1.b, prt2_l_p2_p1.c, prt2_l_p2_p1.a + -> Seq Scan on public.prt2_l_p2_p1 + Output: prt2_l_p2_p1.b, prt2_l_p2_p1.c, prt2_l_p2_p1.a + Filter: ((prt2_l_p2_p1.b % 25) = 0) + -> Hash Full Join + Output: prt1_l_p2_p2.a, prt1_l_p2_p2.c, prt2_l_p2_p2.b, prt2_l_p2_p2.c + Hash Cond: ((prt1_l_p2_p2.a = prt2_l_p2_p2.b) AND (prt1_l_p2_p2.b = prt2_l_p2_p2.a) AND ((prt1_l_p2_p2.c)::text = (prt2_l_p2_p2.c)::text) AND ((prt1_l_p2_p2.b + prt1_l_p2_p2.a) = (prt2_l_p2_p2.a + prt2_l_p2_p2.b))) + -> Seq Scan on public.prt1_l_p2_p2 + Output: prt1_l_p2_p2.a, prt1_l_p2_p2.c, prt1_l_p2_p2.b + Filter: ((prt1_l_p2_p2.a % 25) = 0) + -> Hash + Output: prt2_l_p2_p2.b, prt2_l_p2_p2.c, prt2_l_p2_p2.a + -> Seq Scan on public.prt2_l_p2_p2 + Output: prt2_l_p2_p2.b, prt2_l_p2_p2.c, prt2_l_p2_p2.a + Filter: ((prt2_l_p2_p2.b % 25) = 0) + -> Hash Full Join + Output: prt1_l_p3_p1.a, prt1_l_p3_p1.c, prt2_l_p3_p1.b, prt2_l_p3_p1.c + Hash Cond: ((prt1_l_p3_p1.a = prt2_l_p3_p1.b) AND (prt1_l_p3_p1.b = prt2_l_p3_p1.a) AND ((prt1_l_p3_p1.c)::text = (prt2_l_p3_p1.c)::text) AND ((prt1_l_p3_p1.b + prt1_l_p3_p1.a) = (prt2_l_p3_p1.a + prt2_l_p3_p1.b))) + -> Seq Scan on public.prt1_l_p3_p1 + Output: prt1_l_p3_p1.a, prt1_l_p3_p1.c, prt1_l_p3_p1.b + Filter: ((prt1_l_p3_p1.a % 25) = 0) + -> Hash + Output: prt2_l_p3_p1.b, prt2_l_p3_p1.c, prt2_l_p3_p1.a + -> Seq Scan on public.prt2_l_p3_p1 + Output: prt2_l_p3_p1.b, prt2_l_p3_p1.c, prt2_l_p3_p1.a + Filter: ((prt2_l_p3_p1.b % 25) = 0) + -> Hash Full Join + Output: prt1_l_p3_p2.a, prt1_l_p3_p2.c, prt2_l_p3_p2.b, prt2_l_p3_p2.c + Hash Cond: ((prt1_l_p3_p2.a = prt2_l_p3_p2.b) AND (prt1_l_p3_p2.b = prt2_l_p3_p2.a) AND ((prt1_l_p3_p2.c)::text = (prt2_l_p3_p2.c)::text) AND ((prt1_l_p3_p2.b + prt1_l_p3_p2.a) = (prt2_l_p3_p2.a + prt2_l_p3_p2.b))) + -> Seq Scan on public.prt1_l_p3_p2 + Output: prt1_l_p3_p2.a, prt1_l_p3_p2.c, prt1_l_p3_p2.b + Filter: ((prt1_l_p3_p2.a % 25) = 0) + -> Hash + Output: prt2_l_p3_p2.b, prt2_l_p3_p2.c, prt2_l_p3_p2.a + -> Seq Scan on public.prt2_l_p3_p2 + Output: prt2_l_p3_p2.b, prt2_l_p3_p2.c, prt2_l_p3_p2.a + Filter: ((prt2_l_p3_p2.b % 25) = 0) +(70 rows) + +SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_l WHERE prt1_l.a % 25 = 0) t1 FULL JOIN (SELECT * FROM prt2_l WHERE prt2_l.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.b = t2.a AND t1.c = t2.c AND t1.b + t1.a = t2.a + t2.b) ORDER BY t1.a, t2.b; + a | c | b | c +-----+------+-----+------ + 0 | 0000 | 0 | 0000 + 50 | 0050 | | + 100 | 0100 | | + 150 | 0150 | 150 | 0150 + 200 | 0200 | | + 250 | 0250 | | + 300 | 0300 | 300 | 0300 + 350 | 0350 | | + 400 | 0400 | | + 450 | 0450 | 450 | 0450 + 500 | 0500 | | + 550 | 0550 | | + | | 75 | 0075 + | | 225 | 0225 + | | 375 | 0375 + | | 525 | 0525 +(16 rows) + +SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM uprt1_l t1 WHERE t1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM uprt2_l t2 WHERE t2.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.b = t2.a AND t1.c = t2.c AND t1.b + t1.a = t2.a + t2.b) ORDER BY t1.a, t2.b; + a | c | b | c +-----+------+-----+------ + 0 | 0000 | 0 | 0000 + 50 | 0050 | | + 100 | 0100 | | + 150 | 0150 | 150 | 0150 + 200 | 0200 | | + 250 | 0250 | | + 300 | 0300 | 300 | 0300 + 350 | 0350 | | + 400 | 0400 | | + 450 | 0450 | 450 | 0450 + 500 | 0500 | | + 550 | 0550 | | + | | 75 | 0075 + | | 225 | 0225 + | | 375 | 0375 + | | 525 | 0525 +(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; +ANALYZE plt1_p1; +ANALYZE plt1_p2; +ANALYZE plt1_p3; +-- TODO: This table is created only for testing the results. Remove once +-- results are tested. +CREATE TABLE uplt1 AS SELECT * FROM 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; +ANALYZE plt2_p1; +ANALYZE plt2_p2; +ANALYZE plt2_p3; +-- TODO: This table is created only for testing the results. Remove once +-- results are tested. +CREATE TABLE uplt2 AS SELECT * FROM plt2; +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM plt1 t1, plt2 t2 WHERE t1.c = t2.c AND t1.a = t2.a AND t1.a % 25 = 0 ORDER BY t1.a, t2.b; + QUERY PLAN +-------------------------------------------------------------------- + Sort + Output: t1.a, t1.c, t2.b, t2.c + Sort Key: t1.a, t2.b + -> Append + -> Hash Join + Output: t1.a, t1.c, t2.b, t2.c + Hash Cond: ((t2.c = t1.c) AND (t2.a = t1.a)) + -> Seq Scan on public.plt2_p1 t2 + Output: t2.b, t2.c, t2.a + -> Hash + Output: t1.a, t1.c + -> Seq Scan on public.plt1_p1 t1 + Output: t1.a, t1.c + Filter: ((t1.a % 25) = 0) + -> Hash Join + Output: t1_1.a, t1_1.c, t2_1.b, t2_1.c + Hash Cond: ((t2_1.c = t1_1.c) AND (t2_1.a = t1_1.a)) + -> Seq Scan on public.plt2_p2 t2_1 + Output: t2_1.b, t2_1.c, t2_1.a + -> Hash + Output: t1_1.a, t1_1.c + -> Seq Scan on public.plt1_p2 t1_1 + Output: t1_1.a, t1_1.c + Filter: ((t1_1.a % 25) = 0) + -> Hash Join + Output: t1_2.a, t1_2.c, t2_2.b, t2_2.c + Hash Cond: ((t2_2.c = t1_2.c) AND (t2_2.a = t1_2.a)) + -> Seq Scan on public.plt2_p3 t2_2 + Output: t2_2.b, t2_2.c, t2_2.a + -> Hash + Output: t1_2.a, t1_2.c + -> Seq Scan on public.plt1_p3 t1_2 + Output: t1_2.a, t1_2.c + Filter: ((t1_2.a % 25) = 0) +(34 rows) + +SELECT t1.a, t1.c, t2.b, t2.c FROM plt1 t1, plt2 t2 WHERE t1.c = t2.c AND t1.a = t2.a AND t1.a % 25 = 0 ORDER BY t1.a, t2.b; + a | c | b | c +-----+------+-----+------ + 0 | 0000 | 0 | 0000 + 150 | 0003 | 150 | 0003 + 300 | 0006 | 300 | 0006 + 450 | 0009 | 450 | 0009 +(4 rows) + +SELECT t1.a, t1.c, t2.b, t2.c FROM uplt1 t1, uplt2 t2 WHERE t1.c = t2.c AND t1.a = t2.a AND t1.a % 25 = 0 ORDER BY t1.a, t2.b; + a | c | b | c +-----+------+-----+------ + 0 | 0000 | 0 | 0000 + 150 | 0003 | 150 | 0003 + 300 | 0006 | 300 | 0006 + 450 | 0009 | 450 | 0009 +(4 rows) + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.a = t2.a AND t1.c = t2.c WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b; + QUERY PLAN +-------------------------------------------------------------------- + Sort + Output: t1.a, t1.c, t2.b, t2.c + Sort Key: t1.a, t2.b + -> Append + -> Hash Right Join + Output: t1.a, t1.c, t2.b, t2.c + Hash Cond: ((t2.a = t1.a) AND (t2.c = t1.c)) + -> Seq Scan on public.plt2_p1 t2 + Output: t2.b, t2.c, t2.a + -> Hash + Output: t1.a, t1.c + -> Seq Scan on public.plt1_p1 t1 + Output: t1.a, t1.c + Filter: ((t1.a % 25) = 0) + -> Hash Right Join + Output: t1_1.a, t1_1.c, t2_1.b, t2_1.c + Hash Cond: ((t2_1.a = t1_1.a) AND (t2_1.c = t1_1.c)) + -> Seq Scan on public.plt2_p2 t2_1 + Output: t2_1.b, t2_1.c, t2_1.a + -> Hash + Output: t1_1.a, t1_1.c + -> Seq Scan on public.plt1_p2 t1_1 + Output: t1_1.a, t1_1.c + Filter: ((t1_1.a % 25) = 0) + -> Hash Right Join + Output: t1_2.a, t1_2.c, t2_2.b, t2_2.c + Hash Cond: ((t2_2.a = t1_2.a) AND (t2_2.c = t1_2.c)) + -> Seq Scan on public.plt2_p3 t2_2 + Output: t2_2.b, t2_2.c, t2_2.a + -> Hash + Output: t1_2.a, t1_2.c + -> Seq Scan on public.plt1_p3 t1_2 + Output: t1_2.a, t1_2.c + Filter: ((t1_2.a % 25) = 0) +(34 rows) + +SELECT t1.a, t1.c, t2.b, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.a = t2.a AND t1.c = t2.c WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b; + a | c | b | c +-----+------+-----+------ + 0 | 0000 | 0 | 0000 + 50 | 0001 | | + 100 | 0002 | | + 150 | 0003 | 150 | 0003 + 200 | 0004 | | + 250 | 0005 | | + 300 | 0006 | 300 | 0006 + 350 | 0007 | | + 400 | 0008 | | + 450 | 0009 | 450 | 0009 + 500 | 0010 | | + 550 | 0011 | | +(12 rows) + +SELECT t1.a, t1.c, t2.b, t2.c FROM uplt1 t1 LEFT JOIN uplt2 t2 ON t1.a = t2.a AND t1.c = t2.c WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b; + a | c | b | c +-----+------+-----+------ + 0 | 0000 | 0 | 0000 + 50 | 0001 | | + 100 | 0002 | | + 150 | 0003 | 150 | 0003 + 200 | 0004 | | + 250 | 0005 | | + 300 | 0006 | 300 | 0006 + 350 | 0007 | | + 400 | 0008 | | + 450 | 0009 | 450 | 0009 + 500 | 0010 | | + 550 | 0011 | | +(12 rows) + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b; + QUERY PLAN +-------------------------------------------------------------------------- + Sort + Output: t1.a, t1.c, t2.b, t2.c + Sort Key: t1.a, t2.b + -> Result + Output: t1.a, t1.c, t2.b, t2.c + -> Append + -> Hash Right Join + Output: t2.b, t2.c, t1.a, t1.c + Hash Cond: ((t1.a = t2.b) AND (t1.c = t2.c)) + -> Seq Scan on public.plt1_p1 t1 + Output: t1.a, t1.c + -> Hash + Output: t2.b, t2.c + -> Seq Scan on public.plt2_p1 t2 + Output: t2.b, t2.c + Filter: ((t2.b % 25) = 0) + -> Hash Right Join + Output: t2_1.b, t2_1.c, t1_1.a, t1_1.c + Hash Cond: ((t1_1.a = t2_1.b) AND (t1_1.c = t2_1.c)) + -> Seq Scan on public.plt1_p2 t1_1 + Output: t1_1.a, t1_1.c + -> Hash + Output: t2_1.b, t2_1.c + -> Seq Scan on public.plt2_p2 t2_1 + Output: t2_1.b, t2_1.c + Filter: ((t2_1.b % 25) = 0) + -> Hash Right Join + Output: t2_2.b, t2_2.c, t1_2.a, t1_2.c + Hash Cond: ((t1_2.a = t2_2.b) AND (t1_2.c = t2_2.c)) + -> Seq Scan on public.plt1_p3 t1_2 + Output: t1_2.a, t1_2.c + -> Hash + Output: t2_2.b, t2_2.c + -> Seq Scan on public.plt2_p3 t2_2 + Output: t2_2.b, t2_2.c + Filter: ((t2_2.b % 25) = 0) +(36 rows) + +SELECT t1.a, t1.c, t2.b, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b; + a | c | b | c +-----+------+-----+------ + 0 | 0000 | 0 | 0000 + 150 | 0003 | 150 | 0003 + 300 | 0006 | 300 | 0006 + 450 | 0009 | 450 | 0009 + | | 75 | 0001 + | | 225 | 0004 + | | 375 | 0007 + | | 525 | 0010 +(8 rows) + +SELECT t1.a, t1.c, t2.b, t2.c FROM uplt1 t1 RIGHT JOIN uplt2 t2 ON t1.a = t2.b AND t1.c = t2.c WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b; + a | c | b | c +-----+------+-----+------ + 0 | 0000 | 0 | 0000 + 150 | 0003 | 150 | 0003 + 300 | 0006 | 300 | 0006 + 450 | 0009 | 450 | 0009 + | | 75 | 0001 + | | 225 | 0004 + | | 375 | 0007 + | | 525 | 0010 +(8 rows) + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM plt1 WHERE plt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM plt2 WHERE plt2.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c) ORDER BY t1.a, t2.b; + QUERY PLAN +-------------------------------------------------------------------------------- + Sort + Output: plt1_p1.a, plt1_p1.c, plt2_p1.b, plt2_p1.c + Sort Key: plt1_p1.a, plt2_p1.b + -> Append + -> Hash Full Join + Output: plt1_p1.a, plt1_p1.c, plt2_p1.b, plt2_p1.c + Hash Cond: ((plt1_p1.a = plt2_p1.b) AND (plt1_p1.c = plt2_p1.c)) + -> Seq Scan on public.plt1_p1 + Output: plt1_p1.a, plt1_p1.c + Filter: ((plt1_p1.a % 25) = 0) + -> Hash + Output: plt2_p1.b, plt2_p1.c + -> Seq Scan on public.plt2_p1 + Output: plt2_p1.b, plt2_p1.c + Filter: ((plt2_p1.b % 25) = 0) + -> Hash Full Join + Output: plt1_p2.a, plt1_p2.c, plt2_p2.b, plt2_p2.c + Hash Cond: ((plt1_p2.a = plt2_p2.b) AND (plt1_p2.c = plt2_p2.c)) + -> Seq Scan on public.plt1_p2 + Output: plt1_p2.a, plt1_p2.c + Filter: ((plt1_p2.a % 25) = 0) + -> Hash + Output: plt2_p2.b, plt2_p2.c + -> Seq Scan on public.plt2_p2 + Output: plt2_p2.b, plt2_p2.c + Filter: ((plt2_p2.b % 25) = 0) + -> Hash Full Join + Output: plt1_p3.a, plt1_p3.c, plt2_p3.b, plt2_p3.c + Hash Cond: ((plt1_p3.a = plt2_p3.b) AND (plt1_p3.c = plt2_p3.c)) + -> Seq Scan on public.plt1_p3 + Output: plt1_p3.a, plt1_p3.c + Filter: ((plt1_p3.a % 25) = 0) + -> Hash + Output: plt2_p3.b, plt2_p3.c + -> Seq Scan on public.plt2_p3 + Output: plt2_p3.b, plt2_p3.c + Filter: ((plt2_p3.b % 25) = 0) +(37 rows) + +SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM plt1 WHERE plt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM plt2 WHERE plt2.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c) ORDER BY t1.a, t2.b; + a | c | b | c +-----+------+-----+------ + 0 | 0000 | 0 | 0000 + 50 | 0001 | | + 100 | 0002 | | + 150 | 0003 | 150 | 0003 + 200 | 0004 | | + 250 | 0005 | | + 300 | 0006 | 300 | 0006 + 350 | 0007 | | + 400 | 0008 | | + 450 | 0009 | 450 | 0009 + 500 | 0010 | | + 550 | 0011 | | + | | 75 | 0001 + | | 225 | 0004 + | | 375 | 0007 + | | 525 | 0010 +(16 rows) + +SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM uplt1 t1 WHERE t1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM uplt2 t2 WHERE t2.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c) ORDER BY t1.a, t2.b; + a | c | b | c +-----+------+-----+------ + 0 | 0000 | 0 | 0000 + 50 | 0001 | | + 100 | 0002 | | + 150 | 0003 | 150 | 0003 + 200 | 0004 | | + 250 | 0005 | | + 300 | 0006 | 300 | 0006 + 350 | 0007 | | + 400 | 0008 | | + 450 | 0009 | 450 | 0009 + 500 | 0010 | | + 550 | 0011 | | + | | 75 | 0001 + | | 225 | 0004 + | | 375 | 0007 + | | 525 | 0010 +(16 rows) + +-- Cases with non-nullable expressions in subquery results; +-- make sure these go to null as expected +EXPLAIN (VERBOSE, COSTS OFF) +SELECT sum(t1.a), t1.c, avg(t2.b), t2.c FROM (SELECT 50 phv, * FROM plt1 WHERE plt1.a % 25 = 0) t1 FULL JOIN (SELECT 75 phv, * FROM plt2 WHERE plt2.b % 25 = 0) t2 ON (t1.c = t2.c AND t1.a = t2.b) GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c; + QUERY PLAN +-------------------------------------------------------------------------------------------- + GroupAggregate + Output: sum(plt1_p1.a), plt1_p1.c, avg(plt2_p1.b), plt2_p1.c + Group Key: plt1_p1.c, plt2_p1.c + -> Sort + Output: plt1_p1.c, plt2_p1.c, plt1_p1.a, plt2_p1.b + Sort Key: plt1_p1.c, plt2_p1.c + -> Result + Output: plt1_p1.c, plt2_p1.c, plt1_p1.a, plt2_p1.b + -> Append + -> Hash Full Join + Output: plt1_p1.a, plt1_p1.c, plt2_p1.b, plt2_p1.c + Hash Cond: ((plt1_p1.c = plt2_p1.c) AND (plt1_p1.a = plt2_p1.b)) + -> Seq Scan on public.plt1_p1 + Output: plt1_p1.a, plt1_p1.c + Filter: ((plt1_p1.a % 25) = 0) + -> Hash + Output: plt2_p1.b, plt2_p1.c + -> Seq Scan on public.plt2_p1 + Output: plt2_p1.b, plt2_p1.c + Filter: ((plt2_p1.b % 25) = 0) + -> Hash Full Join + Output: plt1_p2.a, plt1_p2.c, plt2_p2.b, plt2_p2.c + Hash Cond: ((plt1_p2.c = plt2_p2.c) AND (plt1_p2.a = plt2_p2.b)) + -> Seq Scan on public.plt1_p2 + Output: plt1_p2.a, plt1_p2.c + Filter: ((plt1_p2.a % 25) = 0) + -> Hash + Output: plt2_p2.b, plt2_p2.c + -> Seq Scan on public.plt2_p2 + Output: plt2_p2.b, plt2_p2.c + Filter: ((plt2_p2.b % 25) = 0) + -> Hash Full Join + Output: plt1_p3.a, plt1_p3.c, plt2_p3.b, plt2_p3.c + Hash Cond: ((plt1_p3.c = plt2_p3.c) AND (plt1_p3.a = plt2_p3.b)) + -> Seq Scan on public.plt1_p3 + Output: plt1_p3.a, plt1_p3.c + Filter: ((plt1_p3.a % 25) = 0) + -> Hash + Output: plt2_p3.b, plt2_p3.c + -> Seq Scan on public.plt2_p3 + Output: plt2_p3.b, plt2_p3.c + Filter: ((plt2_p3.b % 25) = 0) +(42 rows) + +SELECT sum(t1.a), t1.c, avg(t2.b), t2.c FROM (SELECT 50 phv, * FROM plt1 WHERE plt1.a % 25 = 0) t1 FULL JOIN (SELECT 75 phv, * FROM plt2 WHERE plt2.b % 25 = 0) t2 ON (t1.c = t2.c AND t1.a = t2.b) GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c; + sum | c | avg | c +-----+------+------------------------+------ + 0 | 0000 | 0.00000000000000000000 | 0000 + 50 | 0001 | | + 100 | 0002 | | + 150 | 0003 | 150.0000000000000000 | 0003 + 200 | 0004 | | + 250 | 0005 | | + 300 | 0006 | 300.0000000000000000 | 0006 + 350 | 0007 | | + 400 | 0008 | | + 450 | 0009 | 450.0000000000000000 | 0009 + 500 | 0010 | | + 550 | 0011 | | + | | 75.0000000000000000 | 0001 + | | 225.0000000000000000 | 0004 + | | 375.0000000000000000 | 0007 + | | 525.0000000000000000 | 0010 +(16 rows) + +SELECT sum(t1.a), t1.c, avg(t2.b), t2.c FROM (SELECT 50 phv, * FROM uplt1 WHERE uplt1.a % 25 = 0) t1 FULL JOIN (SELECT 75 phv, * FROM uplt2 WHERE uplt2.b % 25 = 0) t2 ON (t1.c = t2.c AND t1.a = t2.b) GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c; + sum | c | avg | c +-----+------+------------------------+------ + 0 | 0000 | 0.00000000000000000000 | 0000 + 50 | 0001 | | + 100 | 0002 | | + 150 | 0003 | 150.0000000000000000 | 0003 + 200 | 0004 | | + 250 | 0005 | | + 300 | 0006 | 300.0000000000000000 | 0006 + 350 | 0007 | | + 400 | 0008 | | + 450 | 0009 | 450.0000000000000000 | 0009 + 500 | 0010 | | + 550 | 0011 | | + | | 75.0000000000000000 | 0001 + | | 225.0000000000000000 | 0004 + | | 375.0000000000000000 | 0007 + | | 525.0000000000000000 | 0010 +(16 rows) + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT sum(t1.a), t1.c, sum(t1.phv), avg(t2.b), t2.c, avg(t2.phv) FROM (SELECT 25 phv, * FROM plt1 WHERE plt1.a % 25 = 0) t1 FULL JOIN (SELECT 50 phv, * FROM plt2 WHERE plt2.b % 25 = 0) t2 ON (t1.c = t2.c AND t1.a = t2.b) GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c; + QUERY PLAN +-------------------------------------------------------------------------------------------- + GroupAggregate + Output: sum(plt1_p1.a), plt1_p1.c, sum((25)), avg(plt2_p1.b), plt2_p1.c, avg((50)) + Group Key: plt1_p1.c, plt2_p1.c + -> Sort + Output: plt1_p1.c, plt2_p1.c, plt1_p1.a, (25), plt2_p1.b, (50) + Sort Key: plt1_p1.c, plt2_p1.c + -> Result + Output: plt1_p1.c, plt2_p1.c, plt1_p1.a, (25), plt2_p1.b, (50) + -> Append + -> Hash Full Join + Output: plt1_p1.a, plt1_p1.c, plt2_p1.b, plt2_p1.c, (25), (50) + Hash Cond: ((plt1_p1.c = plt2_p1.c) AND (plt1_p1.a = plt2_p1.b)) + -> Seq Scan on public.plt1_p1 + Output: plt1_p1.a, plt1_p1.c, 25 + Filter: ((plt1_p1.a % 25) = 0) + -> Hash + Output: plt2_p1.b, plt2_p1.c, (50) + -> Seq Scan on public.plt2_p1 + Output: plt2_p1.b, plt2_p1.c, 50 + Filter: ((plt2_p1.b % 25) = 0) + -> Hash Full Join + Output: plt1_p2.a, plt1_p2.c, plt2_p2.b, plt2_p2.c, (25), (50) + Hash Cond: ((plt1_p2.c = plt2_p2.c) AND (plt1_p2.a = plt2_p2.b)) + -> Seq Scan on public.plt1_p2 + Output: plt1_p2.a, plt1_p2.c, 25 + Filter: ((plt1_p2.a % 25) = 0) + -> Hash + Output: plt2_p2.b, plt2_p2.c, (50) + -> Seq Scan on public.plt2_p2 + Output: plt2_p2.b, plt2_p2.c, 50 + Filter: ((plt2_p2.b % 25) = 0) + -> Hash Full Join + Output: plt1_p3.a, plt1_p3.c, plt2_p3.b, plt2_p3.c, (25), (50) + Hash Cond: ((plt1_p3.c = plt2_p3.c) AND (plt1_p3.a = plt2_p3.b)) + -> Seq Scan on public.plt1_p3 + Output: plt1_p3.a, plt1_p3.c, 25 + Filter: ((plt1_p3.a % 25) = 0) + -> Hash + Output: plt2_p3.b, plt2_p3.c, (50) + -> Seq Scan on public.plt2_p3 + Output: plt2_p3.b, plt2_p3.c, 50 + Filter: ((plt2_p3.b % 25) = 0) +(42 rows) + +SELECT sum(t1.a), t1.c, sum(t1.phv), avg(t2.b), t2.c, avg(t2.phv) FROM (SELECT 25 phv, * FROM plt1 WHERE plt1.a % 25 = 0) t1 FULL JOIN (SELECT 50 phv, * FROM plt2 WHERE plt2.b % 25 = 0) t2 ON (t1.c = t2.c AND t1.a = t2.b) GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c; + sum | c | sum | avg | c | avg +-----+------+-----+------------------------+------+--------------------- + 0 | 0000 | 25 | 0.00000000000000000000 | 0000 | 50.0000000000000000 + 50 | 0001 | 25 | | | + 100 | 0002 | 25 | | | + 150 | 0003 | 25 | 150.0000000000000000 | 0003 | 50.0000000000000000 + 200 | 0004 | 25 | | | + 250 | 0005 | 25 | | | + 300 | 0006 | 25 | 300.0000000000000000 | 0006 | 50.0000000000000000 + 350 | 0007 | 25 | | | + 400 | 0008 | 25 | | | + 450 | 0009 | 25 | 450.0000000000000000 | 0009 | 50.0000000000000000 + 500 | 0010 | 25 | | | + 550 | 0011 | 25 | | | + | | | 75.0000000000000000 | 0001 | 50.0000000000000000 + | | | 225.0000000000000000 | 0004 | 50.0000000000000000 + | | | 375.0000000000000000 | 0007 | 50.0000000000000000 + | | | 525.0000000000000000 | 0010 | 50.0000000000000000 +(16 rows) + +SELECT sum(t1.a), t1.c, sum(t1.phv), avg(t2.b), t2.c, avg(t2.phv) FROM (SELECT 25 phv, * FROM uplt1 WHERE uplt1.a % 25 = 0) t1 FULL JOIN (SELECT 50 phv, * FROM uplt2 WHERE uplt2.b % 25 = 0) t2 ON (t1.c = t2.c AND t1.a = t2.b) GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c; + sum | c | sum | avg | c | avg +-----+------+-----+------------------------+------+--------------------- + 0 | 0000 | 25 | 0.00000000000000000000 | 0000 | 50.0000000000000000 + 50 | 0001 | 25 | | | + 100 | 0002 | 25 | | | + 150 | 0003 | 25 | 150.0000000000000000 | 0003 | 50.0000000000000000 + 200 | 0004 | 25 | | | + 250 | 0005 | 25 | | | + 300 | 0006 | 25 | 300.0000000000000000 | 0006 | 50.0000000000000000 + 350 | 0007 | 25 | | | + 400 | 0008 | 25 | | | + 450 | 0009 | 25 | 450.0000000000000000 | 0009 | 50.0000000000000000 + 500 | 0010 | 25 | | | + 550 | 0011 | 25 | | | + | | | 75.0000000000000000 | 0001 | 50.0000000000000000 + | | | 225.0000000000000000 | 0004 | 50.0000000000000000 + | | | 375.0000000000000000 | 0007 | 50.0000000000000000 + | | | 525.0000000000000000 | 0010 | 50.0000000000000000 +(16 rows) + +-- Join with pruned partitions from joining relations +EXPLAIN (VERBOSE, COSTS OFF) +SELECT sum(t1.a), t1.c, avg(t2.b), t2.c FROM plt1 t1, plt2 t2 WHERE t1.c = t2.c AND t1.c NOT IN ('0001', '0005', '0002', '0009') AND t2.c NOT IN ('0000', '0003', '0004', '0010') GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c; + QUERY PLAN +----------------------------------------------------------------------------------------------- + Sort + Output: (sum(t1.a)), t1.c, (avg(t2.b)), t2.c + Sort Key: t1.c + -> HashAggregate + Output: sum(t1.a), t1.c, avg(t2.b), t2.c + Group Key: t1.c, t2.c + -> Result + Output: t1.c, t2.c, t1.a, t2.b + -> Append + -> Hash Join + Output: t1.a, t1.c, t2.b, t2.c + Hash Cond: (t1.c = t2.c) + -> Seq Scan on public.plt1_p3 t1 + Output: t1.a, t1.c + Filter: (t1.c <> ALL ('{0001,0005,0002,0009}'::text[])) + -> Hash + Output: t2.b, t2.c + -> Seq Scan on public.plt2_p3 t2 + Output: t2.b, t2.c + Filter: (t2.c <> ALL ('{0000,0003,0004,0010}'::text[])) +(20 rows) + +SELECT sum(t1.a), t1.c, avg(t2.b), t2.c FROM plt1 t1, plt2 t2 WHERE t1.c = t2.c AND t1.c NOT IN ('0001', '0005', '0002', '0009') AND t2.c NOT IN ('0000', '0003', '0004', '0010') GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c; + sum | c | avg | c +--------+------+----------------------+------ + 137700 | 0006 | 324.0000000000000000 | 0006 + 158950 | 0007 | 375.0000000000000000 | 0007 + 169600 | 0008 | 424.5000000000000000 | 0008 + 229600 | 0011 | 574.5000000000000000 | 0011 +(4 rows) + +SELECT sum(t1.a), t1.c, avg(t2.b), t2.c FROM uplt1 t1, uplt2 t2 WHERE t1.c = t2.c AND t1.c NOT IN ('0001', '0005', '0002', '0009') AND t2.c NOT IN ('0000', '0003', '0004', '0010') GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c; + sum | c | avg | c +--------+------+----------------------+------ + 137700 | 0006 | 324.0000000000000000 | 0006 + 158950 | 0007 | 375.0000000000000000 | 0007 + 169600 | 0008 | 424.5000000000000000 | 0008 + 229600 | 0011 | 574.5000000000000000 | 0011 +(4 rows) + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT sum(t1.a), t1.c, sum(t2.b), t2.c FROM (SELECT * FROM plt1 t1 WHERE t1.c NOT IN ('0001', '0005', '0002', '0009')) t1 LEFT JOIN (SELECT * FROM plt2 t2 WHERE t2.c NOT IN ('0000', '0003', '0004', '0010')) t2 ON t1.c = t2.c GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c; + QUERY PLAN +----------------------------------------------------------------------------------------------- + Sort + Output: (sum(t1.a)), t1.c, (sum(b)), c + Sort Key: t1.c, c + -> HashAggregate + Output: sum(t1.a), t1.c, sum(b), c + Group Key: t1.c, c + -> Result + Output: t1.c, c, t1.a, b + -> Append + -> Hash Left Join + Output: t1.a, t1.c, b, c + Hash Cond: (t1.c = c) + -> Seq Scan on public.plt1_p1 t1 + Output: t1.a, t1.c + Filter: (t1.c <> ALL ('{0001,0005,0002,0009}'::text[])) + -> Hash + Output: b, c + -> Result + Output: b, c + One-Time Filter: false + -> Hash Left Join + Output: t1_1.a, t1_1.c, t2.b, t2.c + Hash Cond: (t1_1.c = t2.c) + -> Seq Scan on public.plt1_p3 t1_1 + Output: t1_1.a, t1_1.c + Filter: (t1_1.c <> ALL ('{0001,0005,0002,0009}'::text[])) + -> Hash + Output: t2.b, t2.c + -> Seq Scan on public.plt2_p3 t2 + Output: t2.b, t2.c + Filter: (t2.c <> ALL ('{0000,0003,0004,0010}'::text[])) +(31 rows) + +SELECT sum(t1.a), t1.c, sum(t2.b), t2.c FROM (SELECT * FROM plt1 t1 WHERE t1.c NOT IN ('0001', '0005', '0002', '0009')) t1 LEFT JOIN (SELECT * FROM plt2 t2 WHERE t2.c NOT IN ('0000', '0003', '0004', '0010')) t2 ON t1.c = t2.c GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c; + sum | c | sum | c +--------+------+--------+------ + 600 | 0000 | | + 4350 | 0003 | | + 5600 | 0004 | | + 137700 | 0006 | 137700 | 0006 + 158950 | 0007 | 159375 | 0007 + 169600 | 0008 | 169800 | 0008 + 13100 | 0010 | | + 229600 | 0011 | 229800 | 0011 +(8 rows) + +SELECT sum(t1.a), t1.c, sum(t2.b), t2.c FROM (SELECT * FROM uplt1 t1 WHERE t1.c NOT IN ('0001', '0005', '0002', '0009')) t1 LEFT JOIN (SELECT * FROM uplt2 t2 WHERE t2.c NOT IN ('0000', '0003', '0004', '0010')) t2 ON t1.c = t2.c GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c; + sum | c | sum | c +--------+------+--------+------ + 600 | 0000 | | + 4350 | 0003 | | + 5600 | 0004 | | + 137700 | 0006 | 137700 | 0006 + 158950 | 0007 | 159375 | 0007 + 169600 | 0008 | 169800 | 0008 + 13100 | 0010 | | + 229600 | 0011 | 229800 | 0011 +(8 rows) + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT sum(t1.a), t1.c, sum(t2.b), t2.c FROM (SELECT * FROM plt1 t1 WHERE t1.c NOT IN ('0001', '0005', '0002', '0009')) t1 RIGHT JOIN (SELECT * FROM plt2 t2 WHERE t2.c NOT IN ('0000', '0003', '0004', '0010')) t2 ON t1.c = t2.c GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c; + QUERY PLAN +------------------------------------------------------------------------------------------------- + Sort + Output: (sum(a)), c, (sum(t2.b)), t2.c + Sort Key: c, t2.c + -> HashAggregate + Output: sum(a), c, sum(t2.b), t2.c + Group Key: c, t2.c + -> Result + Output: c, t2.c, a, t2.b + -> Append + -> Hash Left Join + Output: t2.b, t2.c, a, c + Hash Cond: (t2.c = c) + -> Seq Scan on public.plt2_p2 t2 + Output: t2.b, t2.c + Filter: (t2.c <> ALL ('{0000,0003,0004,0010}'::text[])) + -> Hash + Output: a, c + -> Result + Output: a, c + One-Time Filter: false + -> Hash Right Join + Output: t2_1.b, t2_1.c, t1.a, t1.c + Hash Cond: (t1.c = t2_1.c) + -> Seq Scan on public.plt1_p3 t1 + Output: t1.a, t1.c + Filter: (t1.c <> ALL ('{0001,0005,0002,0009}'::text[])) + -> Hash + Output: t2_1.b, t2_1.c + -> Seq Scan on public.plt2_p3 t2_1 + Output: t2_1.b, t2_1.c + Filter: (t2_1.c <> ALL ('{0000,0003,0004,0010}'::text[])) +(31 rows) + +SELECT sum(t1.a), t1.c, sum(t2.b), t2.c FROM (SELECT * FROM plt1 t1 WHERE t1.c NOT IN ('0001', '0005', '0002', '0009')) t1 RIGHT JOIN (SELECT * FROM plt2 t2 WHERE t2.c NOT IN ('0000', '0003', '0004', '0010')) t2 ON t1.c = t2.c GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c; + sum | c | sum | c +--------+------+--------+------ + 137700 | 0006 | 137700 | 0006 + 158950 | 0007 | 159375 | 0007 + 169600 | 0008 | 169800 | 0008 + 229600 | 0011 | 229800 | 0011 + | | 1275 | 0001 + | | 1992 | 0002 + | | 4392 | 0005 + | | 8058 | 0009 +(8 rows) + +SELECT sum(t1.a), t1.c, sum(t2.b), t2.c FROM (SELECT * FROM uplt1 t1 WHERE t1.c NOT IN ('0001', '0005', '0002', '0009')) t1 RIGHT JOIN (SELECT * FROM uplt2 t2 WHERE t2.c NOT IN ('0000', '0003', '0004', '0010')) t2 ON t1.c = t2.c GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c; + sum | c | sum | c +--------+------+--------+------ + 137700 | 0006 | 137700 | 0006 + 158950 | 0007 | 159375 | 0007 + 169600 | 0008 | 169800 | 0008 + 229600 | 0011 | 229800 | 0011 + | | 1275 | 0001 + | | 1992 | 0002 + | | 4392 | 0005 + | | 8058 | 0009 +(8 rows) + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT sum(t1.a), t1.c, sum(t2.b), t2.c FROM (SELECT * FROM plt1 t1 WHERE t1.c NOT IN ('0001', '0005', '0002', '0009')) t1 FULL JOIN (SELECT * FROM plt2 t2 WHERE t2.c NOT IN ('0000', '0003', '0004', '0010')) t2 ON t1.c = t2.c GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c; + QUERY PLAN +------------------------------------------------------------------------------------------------- + Sort + Output: (sum(t1.a)), t1.c, (sum(b)), c + Sort Key: t1.c, c + -> HashAggregate + Output: sum(t1.a), t1.c, sum(b), c + Group Key: t1.c, c + -> Result + Output: t1.c, c, t1.a, b + -> Append + -> Hash Full Join + Output: t1.a, t1.c, b, c + Hash Cond: (t1.c = c) + -> Seq Scan on public.plt1_p1 t1 + Output: t1.a, t1.c + Filter: (t1.c <> ALL ('{0001,0005,0002,0009}'::text[])) + -> Hash + Output: b, c + -> Result + Output: b, c + One-Time Filter: false + -> Hash Full Join + Output: a, c, t2.b, t2.c + Hash Cond: (t2.c = c) + -> Seq Scan on public.plt2_p2 t2 + Output: t2.b, t2.c + Filter: (t2.c <> ALL ('{0000,0003,0004,0010}'::text[])) + -> Hash + Output: a, c + -> Result + Output: a, c + One-Time Filter: false + -> Hash Full Join + Output: t1_1.a, t1_1.c, t2_1.b, t2_1.c + Hash Cond: (t1_1.c = t2_1.c) + -> Seq Scan on public.plt1_p3 t1_1 + Output: t1_1.a, t1_1.c + Filter: (t1_1.c <> ALL ('{0001,0005,0002,0009}'::text[])) + -> Hash + Output: t2_1.b, t2_1.c + -> Seq Scan on public.plt2_p3 t2_1 + Output: t2_1.b, t2_1.c + Filter: (t2_1.c <> ALL ('{0000,0003,0004,0010}'::text[])) +(42 rows) + +SELECT sum(t1.a), t1.c, sum(t2.b), t2.c FROM (SELECT * FROM plt1 t1 WHERE t1.c NOT IN ('0001', '0005', '0002', '0009')) t1 FULL JOIN (SELECT * FROM plt2 t2 WHERE t2.c NOT IN ('0000', '0003', '0004', '0010')) t2 ON t1.c = t2.c GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c; + sum | c | sum | c +--------+------+--------+------ + 600 | 0000 | | + 4350 | 0003 | | + 5600 | 0004 | | + 137700 | 0006 | 137700 | 0006 + 158950 | 0007 | 159375 | 0007 + 169600 | 0008 | 169800 | 0008 + 13100 | 0010 | | + 229600 | 0011 | 229800 | 0011 + | | 1275 | 0001 + | | 1992 | 0002 + | | 4392 | 0005 + | | 8058 | 0009 +(12 rows) + +SELECT sum(t1.a), t1.c, sum(t2.b), t2.c FROM (SELECT * FROM uplt1 t1 WHERE t1.c NOT IN ('0001', '0005', '0002', '0009')) t1 FULL JOIN (SELECT * FROM uplt2 t2 WHERE t2.c NOT IN ('0000', '0003', '0004', '0010')) t2 ON t1.c = t2.c GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c; + sum | c | sum | c +--------+------+--------+------ + 600 | 0000 | | + 4350 | 0003 | | + 5600 | 0004 | | + 137700 | 0006 | 137700 | 0006 + 158950 | 0007 | 159375 | 0007 + 169600 | 0008 | 169800 | 0008 + 13100 | 0010 | | + 229600 | 0011 | 229800 | 0011 + | | 1275 | 0001 + | | 1992 | 0002 + | | 4392 | 0005 + | | 8058 | 0009 +(12 rows) + +-- Semi-join +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.* FROM plt1 t1 WHERE t1.c IN (SELECT t1.c FROM plt2 t1 WHERE t1.b % 25 = 0) AND t1.a % 25 = 0 ORDER BY t1.a; + QUERY PLAN +--------------------------------------------------------- + Sort + Output: t1.a, t1.b, t1.c + Sort Key: t1.a + -> Append + -> Nested Loop Semi Join + Output: t1.a, t1.b, t1.c + Join Filter: (t1.c = t1_3.c) + -> Seq Scan on public.plt1_p1 t1 + Output: t1.a, t1.b, t1.c + Filter: ((t1.a % 25) = 0) + -> Materialize + Output: t1_3.c + -> Seq Scan on public.plt2_p1 t1_3 + Output: t1_3.c + Filter: ((t1_3.b % 25) = 0) + -> Nested Loop Semi Join + Output: t1_1.a, t1_1.b, t1_1.c + Join Filter: (t1_1.c = t1_4.c) + -> Seq Scan on public.plt1_p2 t1_1 + Output: t1_1.a, t1_1.b, t1_1.c + Filter: ((t1_1.a % 25) = 0) + -> Materialize + Output: t1_4.c + -> Seq Scan on public.plt2_p2 t1_4 + Output: t1_4.c + Filter: ((t1_4.b % 25) = 0) + -> Nested Loop Semi Join + Output: t1_2.a, t1_2.b, t1_2.c + Join Filter: (t1_2.c = t1_5.c) + -> Seq Scan on public.plt1_p3 t1_2 + Output: t1_2.a, t1_2.b, t1_2.c + Filter: ((t1_2.a % 25) = 0) + -> Materialize + Output: t1_5.c + -> Seq Scan on public.plt2_p3 t1_5 + Output: t1_5.c + Filter: ((t1_5.b % 25) = 0) +(37 rows) + +SELECT t1.* FROM plt1 t1 WHERE t1.c IN (SELECT t1.c FROM plt2 t1 WHERE t1.b % 25 = 0) AND t1.a % 25 = 0 ORDER BY t1.a; + a | b | c +-----+-----+------ + 0 | 0 | 0000 + 50 | 50 | 0001 + 150 | 150 | 0003 + 200 | 200 | 0004 + 300 | 300 | 0006 + 350 | 350 | 0007 + 450 | 450 | 0009 + 500 | 500 | 0010 +(8 rows) + +SELECT t1.* FROM uplt1 t1 WHERE t1.c IN (SELECT t1.c FROM uplt2 t1 WHERE t1.b % 25 = 0) AND t1.a % 25 = 0 ORDER BY t1.a; + a | b | c +-----+-----+------ + 0 | 0 | 0000 + 50 | 50 | 0001 + 150 | 150 | 0003 + 200 | 200 | 0004 + 300 | 300 | 0006 + 350 | 350 | 0007 + 450 | 450 | 0009 + 500 | 500 | 0010 +(8 rows) + +-- +-- 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; +ANALYZE plt1_e_p1; +ANALYZE plt1_e_p2; +ANALYZE plt1_e_p3; +-- TODO: This table is created only for testing the results. Remove once +-- results are tested. +CREATE TABLE uplt1_e AS SELECT * FROM plt1_e; +CREATE TABLE plt2_e (a int, b int, c text) PARTITION BY LIST(ltrim(c, 'A')); +CREATE TABLE plt2_e_p1 PARTITION OF plt2_e FOR VALUES IN ('0000', '0003', '0004', '0010'); +CREATE TABLE plt2_e_p2 PARTITION OF plt2_e FOR VALUES IN ('0001', '0005', '0002', '0009'); +CREATE TABLE plt2_e_p3 PARTITION OF plt2_e FOR VALUES IN ('0006', '0007', '0008', '0011'); +INSERT INTO plt2_e SELECT i, i, 'A' || to_char(i/50, 'FM0000') FROM generate_series(0, 599, 3) i; +ANALYZE plt2_e; +ANALYZE plt2_e_p1; +ANALYZE plt2_e_p2; +ANALYZE plt2_e_p3; +-- TODO: This table is created only for testing the results. Remove once +-- results are tested. +CREATE TABLE uplt2_e AS SELECT * FROM plt2_e; +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM plt1_e t1, plt2_e t2 WHERE t1.a = t2.b AND ltrim(t1.c, 'A') = ltrim(t2.c, 'A') AND t1.a % 25 = 0 ORDER BY t1.a, t2.b; + QUERY PLAN +-------------------------------------------------------------------------------------------------------- + Sort + Output: t1.a, t1.c, t2.b, t2.c + Sort Key: t1.a + -> Append + -> Hash Join + Output: t1.a, t1.c, t2.b, t2.c + Hash Cond: ((t2.b = t1.a) AND (ltrim(t2.c, 'A'::text) = ltrim(t1.c, 'A'::text))) + -> Seq Scan on public.plt2_e_p1 t2 + Output: t2.b, t2.c + -> Hash + Output: t1.a, t1.c + -> Seq Scan on public.plt1_e_p1 t1 + Output: t1.a, t1.c + Filter: ((t1.a % 25) = 0) + -> Hash Join + Output: t1_1.a, t1_1.c, t2_1.b, t2_1.c + Hash Cond: ((t2_1.b = t1_1.a) AND (ltrim(t2_1.c, 'A'::text) = ltrim(t1_1.c, 'A'::text))) + -> Seq Scan on public.plt2_e_p2 t2_1 + Output: t2_1.b, t2_1.c + -> Hash + Output: t1_1.a, t1_1.c + -> Seq Scan on public.plt1_e_p2 t1_1 + Output: t1_1.a, t1_1.c + Filter: ((t1_1.a % 25) = 0) + -> Hash Join + Output: t1_2.a, t1_2.c, t2_2.b, t2_2.c + Hash Cond: ((t2_2.b = t1_2.a) AND (ltrim(t2_2.c, 'A'::text) = ltrim(t1_2.c, 'A'::text))) + -> Seq Scan on public.plt2_e_p3 t2_2 + Output: t2_2.b, t2_2.c + -> Hash + Output: t1_2.a, t1_2.c + -> Seq Scan on public.plt1_e_p3 t1_2 + Output: t1_2.a, t1_2.c + Filter: ((t1_2.a % 25) = 0) +(34 rows) + +SELECT t1.a, t1.c, t2.b, t2.c FROM plt1_e t1, plt2_e t2 WHERE t1.a = t2.b AND ltrim(t1.c, 'A') = ltrim(t2.c, 'A') AND t1.a % 25 = 0 ORDER BY t1.a, t2.b; + a | c | b | c +-----+-------+-----+------- + 0 | A0000 | 0 | A0000 + 150 | A0003 | 150 | A0003 + 300 | A0006 | 300 | A0006 + 450 | A0009 | 450 | A0009 +(4 rows) + +SELECT t1.a, t1.c, t2.b, t2.c FROM uplt1_e t1, uplt2_e t2 WHERE t1.a = t2.b AND ltrim(t1.c, 'A') = ltrim(t2.c, 'A') AND t1.a % 25 = 0 ORDER BY t1.a, t2.b; + a | c | b | c +-----+-------+-----+------- + 0 | A0000 | 0 | A0000 + 150 | A0003 | 150 | A0003 + 300 | A0006 | 300 | A0006 + 450 | A0009 | 450 | A0009 +(4 rows) + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM plt1_e t1 LEFT JOIN plt2_e t2 ON t1.a = t2.b AND ltrim(t1.c, 'A') = ltrim(t2.c, 'A') WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b; + QUERY PLAN +-------------------------------------------------------------------------------------------------------- + Sort + Output: t1.a, t1.c, t2.b, t2.c + Sort Key: t1.a, t2.b + -> Append + -> Hash Right Join + Output: t1.a, t1.c, t2.b, t2.c + Hash Cond: ((t2.b = t1.a) AND (ltrim(t2.c, 'A'::text) = ltrim(t1.c, 'A'::text))) + -> Seq Scan on public.plt2_e_p1 t2 + Output: t2.b, t2.c + -> Hash + Output: t1.a, t1.c + -> Seq Scan on public.plt1_e_p1 t1 + Output: t1.a, t1.c + Filter: ((t1.a % 25) = 0) + -> Hash Right Join + Output: t1_1.a, t1_1.c, t2_1.b, t2_1.c + Hash Cond: ((t2_1.b = t1_1.a) AND (ltrim(t2_1.c, 'A'::text) = ltrim(t1_1.c, 'A'::text))) + -> Seq Scan on public.plt2_e_p2 t2_1 + Output: t2_1.b, t2_1.c + -> Hash + Output: t1_1.a, t1_1.c + -> Seq Scan on public.plt1_e_p2 t1_1 + Output: t1_1.a, t1_1.c + Filter: ((t1_1.a % 25) = 0) + -> Hash Right Join + Output: t1_2.a, t1_2.c, t2_2.b, t2_2.c + Hash Cond: ((t2_2.b = t1_2.a) AND (ltrim(t2_2.c, 'A'::text) = ltrim(t1_2.c, 'A'::text))) + -> Seq Scan on public.plt2_e_p3 t2_2 + Output: t2_2.b, t2_2.c + -> Hash + Output: t1_2.a, t1_2.c + -> Seq Scan on public.plt1_e_p3 t1_2 + Output: t1_2.a, t1_2.c + Filter: ((t1_2.a % 25) = 0) +(34 rows) + +SELECT t1.a, t1.c, t2.b, t2.c FROM plt1_e t1 LEFT JOIN plt2_e t2 ON t1.a = t2.b AND ltrim(t1.c, 'A') = ltrim(t2.c, 'A') WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b; + a | c | b | c +-----+-------+-----+------- + 0 | A0000 | 0 | A0000 + 50 | A0001 | | + 100 | A0002 | | + 150 | A0003 | 150 | A0003 + 200 | A0004 | | + 250 | A0005 | | + 300 | A0006 | 300 | A0006 + 350 | A0007 | | + 400 | A0008 | | + 450 | A0009 | 450 | A0009 + 500 | A0010 | | + 550 | A0011 | | +(12 rows) + +SELECT t1.a, t1.c, t2.b, t2.c FROM uplt1_e t1 LEFT JOIN uplt2_e t2 ON t1.a = t2.b AND ltrim(t1.c, 'A') = ltrim(t2.c, 'A') WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b; + a | c | b | c +-----+-------+-----+------- + 0 | A0000 | 0 | A0000 + 50 | A0001 | | + 100 | A0002 | | + 150 | A0003 | 150 | A0003 + 200 | A0004 | | + 250 | A0005 | | + 300 | A0006 | 300 | A0006 + 350 | A0007 | | + 400 | A0008 | | + 450 | A0009 | 450 | A0009 + 500 | A0010 | | + 550 | A0011 | | +(12 rows) + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM plt1_e t1 RIGHT JOIN plt2_e t2 ON t1.a = t2.b AND ltrim(t1.c, 'A') = ltrim(t2.c, 'A') WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b; + QUERY PLAN +-------------------------------------------------------------------------------------------------------------- + Sort + Output: t1.a, t1.c, t2.b, t2.c + Sort Key: t1.a, t2.b + -> Result + Output: t1.a, t1.c, t2.b, t2.c + -> Append + -> Hash Right Join + Output: t2.b, t2.c, t1.a, t1.c + Hash Cond: ((t1.a = t2.b) AND (ltrim(t1.c, 'A'::text) = ltrim(t2.c, 'A'::text))) + -> Seq Scan on public.plt1_e_p1 t1 + Output: t1.a, t1.c + -> Hash + Output: t2.b, t2.c + -> Seq Scan on public.plt2_e_p1 t2 + Output: t2.b, t2.c + Filter: ((t2.b % 25) = 0) + -> Hash Right Join + Output: t2_1.b, t2_1.c, t1_1.a, t1_1.c + Hash Cond: ((t1_1.a = t2_1.b) AND (ltrim(t1_1.c, 'A'::text) = ltrim(t2_1.c, 'A'::text))) + -> Seq Scan on public.plt1_e_p2 t1_1 + Output: t1_1.a, t1_1.c + -> Hash + Output: t2_1.b, t2_1.c + -> Seq Scan on public.plt2_e_p2 t2_1 + Output: t2_1.b, t2_1.c + Filter: ((t2_1.b % 25) = 0) + -> Hash Right Join + Output: t2_2.b, t2_2.c, t1_2.a, t1_2.c + Hash Cond: ((t1_2.a = t2_2.b) AND (ltrim(t1_2.c, 'A'::text) = ltrim(t2_2.c, 'A'::text))) + -> Seq Scan on public.plt1_e_p3 t1_2 + Output: t1_2.a, t1_2.c + -> Hash + Output: t2_2.b, t2_2.c + -> Seq Scan on public.plt2_e_p3 t2_2 + Output: t2_2.b, t2_2.c + Filter: ((t2_2.b % 25) = 0) +(36 rows) + +SELECT t1.a, t1.c, t2.b, t2.c FROM plt1_e t1 RIGHT JOIN plt2_e t2 ON t1.a = t2.b AND ltrim(t1.c, 'A') = ltrim(t2.c, 'A') WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b; + a | c | b | c +-----+-------+-----+------- + 0 | A0000 | 0 | A0000 + 150 | A0003 | 150 | A0003 + 300 | A0006 | 300 | A0006 + 450 | A0009 | 450 | A0009 + | | 75 | A0001 + | | 225 | A0004 + | | 375 | A0007 + | | 525 | A0010 +(8 rows) + +SELECT t1.a, t1.c, t2.b, t2.c FROM uplt1_e t1 RIGHT JOIN uplt2_e t2 ON t1.a = t2.b AND ltrim(t1.c, 'A') = ltrim(t2.c, 'A') WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b; + a | c | b | c +-----+-------+-----+------- + 0 | A0000 | 0 | A0000 + 150 | A0003 | 150 | A0003 + 300 | A0006 | 300 | A0006 + 450 | A0009 | 450 | A0009 + | | 75 | A0001 + | | 225 | A0004 + | | 375 | A0007 + | | 525 | A0010 +(8 rows) + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM plt1_e WHERE plt1_e.a % 25 = 0) t1 FULL JOIN (SELECT * FROM plt2_e WHERE plt2_e.b % 25 = 0) t2 ON (t1.a = t2.b AND ltrim(t1.c, 'A') = ltrim(t2.c, 'A')) ORDER BY t1.a, t2.b; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------- + Sort + Output: plt1_e_p1.a, plt1_e_p1.c, plt2_e_p1.b, plt2_e_p1.c + Sort Key: plt1_e_p1.a, plt2_e_p1.b + -> Append + -> Hash Full Join + Output: plt1_e_p1.a, plt1_e_p1.c, plt2_e_p1.b, plt2_e_p1.c + Hash Cond: ((plt1_e_p1.a = plt2_e_p1.b) AND (ltrim(plt1_e_p1.c, 'A'::text) = ltrim(plt2_e_p1.c, 'A'::text))) + -> Seq Scan on public.plt1_e_p1 + Output: plt1_e_p1.a, plt1_e_p1.c + Filter: ((plt1_e_p1.a % 25) = 0) + -> Hash + Output: plt2_e_p1.b, plt2_e_p1.c + -> Seq Scan on public.plt2_e_p1 + Output: plt2_e_p1.b, plt2_e_p1.c + Filter: ((plt2_e_p1.b % 25) = 0) + -> Hash Full Join + Output: plt1_e_p2.a, plt1_e_p2.c, plt2_e_p2.b, plt2_e_p2.c + Hash Cond: ((plt1_e_p2.a = plt2_e_p2.b) AND (ltrim(plt1_e_p2.c, 'A'::text) = ltrim(plt2_e_p2.c, 'A'::text))) + -> Seq Scan on public.plt1_e_p2 + Output: plt1_e_p2.a, plt1_e_p2.c + Filter: ((plt1_e_p2.a % 25) = 0) + -> Hash + Output: plt2_e_p2.b, plt2_e_p2.c + -> Seq Scan on public.plt2_e_p2 + Output: plt2_e_p2.b, plt2_e_p2.c + Filter: ((plt2_e_p2.b % 25) = 0) + -> Hash Full Join + Output: plt1_e_p3.a, plt1_e_p3.c, plt2_e_p3.b, plt2_e_p3.c + Hash Cond: ((plt1_e_p3.a = plt2_e_p3.b) AND (ltrim(plt1_e_p3.c, 'A'::text) = ltrim(plt2_e_p3.c, 'A'::text))) + -> Seq Scan on public.plt1_e_p3 + Output: plt1_e_p3.a, plt1_e_p3.c + Filter: ((plt1_e_p3.a % 25) = 0) + -> Hash + Output: plt2_e_p3.b, plt2_e_p3.c + -> Seq Scan on public.plt2_e_p3 + Output: plt2_e_p3.b, plt2_e_p3.c + Filter: ((plt2_e_p3.b % 25) = 0) +(37 rows) + +SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM plt1_e WHERE plt1_e.a % 25 = 0) t1 FULL JOIN (SELECT * FROM plt2_e WHERE plt2_e.b % 25 = 0) t2 ON (t1.a = t2.b AND ltrim(t1.c, 'A') = ltrim(t2.c, 'A')) ORDER BY t1.a, t2.b; + a | c | b | c +-----+-------+-----+------- + 0 | A0000 | 0 | A0000 + 50 | A0001 | | + 100 | A0002 | | + 150 | A0003 | 150 | A0003 + 200 | A0004 | | + 250 | A0005 | | + 300 | A0006 | 300 | A0006 + 350 | A0007 | | + 400 | A0008 | | + 450 | A0009 | 450 | A0009 + 500 | A0010 | | + 550 | A0011 | | + | | 75 | A0001 + | | 225 | A0004 + | | 375 | A0007 + | | 525 | A0010 +(16 rows) + +SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM uplt1_e t1 WHERE t1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM uplt2_e t2 WHERE t2.b % 25 = 0) t2 ON (t1.a = t2.b AND ltrim(t1.c, 'A') = ltrim(t2.c, 'A')) ORDER BY t1.a, t2.b; + a | c | b | c +-----+-------+-----+------- + 0 | A0000 | 0 | A0000 + 50 | A0001 | | + 100 | A0002 | | + 150 | A0003 | 150 | A0003 + 200 | A0004 | | + 250 | A0005 | | + 300 | A0006 | 300 | A0006 + 350 | A0007 | | + 400 | A0008 | | + 450 | A0009 | 450 | A0009 + 500 | A0010 | | + 550 | A0011 | | + | | 75 | A0001 + | | 225 | A0004 + | | 375 | A0007 + | | 525 | A0010 +(16 rows) + +-- +-- N-way join +-- +EXPLAIN (VERBOSE, 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.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 +------------------------------------------------------------------------------------------ + Sort + Output: (avg(t1.a)), (avg(t2.b)), (avg((t3.a + t3.b))), t1.c, t2.c, t3.c + Sort Key: t1.c, t3.c + -> HashAggregate + Output: avg(t1.a), avg(t2.b), avg((t3.a + t3.b)), t1.c, t2.c, t3.c + Group Key: t1.c, t2.c, t3.c + -> Result + Output: t1.c, t2.c, t3.c, t1.a, t2.b, t3.a, t3.b + -> Append + -> Hash Join + Output: t1.a, t1.c, t2.b, t2.c, t3.a, t3.b, t3.c + Hash Cond: (t1.c = t2.c) + -> Seq Scan on public.plt1_p1 t1 + Output: t1.a, t1.c + -> Hash + Output: t2.b, t2.c, t3.a, t3.b, t3.c + -> Hash Join + Output: t2.b, t2.c, t3.a, t3.b, t3.c + Hash Cond: (t2.c = ltrim(t3.c, 'A'::text)) + -> Seq Scan on public.plt2_p1 t2 + Output: t2.b, t2.c + -> Hash + Output: t3.a, t3.b, t3.c + -> Seq Scan on public.plt1_e_p1 t3 + Output: t3.a, t3.b, t3.c + -> Hash Join + Output: t1_1.a, t1_1.c, t2_1.b, t2_1.c, t3_1.a, t3_1.b, t3_1.c + Hash Cond: (t1_1.c = t2_1.c) + -> Seq Scan on public.plt1_p2 t1_1 + Output: t1_1.a, t1_1.c + -> Hash + Output: t2_1.b, t2_1.c, t3_1.a, t3_1.b, t3_1.c + -> Hash Join + Output: t2_1.b, t2_1.c, t3_1.a, t3_1.b, t3_1.c + Hash Cond: (t2_1.c = ltrim(t3_1.c, 'A'::text)) + -> Seq Scan on public.plt2_p2 t2_1 + Output: t2_1.b, t2_1.c + -> Hash + Output: t3_1.a, t3_1.b, t3_1.c + -> Seq Scan on public.plt1_e_p2 t3_1 + Output: t3_1.a, t3_1.b, t3_1.c + -> Hash Join + Output: t1_2.a, t1_2.c, t2_2.b, t2_2.c, t3_2.a, t3_2.b, t3_2.c + Hash Cond: (t1_2.c = t2_2.c) + -> Seq Scan on public.plt1_p3 t1_2 + Output: t1_2.a, t1_2.c + -> Hash + Output: t2_2.b, t2_2.c, t3_2.a, t3_2.b, t3_2.c + -> Hash Join + Output: t2_2.b, t2_2.c, t3_2.a, t3_2.b, t3_2.c + Hash Cond: (t2_2.c = ltrim(t3_2.c, 'A'::text)) + -> Seq Scan on public.plt2_p3 t2_2 + Output: t2_2.b, t2_2.c + -> Hash + Output: t3_2.a, t3_2.b, t3_2.c + -> Seq Scan on public.plt1_e_p3 t3_2 + Output: t3_2.a, t3_2.b, t3_2.c +(57 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.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 + 74.0000000000000000 | 75.0000000000000000 | 148.0000000000000000 | 0001 | 0001 | A0001 + 124.0000000000000000 | 124.5000000000000000 | 248.0000000000000000 | 0002 | 0002 | A0002 + 174.0000000000000000 | 174.0000000000000000 | 348.0000000000000000 | 0003 | 0003 | A0003 + 224.0000000000000000 | 225.0000000000000000 | 448.0000000000000000 | 0004 | 0004 | A0004 + 274.0000000000000000 | 274.5000000000000000 | 548.0000000000000000 | 0005 | 0005 | A0005 + 324.0000000000000000 | 324.0000000000000000 | 648.0000000000000000 | 0006 | 0006 | A0006 + 374.0000000000000000 | 375.0000000000000000 | 748.0000000000000000 | 0007 | 0007 | A0007 + 424.0000000000000000 | 424.5000000000000000 | 848.0000000000000000 | 0008 | 0008 | A0008 + 474.0000000000000000 | 474.0000000000000000 | 948.0000000000000000 | 0009 | 0009 | A0009 + 524.0000000000000000 | 525.0000000000000000 | 1048.0000000000000000 | 0010 | 0010 | A0010 + 574.0000000000000000 | 574.5000000000000000 | 1148.0000000000000000 | 0011 | 0011 | A0011 +(12 rows) + +SELECT avg(t1.a), avg(t2.b), avg(t3.a + t3.b), t1.c, t2.c, t3.c FROM uplt1 t1, uplt2 t2, uplt1_e t3 WHERE 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 + 74.0000000000000000 | 75.0000000000000000 | 148.0000000000000000 | 0001 | 0001 | A0001 + 124.0000000000000000 | 124.5000000000000000 | 248.0000000000000000 | 0002 | 0002 | A0002 + 174.0000000000000000 | 174.0000000000000000 | 348.0000000000000000 | 0003 | 0003 | A0003 + 224.0000000000000000 | 225.0000000000000000 | 448.0000000000000000 | 0004 | 0004 | A0004 + 274.0000000000000000 | 274.5000000000000000 | 548.0000000000000000 | 0005 | 0005 | A0005 + 324.0000000000000000 | 324.0000000000000000 | 648.0000000000000000 | 0006 | 0006 | A0006 + 374.0000000000000000 | 375.0000000000000000 | 748.0000000000000000 | 0007 | 0007 | A0007 + 424.0000000000000000 | 424.5000000000000000 | 848.0000000000000000 | 0008 | 0008 | A0008 + 474.0000000000000000 | 474.0000000000000000 | 948.0000000000000000 | 0009 | 0009 | A0009 + 524.0000000000000000 | 525.0000000000000000 | 1048.0000000000000000 | 0010 | 0010 | A0010 + 574.0000000000000000 | 574.5000000000000000 | 1148.0000000000000000 | 0011 | 0011 | A0011 +(12 rows) + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (plt1 t1 LEFT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c) LEFT JOIN plt1_e t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; + QUERY PLAN +-------------------------------------------------------------------------------------------- + Sort + Output: t1.a, t1.c, t2.b, t2.c, ((t3.a + t3.b)), t3.c + Sort Key: t1.a, t2.b, ((t3.a + t3.b)) + -> Result + Output: t1.a, t1.c, t2.b, t2.c, (t3.a + t3.b), t3.c + -> Append + -> Hash Right Join + Output: t1.a, t1.c, t2.b, t2.c, t3.a, t3.b, t3.c + Hash Cond: ((t3.a = t1.a) AND (ltrim(t3.c, 'A'::text) = t1.c)) + -> Seq Scan on public.plt1_e_p1 t3 + Output: t3.a, t3.b, t3.c + -> Hash + Output: t1.a, t1.c, t2.b, t2.c + -> Hash Right Join + Output: t1.a, t1.c, t2.b, t2.c + Hash Cond: ((t2.b = t1.a) AND (t2.c = t1.c)) + -> Seq Scan on public.plt2_p1 t2 + Output: t2.b, t2.c + -> Hash + Output: t1.a, t1.c + -> Seq Scan on public.plt1_p1 t1 + Output: t1.a, t1.c + Filter: ((t1.a % 25) = 0) + -> Hash Right Join + Output: t1_1.a, t1_1.c, t2_1.b, t2_1.c, t3_1.a, t3_1.b, t3_1.c + Hash Cond: ((t3_1.a = t1_1.a) AND (ltrim(t3_1.c, 'A'::text) = t1_1.c)) + -> Seq Scan on public.plt1_e_p2 t3_1 + Output: t3_1.a, t3_1.b, t3_1.c + -> Hash + Output: t1_1.a, t1_1.c, t2_1.b, t2_1.c + -> Hash Right Join + Output: t1_1.a, t1_1.c, t2_1.b, t2_1.c + Hash Cond: ((t2_1.b = t1_1.a) AND (t2_1.c = t1_1.c)) + -> Seq Scan on public.plt2_p2 t2_1 + Output: t2_1.b, t2_1.c + -> Hash + Output: t1_1.a, t1_1.c + -> Seq Scan on public.plt1_p2 t1_1 + Output: t1_1.a, t1_1.c + Filter: ((t1_1.a % 25) = 0) + -> Hash Right Join + Output: t1_2.a, t1_2.c, t2_2.b, t2_2.c, t3_2.a, t3_2.b, t3_2.c + Hash Cond: ((t3_2.a = t1_2.a) AND (ltrim(t3_2.c, 'A'::text) = t1_2.c)) + -> Seq Scan on public.plt1_e_p3 t3_2 + Output: t3_2.a, t3_2.b, t3_2.c + -> Hash + Output: t1_2.a, t1_2.c, t2_2.b, t2_2.c + -> Hash Right Join + Output: t1_2.a, t1_2.c, t2_2.b, t2_2.c + Hash Cond: ((t2_2.b = t1_2.a) AND (t2_2.c = t1_2.c)) + -> Seq Scan on public.plt2_p3 t2_2 + Output: t2_2.b, t2_2.c + -> Hash + Output: t1_2.a, t1_2.c + -> Seq Scan on public.plt1_p3 t1_2 + Output: t1_2.a, t1_2.c + Filter: ((t1_2.a % 25) = 0) +(57 rows) + +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (plt1 t1 LEFT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c) LEFT JOIN plt1_e t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; + a | c | b | c | ?column? | c +-----+------+-----+------+----------+------- + 0 | 0000 | 0 | 0000 | 0 | A0000 + 50 | 0001 | | | 100 | A0001 + 100 | 0002 | | | 200 | A0002 + 150 | 0003 | 150 | 0003 | 300 | A0003 + 200 | 0004 | | | 400 | A0004 + 250 | 0005 | | | 500 | A0005 + 300 | 0006 | 300 | 0006 | 600 | A0006 + 350 | 0007 | | | 700 | A0007 + 400 | 0008 | | | 800 | A0008 + 450 | 0009 | 450 | 0009 | 900 | A0009 + 500 | 0010 | | | 1000 | A0010 + 550 | 0011 | | | 1100 | A0011 +(12 rows) + +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uplt1 t1 LEFT JOIN uplt2 t2 ON t1.a = t2.b AND t1.c = t2.c) LEFT JOIN uplt1_e t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; + a | c | b | c | ?column? | c +-----+------+-----+------+----------+------- + 0 | 0000 | 0 | 0000 | 0 | A0000 + 50 | 0001 | | | 100 | A0001 + 100 | 0002 | | | 200 | A0002 + 150 | 0003 | 150 | 0003 | 300 | A0003 + 200 | 0004 | | | 400 | A0004 + 250 | 0005 | | | 500 | A0005 + 300 | 0006 | 300 | 0006 | 600 | A0006 + 350 | 0007 | | | 700 | A0007 + 400 | 0008 | | | 800 | A0008 + 450 | 0009 | 450 | 0009 | 900 | A0009 + 500 | 0010 | | | 1000 | A0010 + 550 | 0011 | | | 1100 | A0011 +(12 rows) + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (plt1 t1 LEFT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c) LEFT JOIN plt1_e t3 ON (t2.b = t3.a AND t2.c = ltrim(t3.c, 'A')) WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; + QUERY PLAN +-------------------------------------------------------------------------------------------- + Sort + Output: t1.a, t1.c, t2.b, t2.c, ((t3.a + t3.b)), t3.c + Sort Key: t1.a, t2.b, ((t3.a + t3.b)) + -> Result + Output: t1.a, t1.c, t2.b, t2.c, (t3.a + t3.b), t3.c + -> Append + -> Hash Right Join + Output: t1.a, t1.c, t2.b, t2.c, t3.a, t3.b, t3.c + Hash Cond: ((t3.a = t2.b) AND (ltrim(t3.c, 'A'::text) = t2.c)) + -> Seq Scan on public.plt1_e_p1 t3 + Output: t3.a, t3.b, t3.c + -> Hash + Output: t1.a, t1.c, t2.b, t2.c + -> Hash Right Join + Output: t1.a, t1.c, t2.b, t2.c + Hash Cond: ((t2.b = t1.a) AND (t2.c = t1.c)) + -> Seq Scan on public.plt2_p1 t2 + Output: t2.b, t2.c + -> Hash + Output: t1.a, t1.c + -> Seq Scan on public.plt1_p1 t1 + Output: t1.a, t1.c + Filter: ((t1.a % 25) = 0) + -> Hash Right Join + Output: t1_1.a, t1_1.c, t2_1.b, t2_1.c, t3_1.a, t3_1.b, t3_1.c + Hash Cond: ((t3_1.a = t2_1.b) AND (ltrim(t3_1.c, 'A'::text) = t2_1.c)) + -> Seq Scan on public.plt1_e_p2 t3_1 + Output: t3_1.a, t3_1.b, t3_1.c + -> Hash + Output: t1_1.a, t1_1.c, t2_1.b, t2_1.c + -> Hash Right Join + Output: t1_1.a, t1_1.c, t2_1.b, t2_1.c + Hash Cond: ((t2_1.b = t1_1.a) AND (t2_1.c = t1_1.c)) + -> Seq Scan on public.plt2_p2 t2_1 + Output: t2_1.b, t2_1.c + -> Hash + Output: t1_1.a, t1_1.c + -> Seq Scan on public.plt1_p2 t1_1 + Output: t1_1.a, t1_1.c + Filter: ((t1_1.a % 25) = 0) + -> Hash Right Join + Output: t1_2.a, t1_2.c, t2_2.b, t2_2.c, t3_2.a, t3_2.b, t3_2.c + Hash Cond: ((t3_2.a = t2_2.b) AND (ltrim(t3_2.c, 'A'::text) = t2_2.c)) + -> Seq Scan on public.plt1_e_p3 t3_2 + Output: t3_2.a, t3_2.b, t3_2.c + -> Hash + Output: t1_2.a, t1_2.c, t2_2.b, t2_2.c + -> Hash Right Join + Output: t1_2.a, t1_2.c, t2_2.b, t2_2.c + Hash Cond: ((t2_2.b = t1_2.a) AND (t2_2.c = t1_2.c)) + -> Seq Scan on public.plt2_p3 t2_2 + Output: t2_2.b, t2_2.c + -> Hash + Output: t1_2.a, t1_2.c + -> Seq Scan on public.plt1_p3 t1_2 + Output: t1_2.a, t1_2.c + Filter: ((t1_2.a % 25) = 0) +(57 rows) + +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (plt1 t1 LEFT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c) LEFT JOIN plt1_e t3 ON (t2.b = t3.a AND t2.c = ltrim(t3.c, 'A')) WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; + a | c | b | c | ?column? | c +-----+------+-----+------+----------+------- + 0 | 0000 | 0 | 0000 | 0 | A0000 + 50 | 0001 | | | | + 100 | 0002 | | | | + 150 | 0003 | 150 | 0003 | 300 | A0003 + 200 | 0004 | | | | + 250 | 0005 | | | | + 300 | 0006 | 300 | 0006 | 600 | A0006 + 350 | 0007 | | | | + 400 | 0008 | | | | + 450 | 0009 | 450 | 0009 | 900 | A0009 + 500 | 0010 | | | | + 550 | 0011 | | | | +(12 rows) + +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uplt1 t1 LEFT JOIN uplt2 t2 ON t1.a = t2.b AND t1.c = t2.c) LEFT JOIN uplt1_e t3 ON (t2.b = t3.a AND t2.c = ltrim(t3.c, 'A')) WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; + a | c | b | c | ?column? | c +-----+------+-----+------+----------+------- + 0 | 0000 | 0 | 0000 | 0 | A0000 + 50 | 0001 | | | | + 100 | 0002 | | | | + 150 | 0003 | 150 | 0003 | 300 | A0003 + 200 | 0004 | | | | + 250 | 0005 | | | | + 300 | 0006 | 300 | 0006 | 600 | A0006 + 350 | 0007 | | | | + 400 | 0008 | | | | + 450 | 0009 | 450 | 0009 | 900 | A0009 + 500 | 0010 | | | | + 550 | 0011 | | | | +(12 rows) + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (plt1 t1 LEFT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c) RIGHT JOIN plt1_e t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; + QUERY PLAN +-------------------------------------------------------------------------------------------------------- + Sort + Output: t1.a, t1.c, t2.b, t2.c, ((t3.a + t3.b)), t3.c + Sort Key: t1.a, t2.b, ((t3.a + t3.b)) + -> Result + Output: t1.a, t1.c, t2.b, t2.c, (t3.a + t3.b), t3.c + -> Append + -> Hash Right Join + Output: t3.a, t3.b, t3.c, t1.a, t1.c, t2.b, t2.c + Hash Cond: ((t2.b = t1.a) AND (t2.c = t1.c)) + -> Seq Scan on public.plt2_p1 t2 + Output: t2.b, t2.c + -> Hash + Output: t3.a, t3.b, t3.c, t1.a, t1.c + -> Hash Right Join + Output: t3.a, t3.b, t3.c, t1.a, t1.c + Hash Cond: ((t1.c = ltrim(t3.c, 'A'::text)) AND (t1.a = t3.a)) + -> Seq Scan on public.plt1_p1 t1 + Output: t1.a, t1.c + -> Hash + Output: t3.a, t3.b, t3.c + -> Seq Scan on public.plt1_e_p1 t3 + Output: t3.a, t3.b, t3.c + Filter: ((t3.a % 25) = 0) + -> Hash Right Join + Output: t3_1.a, t3_1.b, t3_1.c, t1_1.a, t1_1.c, t2_1.b, t2_1.c + Hash Cond: ((t2_1.b = t1_1.a) AND (t2_1.c = t1_1.c)) + -> Seq Scan on public.plt2_p2 t2_1 + Output: t2_1.b, t2_1.c + -> Hash + Output: t3_1.a, t3_1.b, t3_1.c, t1_1.a, t1_1.c + -> Hash Right Join + Output: t3_1.a, t3_1.b, t3_1.c, t1_1.a, t1_1.c + Hash Cond: ((t1_1.c = ltrim(t3_1.c, 'A'::text)) AND (t1_1.a = t3_1.a)) + -> Seq Scan on public.plt1_p2 t1_1 + Output: t1_1.a, t1_1.c + -> Hash + Output: t3_1.a, t3_1.b, t3_1.c + -> Seq Scan on public.plt1_e_p2 t3_1 + Output: t3_1.a, t3_1.b, t3_1.c + Filter: ((t3_1.a % 25) = 0) + -> Hash Right Join + Output: t3_2.a, t3_2.b, t3_2.c, t1_2.a, t1_2.c, t2_2.b, t2_2.c + Hash Cond: ((t2_2.b = t1_2.a) AND (t2_2.c = t1_2.c)) + -> Seq Scan on public.plt2_p3 t2_2 + Output: t2_2.b, t2_2.c + -> Hash + Output: t3_2.a, t3_2.b, t3_2.c, t1_2.a, t1_2.c + -> Hash Right Join + Output: t3_2.a, t3_2.b, t3_2.c, t1_2.a, t1_2.c + Hash Cond: ((t1_2.c = ltrim(t3_2.c, 'A'::text)) AND (t1_2.a = t3_2.a)) + -> Seq Scan on public.plt1_p3 t1_2 + Output: t1_2.a, t1_2.c + -> Hash + Output: t3_2.a, t3_2.b, t3_2.c + -> Seq Scan on public.plt1_e_p3 t3_2 + Output: t3_2.a, t3_2.b, t3_2.c + Filter: ((t3_2.a % 25) = 0) +(57 rows) + +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (plt1 t1 LEFT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c) RIGHT JOIN plt1_e t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; + a | c | b | c | ?column? | c +-----+------+-----+------+----------+------- + 0 | 0000 | 0 | 0000 | 0 | A0000 + 50 | 0001 | | | 100 | A0001 + 100 | 0002 | | | 200 | A0002 + 150 | 0003 | 150 | 0003 | 300 | A0003 + 200 | 0004 | | | 400 | A0004 + 250 | 0005 | | | 500 | A0005 + 300 | 0006 | 300 | 0006 | 600 | A0006 + 350 | 0007 | | | 700 | A0007 + 400 | 0008 | | | 800 | A0008 + 450 | 0009 | 450 | 0009 | 900 | A0009 + 500 | 0010 | | | 1000 | A0010 + 550 | 0011 | | | 1100 | A0011 +(12 rows) + +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uplt1 t1 LEFT JOIN uplt2 t2 ON t1.a = t2.b AND t1.c = t2.c) RIGHT JOIN uplt1_e t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; + a | c | b | c | ?column? | c +-----+------+-----+------+----------+------- + 0 | 0000 | 0 | 0000 | 0 | A0000 + 50 | 0001 | | | 100 | A0001 + 100 | 0002 | | | 200 | A0002 + 150 | 0003 | 150 | 0003 | 300 | A0003 + 200 | 0004 | | | 400 | A0004 + 250 | 0005 | | | 500 | A0005 + 300 | 0006 | 300 | 0006 | 600 | A0006 + 350 | 0007 | | | 700 | A0007 + 400 | 0008 | | | 800 | A0008 + 450 | 0009 | 450 | 0009 | 900 | A0009 + 500 | 0010 | | | 1000 | A0010 + 550 | 0011 | | | 1100 | A0011 +(12 rows) + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (plt1 t1 RIGHT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c) RIGHT JOIN plt1_e t3 ON (t2.b = t3.a AND t2.c = ltrim(t3.c, 'A')) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; + QUERY PLAN +-------------------------------------------------------------------------------------------------------- + Sort + Output: t1.a, t1.c, t2.b, t2.c, ((t3.a + t3.b)), t3.c + Sort Key: t1.a, t2.b, ((t3.a + t3.b)) + -> Result + Output: t1.a, t1.c, t2.b, t2.c, (t3.a + t3.b), t3.c + -> Append + -> Hash Right Join + Output: t3.a, t3.b, t3.c, t2.b, t2.c, t1.a, t1.c + Hash Cond: ((t1.a = t2.b) AND (t1.c = t2.c)) + -> Seq Scan on public.plt1_p1 t1 + Output: t1.a, t1.c + -> Hash + Output: t3.a, t3.b, t3.c, t2.b, t2.c + -> Hash Right Join + Output: t3.a, t3.b, t3.c, t2.b, t2.c + Hash Cond: ((t2.b = t3.a) AND (t2.c = ltrim(t3.c, 'A'::text))) + -> Seq Scan on public.plt2_p1 t2 + Output: t2.b, t2.c + -> Hash + Output: t3.a, t3.b, t3.c + -> Seq Scan on public.plt1_e_p1 t3 + Output: t3.a, t3.b, t3.c + Filter: ((t3.a % 25) = 0) + -> Hash Right Join + Output: t3_1.a, t3_1.b, t3_1.c, t2_1.b, t2_1.c, t1_1.a, t1_1.c + Hash Cond: ((t1_1.a = t2_1.b) AND (t1_1.c = t2_1.c)) + -> Seq Scan on public.plt1_p2 t1_1 + Output: t1_1.a, t1_1.c + -> Hash + Output: t3_1.a, t3_1.b, t3_1.c, t2_1.b, t2_1.c + -> Hash Right Join + Output: t3_1.a, t3_1.b, t3_1.c, t2_1.b, t2_1.c + Hash Cond: ((t2_1.b = t3_1.a) AND (t2_1.c = ltrim(t3_1.c, 'A'::text))) + -> Seq Scan on public.plt2_p2 t2_1 + Output: t2_1.b, t2_1.c + -> Hash + Output: t3_1.a, t3_1.b, t3_1.c + -> Seq Scan on public.plt1_e_p2 t3_1 + Output: t3_1.a, t3_1.b, t3_1.c + Filter: ((t3_1.a % 25) = 0) + -> Hash Right Join + Output: t3_2.a, t3_2.b, t3_2.c, t2_2.b, t2_2.c, t1_2.a, t1_2.c + Hash Cond: ((t1_2.a = t2_2.b) AND (t1_2.c = t2_2.c)) + -> Seq Scan on public.plt1_p3 t1_2 + Output: t1_2.a, t1_2.c + -> Hash + Output: t3_2.a, t3_2.b, t3_2.c, t2_2.b, t2_2.c + -> Hash Right Join + Output: t3_2.a, t3_2.b, t3_2.c, t2_2.b, t2_2.c + Hash Cond: ((t2_2.b = t3_2.a) AND (t2_2.c = ltrim(t3_2.c, 'A'::text))) + -> Seq Scan on public.plt2_p3 t2_2 + Output: t2_2.b, t2_2.c + -> Hash + Output: t3_2.a, t3_2.b, t3_2.c + -> Seq Scan on public.plt1_e_p3 t3_2 + Output: t3_2.a, t3_2.b, t3_2.c + Filter: ((t3_2.a % 25) = 0) +(57 rows) + +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (plt1 t1 RIGHT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c) RIGHT JOIN plt1_e t3 ON (t2.b = t3.a AND t2.c = ltrim(t3.c, 'A')) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; + a | c | b | c | ?column? | c +-----+------+-----+------+----------+------- + 0 | 0000 | 0 | 0000 | 0 | A0000 + 150 | 0003 | 150 | 0003 | 300 | A0003 + 300 | 0006 | 300 | 0006 | 600 | A0006 + 450 | 0009 | 450 | 0009 | 900 | A0009 + | | | | 100 | A0001 + | | | | 200 | A0002 + | | | | 400 | A0004 + | | | | 500 | A0005 + | | | | 700 | A0007 + | | | | 800 | A0008 + | | | | 1000 | A0010 + | | | | 1100 | A0011 +(12 rows) + +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uplt1 t1 RIGHT JOIN uplt2 t2 ON t1.a = t2.b AND t1.c = t2.c) RIGHT JOIN uplt1_e t3 ON (t2.b = t3.a AND t2.c = ltrim(t3.c, 'A')) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; + a | c | b | c | ?column? | c +-----+------+-----+------+----------+------- + 0 | 0000 | 0 | 0000 | 0 | A0000 + 150 | 0003 | 150 | 0003 | 300 | A0003 + 300 | 0006 | 300 | 0006 | 600 | A0006 + 450 | 0009 | 450 | 0009 | 900 | A0009 + | | | | 100 | A0001 + | | | | 200 | A0002 + | | | | 400 | A0004 + | | | | 500 | A0005 + | | | | 700 | A0007 + | | | | 800 | A0008 + | | | | 1000 | A0010 + | | | | 1100 | A0011 +(12 rows) + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM ((SELECT * FROM plt1 WHERE plt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM plt2 WHERE plt2.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c)) FULL JOIN (SELECT * FROM plt1_e WHERE plt1_e.a % 25 = 0) t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) ORDER BY t1.a, t2.b, t3.a + t3.b; + QUERY PLAN +--------------------------------------------------------------------------------------------------------------- + Sort + Output: plt1_p1.a, plt1_p1.c, plt2_p1.b, plt2_p1.c, ((plt1_e_p1.a + plt1_e_p1.b)), plt1_e_p1.c + Sort Key: plt1_p1.a, plt2_p1.b, ((plt1_e_p1.a + plt1_e_p1.b)) + -> Result + Output: plt1_p1.a, plt1_p1.c, plt2_p1.b, plt2_p1.c, (plt1_e_p1.a + plt1_e_p1.b), plt1_e_p1.c + -> Append + -> Hash Full Join + Output: plt1_p1.a, plt1_p1.c, plt2_p1.b, plt2_p1.c, plt1_e_p1.a, plt1_e_p1.b, plt1_e_p1.c + Hash Cond: ((plt1_p1.a = plt1_e_p1.a) AND (plt1_p1.c = ltrim(plt1_e_p1.c, 'A'::text))) + -> Hash Full Join + Output: plt1_p1.a, plt1_p1.c, plt2_p1.b, plt2_p1.c + Hash Cond: ((plt1_p1.a = plt2_p1.b) AND (plt1_p1.c = plt2_p1.c)) + -> Seq Scan on public.plt1_p1 + Output: plt1_p1.a, plt1_p1.c + Filter: ((plt1_p1.a % 25) = 0) + -> Hash + Output: plt2_p1.b, plt2_p1.c + -> Seq Scan on public.plt2_p1 + Output: plt2_p1.b, plt2_p1.c + Filter: ((plt2_p1.b % 25) = 0) + -> Hash + Output: plt1_e_p1.a, plt1_e_p1.b, plt1_e_p1.c + -> Seq Scan on public.plt1_e_p1 + Output: plt1_e_p1.a, plt1_e_p1.b, plt1_e_p1.c + Filter: ((plt1_e_p1.a % 25) = 0) + -> Hash Full Join + Output: plt1_p2.a, plt1_p2.c, plt2_p2.b, plt2_p2.c, plt1_e_p2.a, plt1_e_p2.b, plt1_e_p2.c + Hash Cond: ((plt1_p2.a = plt1_e_p2.a) AND (plt1_p2.c = ltrim(plt1_e_p2.c, 'A'::text))) + -> Hash Full Join + Output: plt1_p2.a, plt1_p2.c, plt2_p2.b, plt2_p2.c + Hash Cond: ((plt1_p2.a = plt2_p2.b) AND (plt1_p2.c = plt2_p2.c)) + -> Seq Scan on public.plt1_p2 + Output: plt1_p2.a, plt1_p2.c + Filter: ((plt1_p2.a % 25) = 0) + -> Hash + Output: plt2_p2.b, plt2_p2.c + -> Seq Scan on public.plt2_p2 + Output: plt2_p2.b, plt2_p2.c + Filter: ((plt2_p2.b % 25) = 0) + -> Hash + Output: plt1_e_p2.a, plt1_e_p2.b, plt1_e_p2.c + -> Seq Scan on public.plt1_e_p2 + Output: plt1_e_p2.a, plt1_e_p2.b, plt1_e_p2.c + Filter: ((plt1_e_p2.a % 25) = 0) + -> Hash Full Join + Output: plt1_p3.a, plt1_p3.c, plt2_p3.b, plt2_p3.c, plt1_e_p3.a, plt1_e_p3.b, plt1_e_p3.c + Hash Cond: ((plt1_p3.a = plt1_e_p3.a) AND (plt1_p3.c = ltrim(plt1_e_p3.c, 'A'::text))) + -> Hash Full Join + Output: plt1_p3.a, plt1_p3.c, plt2_p3.b, plt2_p3.c + Hash Cond: ((plt1_p3.a = plt2_p3.b) AND (plt1_p3.c = plt2_p3.c)) + -> Seq Scan on public.plt1_p3 + Output: plt1_p3.a, plt1_p3.c + Filter: ((plt1_p3.a % 25) = 0) + -> Hash + Output: plt2_p3.b, plt2_p3.c + -> Seq Scan on public.plt2_p3 + Output: plt2_p3.b, plt2_p3.c + Filter: ((plt2_p3.b % 25) = 0) + -> Hash + Output: plt1_e_p3.a, plt1_e_p3.b, plt1_e_p3.c + -> Seq Scan on public.plt1_e_p3 + Output: plt1_e_p3.a, plt1_e_p3.b, plt1_e_p3.c + Filter: ((plt1_e_p3.a % 25) = 0) +(63 rows) + +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM ((SELECT * FROM plt1 WHERE plt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM plt2 WHERE plt2.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c)) FULL JOIN (SELECT * FROM plt1_e WHERE plt1_e.a % 25 = 0) t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) ORDER BY t1.a, t2.b, t3.a + t3.b; + a | c | b | c | ?column? | c +-----+------+-----+------+----------+------- + 0 | 0000 | 0 | 0000 | 0 | A0000 + 50 | 0001 | | | 100 | A0001 + 100 | 0002 | | | 200 | A0002 + 150 | 0003 | 150 | 0003 | 300 | A0003 + 200 | 0004 | | | 400 | A0004 + 250 | 0005 | | | 500 | A0005 + 300 | 0006 | 300 | 0006 | 600 | A0006 + 350 | 0007 | | | 700 | A0007 + 400 | 0008 | | | 800 | A0008 + 450 | 0009 | 450 | 0009 | 900 | A0009 + 500 | 0010 | | | 1000 | A0010 + 550 | 0011 | | | 1100 | A0011 + | | 75 | 0001 | | + | | 225 | 0004 | | + | | 375 | 0007 | | + | | 525 | 0010 | | +(16 rows) + +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM ((SELECT * FROM uplt1 WHERE uplt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM uplt2 WHERE uplt2.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c)) FULL JOIN (SELECT * FROM uplt1_e WHERE uplt1_e.a % 25 = 0) t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) ORDER BY t1.a, t2.b, t3.a + t3.b; + a | c | b | c | ?column? | c +-----+------+-----+------+----------+------- + 0 | 0000 | 0 | 0000 | 0 | A0000 + 50 | 0001 | | | 100 | A0001 + 100 | 0002 | | | 200 | A0002 + 150 | 0003 | 150 | 0003 | 300 | A0003 + 200 | 0004 | | | 400 | A0004 + 250 | 0005 | | | 500 | A0005 + 300 | 0006 | 300 | 0006 | 600 | A0006 + 350 | 0007 | | | 700 | A0007 + 400 | 0008 | | | 800 | A0008 + 450 | 0009 | 450 | 0009 | 900 | A0009 + 500 | 0010 | | | 1000 | A0010 + 550 | 0011 | | | 1100 | A0011 + | | 75 | 0001 | | + | | 225 | 0004 | | + | | 375 | 0007 | | + | | 525 | 0010 | | +(16 rows) + +-- Semi-join +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.* FROM plt1 t1 WHERE t1.c IN (SELECT t1.c FROM plt2 t1, plt1_e t2 WHERE t1.c = ltrim(t2.c, 'A')) AND t1.a % 25 = 0 ORDER BY t1.a; + QUERY PLAN +-------------------------------------------------------------------- + Sort + Output: t1.a, t1.b, t1.c + Sort Key: t1.a + -> Append + -> Nested Loop Semi Join + Output: t1.a, t1.b, t1.c + Join Filter: (t1.c = t1_3.c) + -> Seq Scan on public.plt1_p1 t1 + Output: t1.a, t1.b, t1.c + Filter: ((t1.a % 25) = 0) + -> Hash Join + Output: t1_3.c, t2.c + Hash Cond: (t1_3.c = ltrim(t2.c, 'A'::text)) + -> Seq Scan on public.plt2_p1 t1_3 + Output: t1_3.c + -> Hash + Output: t2.c + -> Seq Scan on public.plt1_e_p1 t2 + Output: t2.c + -> Nested Loop Semi Join + Output: t1_1.a, t1_1.b, t1_1.c + Join Filter: (t1_1.c = t1_4.c) + -> Seq Scan on public.plt1_p2 t1_1 + Output: t1_1.a, t1_1.b, t1_1.c + Filter: ((t1_1.a % 25) = 0) + -> Hash Join + Output: t1_4.c, t2_1.c + Hash Cond: (t1_4.c = ltrim(t2_1.c, 'A'::text)) + -> Seq Scan on public.plt2_p2 t1_4 + Output: t1_4.c + -> Hash + Output: t2_1.c + -> Seq Scan on public.plt1_e_p2 t2_1 + Output: t2_1.c + -> Nested Loop Semi Join + Output: t1_2.a, t1_2.b, t1_2.c + Join Filter: (t1_2.c = t1_5.c) + -> Seq Scan on public.plt1_p3 t1_2 + Output: t1_2.a, t1_2.b, t1_2.c + Filter: ((t1_2.a % 25) = 0) + -> Hash Join + Output: t1_5.c, t2_2.c + Hash Cond: (t1_5.c = ltrim(t2_2.c, 'A'::text)) + -> Seq Scan on public.plt2_p3 t1_5 + Output: t1_5.c + -> Hash + Output: t2_2.c + -> Seq Scan on public.plt1_e_p3 t2_2 + Output: t2_2.c +(49 rows) + +SELECT t1.* FROM plt1 t1 WHERE t1.c IN (SELECT t1.c FROM plt2 t1, plt1_e t2 WHERE t1.c = ltrim(t2.c, 'A')) AND t1.a % 25 = 0 ORDER BY t1.a; + a | b | c +-----+-----+------ + 0 | 0 | 0000 + 50 | 50 | 0001 + 100 | 100 | 0002 + 150 | 150 | 0003 + 200 | 200 | 0004 + 250 | 250 | 0005 + 300 | 300 | 0006 + 350 | 350 | 0007 + 400 | 400 | 0008 + 450 | 450 | 0009 + 500 | 500 | 0010 + 550 | 550 | 0011 +(12 rows) + +SELECT t1.* FROM uplt1 t1 WHERE t1.c IN (SELECT t1.c FROM uplt2 t1, uplt1_e t2 WHERE t1.c = ltrim(t2.c, 'A')) AND t1.a % 25 = 0 ORDER BY t1.a; + a | b | c +-----+-----+------ + 0 | 0 | 0000 + 50 | 50 | 0001 + 100 | 100 | 0002 + 150 | 150 | 0003 + 200 | 200 | 0004 + 250 | 250 | 0005 + 300 | 300 | 0006 + 350 | 350 | 0007 + 400 | 400 | 0008 + 450 | 450 | 0009 + 500 | 500 | 0010 + 550 | 550 | 0011 +(12 rows) + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.* FROM plt1 t1 WHERE t1.c IN (SELECT t1.c FROM plt2 t1 WHERE t1.c IN (SELECT ltrim(t1.c, 'A') FROM plt1_e t1 WHERE t1.a % 25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a; + QUERY PLAN +-------------------------------------------------------------------------------- + Sort + Output: t1.a, t1.b, t1.c + Sort Key: t1.a + -> Append + -> Nested Loop Semi Join + Output: t1.a, t1.b, t1.c + Join Filter: (t1.c = t1_3.c) + -> Seq Scan on public.plt1_p1 t1 + Output: t1.a, t1.b, t1.c + Filter: ((t1.a % 25) = 0) + -> Hash Join + Output: t1_3.c, t1_6.c + Hash Cond: (t1_3.c = ltrim(t1_6.c, 'A'::text)) + -> Seq Scan on public.plt2_p1 t1_3 + Output: t1_3.c + -> Hash + Output: t1_6.c + -> HashAggregate + Output: t1_6.c + Group Key: ltrim(t1_6.c, 'A'::text) + -> Seq Scan on public.plt1_e_p1 t1_6 + Output: t1_6.c, ltrim(t1_6.c, 'A'::text) + Filter: ((t1_6.a % 25) = 0) + -> Nested Loop Semi Join + Output: t1_1.a, t1_1.b, t1_1.c + Join Filter: (t1_1.c = t1_4.c) + -> Seq Scan on public.plt1_p2 t1_1 + Output: t1_1.a, t1_1.b, t1_1.c + Filter: ((t1_1.a % 25) = 0) + -> Hash Join + Output: t1_4.c, t1_7.c + Hash Cond: (t1_4.c = ltrim(t1_7.c, 'A'::text)) + -> Seq Scan on public.plt2_p2 t1_4 + Output: t1_4.c + -> Hash + Output: t1_7.c + -> HashAggregate + Output: t1_7.c + Group Key: ltrim(t1_7.c, 'A'::text) + -> Seq Scan on public.plt1_e_p2 t1_7 + Output: t1_7.c, ltrim(t1_7.c, 'A'::text) + Filter: ((t1_7.a % 25) = 0) + -> Nested Loop Semi Join + Output: t1_2.a, t1_2.b, t1_2.c + Join Filter: (t1_2.c = t1_5.c) + -> Seq Scan on public.plt1_p3 t1_2 + Output: t1_2.a, t1_2.b, t1_2.c + Filter: ((t1_2.a % 25) = 0) + -> Hash Join + Output: t1_5.c, t1_8.c + Hash Cond: (t1_5.c = ltrim(t1_8.c, 'A'::text)) + -> Seq Scan on public.plt2_p3 t1_5 + Output: t1_5.c + -> Hash + Output: t1_8.c + -> HashAggregate + Output: t1_8.c + Group Key: ltrim(t1_8.c, 'A'::text) + -> Seq Scan on public.plt1_e_p3 t1_8 + Output: t1_8.c, ltrim(t1_8.c, 'A'::text) + Filter: ((t1_8.a % 25) = 0) +(61 rows) + +SELECT t1.* FROM plt1 t1 WHERE t1.c IN (SELECT t1.c FROM plt2 t1 WHERE t1.c IN (SELECT ltrim(t1.c, 'A') FROM plt1_e t1 WHERE t1.a % 25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a; + a | b | c +-----+-----+------ + 0 | 0 | 0000 + 50 | 50 | 0001 + 100 | 100 | 0002 + 150 | 150 | 0003 + 200 | 200 | 0004 + 250 | 250 | 0005 + 300 | 300 | 0006 + 350 | 350 | 0007 + 400 | 400 | 0008 + 450 | 450 | 0009 + 500 | 500 | 0010 + 550 | 550 | 0011 +(12 rows) + +SELECT t1.* FROM uplt1 t1 WHERE t1.c IN (SELECT t1.c FROM uplt2 t1 WHERE t1.c IN (SELECT ltrim(t1.c, 'A') FROM uplt1_e t1 WHERE t1.a % 25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a; + a | b | c +-----+-----+------ + 0 | 0 | 0000 + 50 | 50 | 0001 + 100 | 100 | 0002 + 150 | 150 | 0003 + 200 | 200 | 0004 + 250 | 250 | 0005 + 300 | 300 | 0006 + 350 | 350 | 0007 + 400 | 400 | 0008 + 450 | 450 | 0009 + 500 | 500 | 0010 + 550 | 550 | 0011 +(12 rows) + +-- test merge join with and without index scan +CREATE INDEX iplt1_c on plt1(c); +CREATE INDEX iplt1_p1_c on plt1_p1(c); +CREATE INDEX iplt1_p2_c on plt1_p2(c); +CREATE INDEX iplt1_p3_c on plt1_p3(c); +CREATE INDEX iplt2_c on plt2(c); +CREATE INDEX iplt2_p1_c on plt2_p1(c); +CREATE INDEX iplt2_p2_c on plt2_p2(c); +CREATE INDEX iplt2_p3_c on plt2_p3(c); +CREATE INDEX iplt1_e_c on plt1_e(ltrim(c, 'A')); +CREATE INDEX iplt1_e_p1_c on plt1_e_p1(ltrim(c, 'A')); +CREATE INDEX iplt1_e_p2_c on plt1_e_p2(ltrim(c, 'A')); +CREATE INDEX iplt1_e_p3_c on plt1_e_p3(ltrim(c, 'A')); +ANALYZE plt1; +ANALYZE plt1_p1; +ANALYZE plt1_p2; +ANALYZE plt1_p3; +ANALYZE plt2; +ANALYZE plt2_p1; +ANALYZE plt2_p2; +ANALYZE plt2_p3; +ANALYZE plt1_e; +ANALYZE plt1_e_p1; +ANALYZE plt1_e_p2; +ANALYZE plt1_e_p3; +SET enable_hashjoin TO off; +SET enable_nestloop TO off; +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (plt1 t1 RIGHT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c) RIGHT JOIN plt1_e t3 ON (t2.b = t3.a AND t2.c = ltrim(t3.c, 'A')) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; + QUERY PLAN +----------------------------------------------------------------------------------------------------------- + Sort + Output: t1.a, t1.c, t2.b, t2.c, ((t3.a + t3.b)), t3.c + Sort Key: t1.a, t2.b, ((t3.a + t3.b)) + -> Result + Output: t1.a, t1.c, t2.b, t2.c, (t3.a + t3.b), t3.c + -> Append + -> Merge Left Join + Output: t3.a, t3.b, t3.c, t2.b, t2.c, t1.a, t1.c + Merge Cond: ((t2.b = t1.a) AND (t2.c = t1.c)) + -> Sort + Output: t3.a, t3.b, t3.c, t2.b, t2.c + Sort Key: t2.b, t2.c + -> Merge Left Join + Output: t3.a, t3.b, t3.c, t2.b, t2.c + Merge Cond: ((t3.a = t2.b) AND ((ltrim(t3.c, 'A'::text)) = t2.c)) + -> Sort + Output: t3.a, t3.b, t3.c, (ltrim(t3.c, 'A'::text)) + Sort Key: t3.a, (ltrim(t3.c, 'A'::text)) + -> Seq Scan on public.plt1_e_p1 t3 + Output: t3.a, t3.b, t3.c, ltrim(t3.c, 'A'::text) + Filter: ((t3.a % 25) = 0) + -> Sort + Output: t2.b, t2.c + Sort Key: t2.b, t2.c + -> Seq Scan on public.plt2_p1 t2 + Output: t2.b, t2.c + -> Sort + Output: t1.a, t1.c + Sort Key: t1.a, t1.c + -> Seq Scan on public.plt1_p1 t1 + Output: t1.a, t1.c + -> Merge Left Join + Output: t3_1.a, t3_1.b, t3_1.c, t2_1.b, t2_1.c, t1_1.a, t1_1.c + Merge Cond: ((t2_1.c = t1_1.c) AND (t2_1.b = t1_1.a)) + -> Sort + Output: t3_1.a, t3_1.b, t3_1.c, t2_1.b, t2_1.c + Sort Key: t2_1.c, t2_1.b + -> Merge Left Join + Output: t3_1.a, t3_1.b, t3_1.c, t2_1.b, t2_1.c + Merge Cond: ((t3_1.a = t2_1.b) AND ((ltrim(t3_1.c, 'A'::text)) = t2_1.c)) + -> Sort + Output: t3_1.a, t3_1.b, t3_1.c, (ltrim(t3_1.c, 'A'::text)) + Sort Key: t3_1.a, (ltrim(t3_1.c, 'A'::text)) + -> Seq Scan on public.plt1_e_p2 t3_1 + Output: t3_1.a, t3_1.b, t3_1.c, ltrim(t3_1.c, 'A'::text) + Filter: ((t3_1.a % 25) = 0) + -> Sort + Output: t2_1.b, t2_1.c + Sort Key: t2_1.b, t2_1.c + -> Seq Scan on public.plt2_p2 t2_1 + Output: t2_1.b, t2_1.c + -> Sort + Output: t1_1.a, t1_1.c + Sort Key: t1_1.c, t1_1.a + -> Seq Scan on public.plt1_p2 t1_1 + Output: t1_1.a, t1_1.c + -> Merge Left Join + Output: t3_2.a, t3_2.b, t3_2.c, t2_2.b, t2_2.c, t1_2.a, t1_2.c + Merge Cond: ((t2_2.b = t1_2.a) AND (t2_2.c = t1_2.c)) + -> Sort + Output: t3_2.a, t3_2.b, t3_2.c, t2_2.b, t2_2.c + Sort Key: t2_2.b, t2_2.c + -> Merge Left Join + Output: t3_2.a, t3_2.b, t3_2.c, t2_2.b, t2_2.c + Merge Cond: ((t3_2.a = t2_2.b) AND ((ltrim(t3_2.c, 'A'::text)) = t2_2.c)) + -> Sort + Output: t3_2.a, t3_2.b, t3_2.c, (ltrim(t3_2.c, 'A'::text)) + Sort Key: t3_2.a, (ltrim(t3_2.c, 'A'::text)) + -> Seq Scan on public.plt1_e_p3 t3_2 + Output: t3_2.a, t3_2.b, t3_2.c, ltrim(t3_2.c, 'A'::text) + Filter: ((t3_2.a % 25) = 0) + -> Sort + Output: t2_2.b, t2_2.c + Sort Key: t2_2.b, t2_2.c + -> Seq Scan on public.plt2_p3 t2_2 + Output: t2_2.b, t2_2.c + -> Sort + Output: t1_2.a, t1_2.c + Sort Key: t1_2.a, t1_2.c + -> Seq Scan on public.plt1_p3 t1_2 + Output: t1_2.a, t1_2.c +(81 rows) + +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (plt1 t1 RIGHT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c) RIGHT JOIN plt1_e t3 ON (t2.b = t3.a AND t2.c = ltrim(t3.c, 'A')) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; + a | c | b | c | ?column? | c +-----+------+-----+------+----------+------- + 0 | 0000 | 0 | 0000 | 0 | A0000 + 150 | 0003 | 150 | 0003 | 300 | A0003 + 300 | 0006 | 300 | 0006 | 600 | A0006 + 450 | 0009 | 450 | 0009 | 900 | A0009 + | | | | 100 | A0001 + | | | | 200 | A0002 + | | | | 400 | A0004 + | | | | 500 | A0005 + | | | | 700 | A0007 + | | | | 800 | A0008 + | | | | 1000 | A0010 + | | | | 1100 | A0011 +(12 rows) + +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uplt1 t1 RIGHT JOIN uplt2 t2 ON t1.a = t2.b AND t1.c = t2.c) RIGHT JOIN uplt1_e t3 ON (t2.b = t3.a AND t2.c = ltrim(t3.c, 'A')) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; + a | c | b | c | ?column? | c +-----+------+-----+------+----------+------- + 0 | 0000 | 0 | 0000 | 0 | A0000 + 150 | 0003 | 150 | 0003 | 300 | A0003 + 300 | 0006 | 300 | 0006 | 600 | A0006 + 450 | 0009 | 450 | 0009 | 900 | A0009 + | | | | 100 | A0001 + | | | | 200 | A0002 + | | | | 400 | A0004 + | | | | 500 | A0005 + | | | | 700 | A0007 + | | | | 800 | A0008 + | | | | 1000 | A0010 + | | | | 1100 | A0011 +(12 rows) + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM ((SELECT * FROM plt1 WHERE plt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM plt2 WHERE plt2.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c)) FULL JOIN (SELECT * FROM plt1_e WHERE plt1_e.a % 25 = 0) t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) ORDER BY t1.a, t2.b, t3.a + t3.b; + QUERY PLAN +--------------------------------------------------------------------------------------------------------------- + Sort + Output: plt1_p1.a, plt1_p1.c, plt2_p1.b, plt2_p1.c, ((plt1_e_p1.a + plt1_e_p1.b)), plt1_e_p1.c + Sort Key: plt1_p1.a, plt2_p1.b, ((plt1_e_p1.a + plt1_e_p1.b)) + -> Result + Output: plt1_p1.a, plt1_p1.c, plt2_p1.b, plt2_p1.c, (plt1_e_p1.a + plt1_e_p1.b), plt1_e_p1.c + -> Append + -> Merge Full Join + Output: plt1_p1.a, plt1_p1.c, plt2_p1.b, plt2_p1.c, plt1_e_p1.a, plt1_e_p1.b, plt1_e_p1.c + Merge Cond: ((plt1_p1.a = plt1_e_p1.a) AND (plt1_p1.c = (ltrim(plt1_e_p1.c, 'A'::text)))) + -> Sort + Output: plt1_p1.a, plt1_p1.c, plt2_p1.b, plt2_p1.c + Sort Key: plt1_p1.a, plt1_p1.c + -> Merge Full Join + Output: plt1_p1.a, plt1_p1.c, plt2_p1.b, plt2_p1.c + Merge Cond: ((plt1_p1.a = plt2_p1.b) AND (plt1_p1.c = plt2_p1.c)) + -> Sort + Output: plt1_p1.a, plt1_p1.c + Sort Key: plt1_p1.a, plt1_p1.c + -> Seq Scan on public.plt1_p1 + Output: plt1_p1.a, plt1_p1.c + Filter: ((plt1_p1.a % 25) = 0) + -> Sort + Output: plt2_p1.b, plt2_p1.c + Sort Key: plt2_p1.b, plt2_p1.c + -> Seq Scan on public.plt2_p1 + Output: plt2_p1.b, plt2_p1.c + Filter: ((plt2_p1.b % 25) = 0) + -> Sort + Output: plt1_e_p1.a, plt1_e_p1.b, plt1_e_p1.c, (ltrim(plt1_e_p1.c, 'A'::text)) + Sort Key: plt1_e_p1.a, (ltrim(plt1_e_p1.c, 'A'::text)) + -> Seq Scan on public.plt1_e_p1 + Output: plt1_e_p1.a, plt1_e_p1.b, plt1_e_p1.c, ltrim(plt1_e_p1.c, 'A'::text) + Filter: ((plt1_e_p1.a % 25) = 0) + -> Merge Full Join + Output: plt1_p2.a, plt1_p2.c, plt2_p2.b, plt2_p2.c, plt1_e_p2.a, plt1_e_p2.b, plt1_e_p2.c + Merge Cond: ((plt1_p2.a = plt1_e_p2.a) AND (plt1_p2.c = (ltrim(plt1_e_p2.c, 'A'::text)))) + -> Sort + Output: plt1_p2.a, plt1_p2.c, plt2_p2.b, plt2_p2.c + Sort Key: plt1_p2.a, plt1_p2.c + -> Merge Full Join + Output: plt1_p2.a, plt1_p2.c, plt2_p2.b, plt2_p2.c + Merge Cond: ((plt1_p2.a = plt2_p2.b) AND (plt1_p2.c = plt2_p2.c)) + -> Sort + Output: plt1_p2.a, plt1_p2.c + Sort Key: plt1_p2.a, plt1_p2.c + -> Seq Scan on public.plt1_p2 + Output: plt1_p2.a, plt1_p2.c + Filter: ((plt1_p2.a % 25) = 0) + -> Sort + Output: plt2_p2.b, plt2_p2.c + Sort Key: plt2_p2.b, plt2_p2.c + -> Seq Scan on public.plt2_p2 + Output: plt2_p2.b, plt2_p2.c + Filter: ((plt2_p2.b % 25) = 0) + -> Sort + Output: plt1_e_p2.a, plt1_e_p2.b, plt1_e_p2.c, (ltrim(plt1_e_p2.c, 'A'::text)) + Sort Key: plt1_e_p2.a, (ltrim(plt1_e_p2.c, 'A'::text)) + -> Seq Scan on public.plt1_e_p2 + Output: plt1_e_p2.a, plt1_e_p2.b, plt1_e_p2.c, ltrim(plt1_e_p2.c, 'A'::text) + Filter: ((plt1_e_p2.a % 25) = 0) + -> Merge Full Join + Output: plt1_p3.a, plt1_p3.c, plt2_p3.b, plt2_p3.c, plt1_e_p3.a, plt1_e_p3.b, plt1_e_p3.c + Merge Cond: ((plt1_p3.a = plt1_e_p3.a) AND (plt1_p3.c = (ltrim(plt1_e_p3.c, 'A'::text)))) + -> Sort + Output: plt1_p3.a, plt1_p3.c, plt2_p3.b, plt2_p3.c + Sort Key: plt1_p3.a, plt1_p3.c + -> Merge Full Join + Output: plt1_p3.a, plt1_p3.c, plt2_p3.b, plt2_p3.c + Merge Cond: ((plt1_p3.a = plt2_p3.b) AND (plt1_p3.c = plt2_p3.c)) + -> Sort + Output: plt1_p3.a, plt1_p3.c + Sort Key: plt1_p3.a, plt1_p3.c + -> Seq Scan on public.plt1_p3 + Output: plt1_p3.a, plt1_p3.c + Filter: ((plt1_p3.a % 25) = 0) + -> Sort + Output: plt2_p3.b, plt2_p3.c + Sort Key: plt2_p3.b, plt2_p3.c + -> Seq Scan on public.plt2_p3 + Output: plt2_p3.b, plt2_p3.c + Filter: ((plt2_p3.b % 25) = 0) + -> Sort + Output: plt1_e_p3.a, plt1_e_p3.b, plt1_e_p3.c, (ltrim(plt1_e_p3.c, 'A'::text)) + Sort Key: plt1_e_p3.a, (ltrim(plt1_e_p3.c, 'A'::text)) + -> Seq Scan on public.plt1_e_p3 + Output: plt1_e_p3.a, plt1_e_p3.b, plt1_e_p3.c, ltrim(plt1_e_p3.c, 'A'::text) + Filter: ((plt1_e_p3.a % 25) = 0) +(87 rows) + +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM ((SELECT * FROM plt1 WHERE plt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM plt2 WHERE plt2.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c)) FULL JOIN (SELECT * FROM plt1_e WHERE plt1_e.a % 25 = 0) t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) ORDER BY t1.a, t2.b, t3.a + t3.b; + a | c | b | c | ?column? | c +-----+------+-----+------+----------+------- + 0 | 0000 | 0 | 0000 | 0 | A0000 + 50 | 0001 | | | 100 | A0001 + 100 | 0002 | | | 200 | A0002 + 150 | 0003 | 150 | 0003 | 300 | A0003 + 200 | 0004 | | | 400 | A0004 + 250 | 0005 | | | 500 | A0005 + 300 | 0006 | 300 | 0006 | 600 | A0006 + 350 | 0007 | | | 700 | A0007 + 400 | 0008 | | | 800 | A0008 + 450 | 0009 | 450 | 0009 | 900 | A0009 + 500 | 0010 | | | 1000 | A0010 + 550 | 0011 | | | 1100 | A0011 + | | 75 | 0001 | | + | | 225 | 0004 | | + | | 375 | 0007 | | + | | 525 | 0010 | | +(16 rows) + +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM ((SELECT * FROM uplt1 WHERE uplt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM uplt2 WHERE uplt2.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c)) FULL JOIN (SELECT * FROM uplt1_e WHERE uplt1_e.a % 25 = 0) t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) ORDER BY t1.a, t2.b, t3.a + t3.b; + a | c | b | c | ?column? | c +-----+------+-----+------+----------+------- + 0 | 0000 | 0 | 0000 | 0 | A0000 + 50 | 0001 | | | 100 | A0001 + 100 | 0002 | | | 200 | A0002 + 150 | 0003 | 150 | 0003 | 300 | A0003 + 200 | 0004 | | | 400 | A0004 + 250 | 0005 | | | 500 | A0005 + 300 | 0006 | 300 | 0006 | 600 | A0006 + 350 | 0007 | | | 700 | A0007 + 400 | 0008 | | | 800 | A0008 + 450 | 0009 | 450 | 0009 | 900 | A0009 + 500 | 0010 | | | 1000 | A0010 + 550 | 0011 | | | 1100 | A0011 + | | 75 | 0001 | | + | | 225 | 0004 | | + | | 375 | 0007 | | + | | 525 | 0010 | | +(16 rows) + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.* FROM plt1 t1 WHERE t1.c IN (SELECT t1.c FROM plt2 t1 WHERE t1.c IN (SELECT ltrim(t1.c, 'A') FROM plt1_e t1 WHERE t1.a % 25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a; + QUERY PLAN +-------------------------------------------------------------------------------------- + Sort + Output: t1.a, t1.b, t1.c + Sort Key: t1.a + -> Append + -> Merge Semi Join + Output: t1.a, t1.b, t1.c + Merge Cond: (t1.c = t1_3.c) + -> Sort + Output: t1.a, t1.b, t1.c + Sort Key: t1.c + -> Seq Scan on public.plt1_p1 t1 + Output: t1.a, t1.b, t1.c + Filter: ((t1.a % 25) = 0) + -> Materialize + Output: t1_3.c, t1_6.c + -> Merge Join + Output: t1_3.c, t1_6.c + Merge Cond: ((ltrim(t1_6.c, 'A'::text)) = t1_3.c) + -> Sort + Output: t1_6.c, (ltrim(t1_6.c, 'A'::text)) + Sort Key: (ltrim(t1_6.c, 'A'::text)) + -> HashAggregate + Output: t1_6.c, (ltrim(t1_6.c, 'A'::text)) + Group Key: ltrim(t1_6.c, 'A'::text) + -> Seq Scan on public.plt1_e_p1 t1_6 + Output: t1_6.c, ltrim(t1_6.c, 'A'::text) + Filter: ((t1_6.a % 25) = 0) + -> Sort + Output: t1_3.c + Sort Key: t1_3.c + -> Seq Scan on public.plt2_p1 t1_3 + Output: t1_3.c + -> Merge Semi Join + Output: t1_1.a, t1_1.b, t1_1.c + Merge Cond: (t1_1.c = t1_4.c) + -> Sort + Output: t1_1.a, t1_1.b, t1_1.c + Sort Key: t1_1.c + -> Seq Scan on public.plt1_p2 t1_1 + Output: t1_1.a, t1_1.b, t1_1.c + Filter: ((t1_1.a % 25) = 0) + -> Materialize + Output: t1_4.c, t1_7.c + -> Merge Join + Output: t1_4.c, t1_7.c + Merge Cond: ((ltrim(t1_7.c, 'A'::text)) = t1_4.c) + -> Sort + Output: t1_7.c, (ltrim(t1_7.c, 'A'::text)) + Sort Key: (ltrim(t1_7.c, 'A'::text)) + -> HashAggregate + Output: t1_7.c, (ltrim(t1_7.c, 'A'::text)) + Group Key: ltrim(t1_7.c, 'A'::text) + -> Seq Scan on public.plt1_e_p2 t1_7 + Output: t1_7.c, ltrim(t1_7.c, 'A'::text) + Filter: ((t1_7.a % 25) = 0) + -> Sort + Output: t1_4.c + Sort Key: t1_4.c + -> Seq Scan on public.plt2_p2 t1_4 + Output: t1_4.c + -> Merge Semi Join + Output: t1_2.a, t1_2.b, t1_2.c + Merge Cond: (t1_2.c = t1_5.c) + -> Sort + Output: t1_2.a, t1_2.b, t1_2.c + Sort Key: t1_2.c + -> Seq Scan on public.plt1_p3 t1_2 + Output: t1_2.a, t1_2.b, t1_2.c + Filter: ((t1_2.a % 25) = 0) + -> Materialize + Output: t1_5.c, t1_8.c + -> Merge Join + Output: t1_5.c, t1_8.c + Merge Cond: ((ltrim(t1_8.c, 'A'::text)) = t1_5.c) + -> Sort + Output: t1_8.c, (ltrim(t1_8.c, 'A'::text)) + Sort Key: (ltrim(t1_8.c, 'A'::text)) + -> HashAggregate + Output: t1_8.c, (ltrim(t1_8.c, 'A'::text)) + Group Key: ltrim(t1_8.c, 'A'::text) + -> Seq Scan on public.plt1_e_p3 t1_8 + Output: t1_8.c, ltrim(t1_8.c, 'A'::text) + Filter: ((t1_8.a % 25) = 0) + -> Sort + Output: t1_5.c + Sort Key: t1_5.c + -> Seq Scan on public.plt2_p3 t1_5 + Output: t1_5.c +(88 rows) + +SELECT t1.* FROM plt1 t1 WHERE t1.c IN (SELECT t1.c FROM plt2 t1 WHERE t1.c IN (SELECT ltrim(t1.c, 'A') FROM plt1_e t1 WHERE t1.a % 25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a; + a | b | c +-----+-----+------ + 0 | 0 | 0000 + 50 | 50 | 0001 + 100 | 100 | 0002 + 150 | 150 | 0003 + 200 | 200 | 0004 + 250 | 250 | 0005 + 300 | 300 | 0006 + 350 | 350 | 0007 + 400 | 400 | 0008 + 450 | 450 | 0009 + 500 | 500 | 0010 + 550 | 550 | 0011 +(12 rows) + +SELECT t1.* FROM uplt1 t1 WHERE t1.c IN (SELECT t1.c FROM uplt2 t1 WHERE t1.c IN (SELECT ltrim(t1.c, 'A') FROM uplt1_e t1 WHERE t1.a % 25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a; + a | b | c +-----+-----+------ + 0 | 0 | 0000 + 50 | 50 | 0001 + 100 | 100 | 0002 + 150 | 150 | 0003 + 200 | 200 | 0004 + 250 | 250 | 0005 + 300 | 300 | 0006 + 350 | 350 | 0007 + 400 | 400 | 0008 + 450 | 450 | 0009 + 500 | 500 | 0010 + 550 | 550 | 0011 +(12 rows) + +SET enable_seqscan TO off; +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (plt1 t1 RIGHT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c) RIGHT JOIN plt1_e t3 ON (t2.b = t3.a AND t2.c = ltrim(t3.c, 'A')) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; + QUERY PLAN +-------------------------------------------------------------------------------------------- + Sort + Output: t1.a, t1.c, t2.b, t2.c, ((t3.a + t3.b)), t3.c + Sort Key: t1.a, t2.b, ((t3.a + t3.b)) + -> Result + Output: t1.a, t1.c, t2.b, t2.c, (t3.a + t3.b), t3.c + -> Append + -> Merge Left Join + Output: t3.a, t3.b, t3.c, t2.b, t2.c, t1.a, t1.c + Merge Cond: ((t2.b = t1.a) AND (t2.c = t1.c)) + -> Sort + Output: t3.a, t3.b, t3.c, t2.b, t2.c + Sort Key: t2.b, t2.c + -> Merge Left Join + Output: t3.a, t3.b, t3.c, t2.b, t2.c + Merge Cond: (ltrim(t3.c, 'A'::text) = t2.c) + Join Filter: (t2.b = t3.a) + -> Index Scan using iplt1_e_p1_c on public.plt1_e_p1 t3 + Output: t3.a, t3.b, t3.c + Filter: ((t3.a % 25) = 0) + -> Index Scan using iplt2_p1_c on public.plt2_p1 t2 + Output: t2.b, t2.c + -> Sort + Output: t1.a, t1.c + Sort Key: t1.a, t1.c + -> Index Scan using iplt1_p1_c on public.plt1_p1 t1 + Output: t1.a, t1.c + -> Merge Left Join + Output: t3_1.a, t3_1.b, t3_1.c, t2_1.b, t2_1.c, t1_1.a, t1_1.c + Merge Cond: ((t2_1.c = t1_1.c) AND (t2_1.b = t1_1.a)) + -> Sort + Output: t3_1.a, t3_1.b, t3_1.c, t2_1.b, t2_1.c + Sort Key: t2_1.c, t2_1.b + -> Merge Left Join + Output: t3_1.a, t3_1.b, t3_1.c, t2_1.b, t2_1.c + Merge Cond: (ltrim(t3_1.c, 'A'::text) = t2_1.c) + Join Filter: (t2_1.b = t3_1.a) + -> Index Scan using iplt1_e_p2_c on public.plt1_e_p2 t3_1 + Output: t3_1.a, t3_1.b, t3_1.c + Filter: ((t3_1.a % 25) = 0) + -> Index Scan using iplt2_p2_c on public.plt2_p2 t2_1 + Output: t2_1.b, t2_1.c + -> Sort + Output: t1_1.a, t1_1.c + Sort Key: t1_1.c, t1_1.a + -> Index Scan using iplt1_p2_c on public.plt1_p2 t1_1 + Output: t1_1.a, t1_1.c + -> Merge Left Join + Output: t3_2.a, t3_2.b, t3_2.c, t2_2.b, t2_2.c, t1_2.a, t1_2.c + Merge Cond: ((t2_2.b = t1_2.a) AND (t2_2.c = t1_2.c)) + -> Sort + Output: t3_2.a, t3_2.b, t3_2.c, t2_2.b, t2_2.c + Sort Key: t2_2.b, t2_2.c + -> Merge Left Join + Output: t3_2.a, t3_2.b, t3_2.c, t2_2.b, t2_2.c + Merge Cond: (ltrim(t3_2.c, 'A'::text) = t2_2.c) + Join Filter: (t2_2.b = t3_2.a) + -> Index Scan using iplt1_e_p3_c on public.plt1_e_p3 t3_2 + Output: t3_2.a, t3_2.b, t3_2.c + Filter: ((t3_2.a % 25) = 0) + -> Index Scan using iplt2_p3_c on public.plt2_p3 t2_2 + Output: t2_2.b, t2_2.c + -> Sort + Output: t1_2.a, t1_2.c + Sort Key: t1_2.a, t1_2.c + -> Index Scan using iplt1_p3_c on public.plt1_p3 t1_2 + Output: t1_2.a, t1_2.c +(66 rows) + +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (plt1 t1 RIGHT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c) RIGHT JOIN plt1_e t3 ON (t2.b = t3.a AND t2.c = ltrim(t3.c, 'A')) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; + a | c | b | c | ?column? | c +-----+------+-----+------+----------+------- + 0 | 0000 | 0 | 0000 | 0 | A0000 + 150 | 0003 | 150 | 0003 | 300 | A0003 + 300 | 0006 | 300 | 0006 | 600 | A0006 + 450 | 0009 | 450 | 0009 | 900 | A0009 + | | | | 100 | A0001 + | | | | 200 | A0002 + | | | | 400 | A0004 + | | | | 500 | A0005 + | | | | 700 | A0007 + | | | | 800 | A0008 + | | | | 1000 | A0010 + | | | | 1100 | A0011 +(12 rows) + +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uplt1 t1 RIGHT JOIN uplt2 t2 ON t1.a = t2.b AND t1.c = t2.c) RIGHT JOIN uplt1_e t3 ON (t2.b = t3.a AND t2.c = ltrim(t3.c, 'A')) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; + a | c | b | c | ?column? | c +-----+------+-----+------+----------+------- + 0 | 0000 | 0 | 0000 | 0 | A0000 + 150 | 0003 | 150 | 0003 | 300 | A0003 + 300 | 0006 | 300 | 0006 | 600 | A0006 + 450 | 0009 | 450 | 0009 | 900 | A0009 + | | | | 100 | A0001 + | | | | 200 | A0002 + | | | | 400 | A0004 + | | | | 500 | A0005 + | | | | 700 | A0007 + | | | | 800 | A0008 + | | | | 1000 | A0010 + | | | | 1100 | A0011 +(12 rows) + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM ((SELECT * FROM plt1 WHERE plt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM plt2 WHERE plt2.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c)) FULL JOIN (SELECT * FROM plt1_e WHERE plt1_e.a % 25 = 0) t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) ORDER BY t1.a, t2.b, t3.a + t3.b; + QUERY PLAN +--------------------------------------------------------------------------------------------------------------- + Sort + Output: plt1_p1.a, plt1_p1.c, plt2_p1.b, plt2_p1.c, ((plt1_e_p1.a + plt1_e_p1.b)), plt1_e_p1.c + Sort Key: plt1_p1.a, plt2_p1.b, ((plt1_e_p1.a + plt1_e_p1.b)) + -> Result + Output: plt1_p1.a, plt1_p1.c, plt2_p1.b, plt2_p1.c, (plt1_e_p1.a + plt1_e_p1.b), plt1_e_p1.c + -> Append + -> Merge Full Join + Output: plt1_p1.a, plt1_p1.c, plt2_p1.b, plt2_p1.c, plt1_e_p1.a, plt1_e_p1.b, plt1_e_p1.c + Merge Cond: ((plt1_p1.a = plt1_e_p1.a) AND (plt1_p1.c = (ltrim(plt1_e_p1.c, 'A'::text)))) + -> Sort + Output: plt1_p1.a, plt1_p1.c, plt2_p1.b, plt2_p1.c + Sort Key: plt1_p1.a, plt1_p1.c + -> Merge Full Join + Output: plt1_p1.a, plt1_p1.c, plt2_p1.b, plt2_p1.c + Merge Cond: ((plt1_p1.a = plt2_p1.b) AND (plt1_p1.c = plt2_p1.c)) + -> Sort + Output: plt1_p1.a, plt1_p1.c + Sort Key: plt1_p1.a, plt1_p1.c + -> Index Scan using iplt1_p1_c on public.plt1_p1 + Output: plt1_p1.a, plt1_p1.c + Filter: ((plt1_p1.a % 25) = 0) + -> Sort + Output: plt2_p1.b, plt2_p1.c + Sort Key: plt2_p1.b, plt2_p1.c + -> Index Scan using iplt2_p1_c on public.plt2_p1 + Output: plt2_p1.b, plt2_p1.c + Filter: ((plt2_p1.b % 25) = 0) + -> Sort + Output: plt1_e_p1.a, plt1_e_p1.b, plt1_e_p1.c, (ltrim(plt1_e_p1.c, 'A'::text)) + Sort Key: plt1_e_p1.a, (ltrim(plt1_e_p1.c, 'A'::text)) + -> Index Scan using iplt1_e_p1_c on public.plt1_e_p1 + Output: plt1_e_p1.a, plt1_e_p1.b, plt1_e_p1.c, ltrim(plt1_e_p1.c, 'A'::text) + Filter: ((plt1_e_p1.a % 25) = 0) + -> Merge Full Join + Output: plt1_p2.a, plt1_p2.c, plt2_p2.b, plt2_p2.c, plt1_e_p2.a, plt1_e_p2.b, plt1_e_p2.c + Merge Cond: ((plt1_p2.a = plt1_e_p2.a) AND (plt1_p2.c = (ltrim(plt1_e_p2.c, 'A'::text)))) + -> Sort + Output: plt1_p2.a, plt1_p2.c, plt2_p2.b, plt2_p2.c + Sort Key: plt1_p2.a, plt1_p2.c + -> Merge Full Join + Output: plt1_p2.a, plt1_p2.c, plt2_p2.b, plt2_p2.c + Merge Cond: ((plt1_p2.a = plt2_p2.b) AND (plt1_p2.c = plt2_p2.c)) + -> Sort + Output: plt1_p2.a, plt1_p2.c + Sort Key: plt1_p2.a, plt1_p2.c + -> Index Scan using iplt1_p2_c on public.plt1_p2 + Output: plt1_p2.a, plt1_p2.c + Filter: ((plt1_p2.a % 25) = 0) + -> Sort + Output: plt2_p2.b, plt2_p2.c + Sort Key: plt2_p2.b, plt2_p2.c + -> Index Scan using iplt2_p2_c on public.plt2_p2 + Output: plt2_p2.b, plt2_p2.c + Filter: ((plt2_p2.b % 25) = 0) + -> Sort + Output: plt1_e_p2.a, plt1_e_p2.b, plt1_e_p2.c, (ltrim(plt1_e_p2.c, 'A'::text)) + Sort Key: plt1_e_p2.a, (ltrim(plt1_e_p2.c, 'A'::text)) + -> Index Scan using iplt1_e_p2_c on public.plt1_e_p2 + Output: plt1_e_p2.a, plt1_e_p2.b, plt1_e_p2.c, ltrim(plt1_e_p2.c, 'A'::text) + Filter: ((plt1_e_p2.a % 25) = 0) + -> Merge Full Join + Output: plt1_p3.a, plt1_p3.c, plt2_p3.b, plt2_p3.c, plt1_e_p3.a, plt1_e_p3.b, plt1_e_p3.c + Merge Cond: ((plt1_p3.a = plt1_e_p3.a) AND (plt1_p3.c = (ltrim(plt1_e_p3.c, 'A'::text)))) + -> Sort + Output: plt1_p3.a, plt1_p3.c, plt2_p3.b, plt2_p3.c + Sort Key: plt1_p3.a, plt1_p3.c + -> Merge Full Join + Output: plt1_p3.a, plt1_p3.c, plt2_p3.b, plt2_p3.c + Merge Cond: ((plt1_p3.a = plt2_p3.b) AND (plt1_p3.c = plt2_p3.c)) + -> Sort + Output: plt1_p3.a, plt1_p3.c + Sort Key: plt1_p3.a, plt1_p3.c + -> Index Scan using iplt1_p3_c on public.plt1_p3 + Output: plt1_p3.a, plt1_p3.c + Filter: ((plt1_p3.a % 25) = 0) + -> Sort + Output: plt2_p3.b, plt2_p3.c + Sort Key: plt2_p3.b, plt2_p3.c + -> Index Scan using iplt2_p3_c on public.plt2_p3 + Output: plt2_p3.b, plt2_p3.c + Filter: ((plt2_p3.b % 25) = 0) + -> Sort + Output: plt1_e_p3.a, plt1_e_p3.b, plt1_e_p3.c, (ltrim(plt1_e_p3.c, 'A'::text)) + Sort Key: plt1_e_p3.a, (ltrim(plt1_e_p3.c, 'A'::text)) + -> Index Scan using iplt1_e_p3_c on public.plt1_e_p3 + Output: plt1_e_p3.a, plt1_e_p3.b, plt1_e_p3.c, ltrim(plt1_e_p3.c, 'A'::text) + Filter: ((plt1_e_p3.a % 25) = 0) +(87 rows) + +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM ((SELECT * FROM plt1 WHERE plt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM plt2 WHERE plt2.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c)) FULL JOIN (SELECT * FROM plt1_e WHERE plt1_e.a % 25 = 0) t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) ORDER BY t1.a, t2.b, t3.a + t3.b; + a | c | b | c | ?column? | c +-----+------+-----+------+----------+------- + 0 | 0000 | 0 | 0000 | 0 | A0000 + 50 | 0001 | | | 100 | A0001 + 100 | 0002 | | | 200 | A0002 + 150 | 0003 | 150 | 0003 | 300 | A0003 + 200 | 0004 | | | 400 | A0004 + 250 | 0005 | | | 500 | A0005 + 300 | 0006 | 300 | 0006 | 600 | A0006 + 350 | 0007 | | | 700 | A0007 + 400 | 0008 | | | 800 | A0008 + 450 | 0009 | 450 | 0009 | 900 | A0009 + 500 | 0010 | | | 1000 | A0010 + 550 | 0011 | | | 1100 | A0011 + | | 75 | 0001 | | + | | 225 | 0004 | | + | | 375 | 0007 | | + | | 525 | 0010 | | +(16 rows) + +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM ((SELECT * FROM uplt1 WHERE uplt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM uplt2 WHERE uplt2.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c)) FULL JOIN (SELECT * FROM uplt1_e WHERE uplt1_e.a % 25 = 0) t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) ORDER BY t1.a, t2.b, t3.a + t3.b; + a | c | b | c | ?column? | c +-----+------+-----+------+----------+------- + 0 | 0000 | 0 | 0000 | 0 | A0000 + 50 | 0001 | | | 100 | A0001 + 100 | 0002 | | | 200 | A0002 + 150 | 0003 | 150 | 0003 | 300 | A0003 + 200 | 0004 | | | 400 | A0004 + 250 | 0005 | | | 500 | A0005 + 300 | 0006 | 300 | 0006 | 600 | A0006 + 350 | 0007 | | | 700 | A0007 + 400 | 0008 | | | 800 | A0008 + 450 | 0009 | 450 | 0009 | 900 | A0009 + 500 | 0010 | | | 1000 | A0010 + 550 | 0011 | | | 1100 | A0011 + | | 75 | 0001 | | + | | 225 | 0004 | | + | | 375 | 0007 | | + | | 525 | 0010 | | +(16 rows) + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.* FROM plt1 t1 WHERE t1.c IN (SELECT t1.c FROM plt2 t1 WHERE t1.c IN (SELECT ltrim(t1.c, 'A') FROM plt1_e t1 WHERE t1.a % 25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a; + QUERY PLAN +-------------------------------------------------------------------------------------------- + Sort + Output: t1.a, t1.b, t1.c + Sort Key: t1.a + -> Append + -> Merge Semi Join + Output: t1.a, t1.b, t1.c + Merge Cond: (t1.c = t1_3.c) + -> Index Scan using iplt1_p1_c on public.plt1_p1 t1 + Output: t1.a, t1.b, t1.c + Filter: ((t1.a % 25) = 0) + -> Materialize + Output: t1_3.c, t1_6.c + -> Merge Semi Join + Output: t1_3.c, t1_6.c + Merge Cond: (t1_3.c = ltrim(t1_6.c, 'A'::text)) + -> Index Only Scan using iplt2_p1_c on public.plt2_p1 t1_3 + Output: t1_3.c + -> Materialize + Output: t1_6.c + -> Index Scan using iplt1_e_p1_c on public.plt1_e_p1 t1_6 + Output: t1_6.c + Filter: ((t1_6.a % 25) = 0) + -> Merge Semi Join + Output: t1_1.a, t1_1.b, t1_1.c + Merge Cond: (t1_1.c = t1_4.c) + -> Index Scan using iplt1_p2_c on public.plt1_p2 t1_1 + Output: t1_1.a, t1_1.b, t1_1.c + Filter: ((t1_1.a % 25) = 0) + -> Materialize + Output: t1_4.c, t1_7.c + -> Merge Semi Join + Output: t1_4.c, t1_7.c + Merge Cond: (t1_4.c = ltrim(t1_7.c, 'A'::text)) + -> Index Only Scan using iplt2_p2_c on public.plt2_p2 t1_4 + Output: t1_4.c + -> Materialize + Output: t1_7.c + -> Index Scan using iplt1_e_p2_c on public.plt1_e_p2 t1_7 + Output: t1_7.c + Filter: ((t1_7.a % 25) = 0) + -> Merge Semi Join + Output: t1_2.a, t1_2.b, t1_2.c + Merge Cond: (t1_2.c = t1_5.c) + -> Index Scan using iplt1_p3_c on public.plt1_p3 t1_2 + Output: t1_2.a, t1_2.b, t1_2.c + Filter: ((t1_2.a % 25) = 0) + -> Materialize + Output: t1_5.c, t1_8.c + -> Merge Semi Join + Output: t1_5.c, t1_8.c + Merge Cond: (t1_5.c = ltrim(t1_8.c, 'A'::text)) + -> Index Only Scan using iplt2_p3_c on public.plt2_p3 t1_5 + Output: t1_5.c + -> Materialize + Output: t1_8.c + -> Index Scan using iplt1_e_p3_c on public.plt1_e_p3 t1_8 + Output: t1_8.c + Filter: ((t1_8.a % 25) = 0) +(58 rows) + +SELECT t1.* FROM plt1 t1 WHERE t1.c IN (SELECT t1.c FROM plt2 t1 WHERE t1.c IN (SELECT ltrim(t1.c, 'A') FROM plt1_e t1 WHERE t1.a % 25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a; + a | b | c +-----+-----+------ + 0 | 0 | 0000 + 50 | 50 | 0001 + 100 | 100 | 0002 + 150 | 150 | 0003 + 200 | 200 | 0004 + 250 | 250 | 0005 + 300 | 300 | 0006 + 350 | 350 | 0007 + 400 | 400 | 0008 + 450 | 450 | 0009 + 500 | 500 | 0010 + 550 | 550 | 0011 +(12 rows) + +SELECT t1.* FROM uplt1 t1 WHERE t1.c IN (SELECT t1.c FROM uplt2 t1 WHERE t1.c IN (SELECT ltrim(t1.c, 'A') FROM uplt1_e t1 WHERE t1.a % 25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a; + a | b | c +-----+-----+------ + 0 | 0 | 0000 + 50 | 50 | 0001 + 100 | 100 | 0002 + 150 | 150 | 0003 + 200 | 200 | 0004 + 250 | 250 | 0005 + 300 | 300 | 0006 + 350 | 350 | 0007 + 400 | 400 | 0008 + 450 | 450 | 0009 + 500 | 500 | 0010 + 550 | 550 | 0011 +(12 rows) + +RESET enable_hashjoin; +RESET enable_nestloop; +RESET enable_seqscan; +-- +-- negative testcases +-- +-- joins where one of the relations is proven empty +EXPLAIN (VERBOSE, 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; + QUERY PLAN +---------------------------------- + Result + Output: t1.a, t1.c, t2.b, t2.c + One-Time Filter: false +(3 rows) + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a = 1 AND a = 2) t1 LEFT JOIN prt2 t2 ON t1.a = t2.b; + QUERY PLAN +-------------------------------------- + Result + Output: prt1.a, prt1.c, t2.b, t2.c + One-Time Filter: false +(3 rows) + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a = 1 AND a = 2) t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b WHERE t2.a % 25 = 0 ORDER BY t1.a, t2.b; + QUERY PLAN +--------------------------------------------------- + Sort + Output: a, c, t2.b, t2.c + Sort Key: a, t2.b + -> Hash Left Join + Output: a, c, t2.b, t2.c + Hash Cond: (t2.b = a) + -> Append + -> Seq Scan on public.prt2 t2 + Output: t2.b, t2.c + Filter: ((t2.a % 25) = 0) + -> Seq Scan on public.prt2_p1 t2_1 + Output: t2_1.b, t2_1.c + Filter: ((t2_1.a % 25) = 0) + -> Seq Scan on public.prt2_p2 t2_2 + Output: t2_2.b, t2_2.c + Filter: ((t2_2.a % 25) = 0) + -> Seq Scan on public.prt2_p3 t2_3 + Output: t2_3.b, t2_3.c + Filter: ((t2_3.a % 25) = 0) + -> Hash + Output: a, c + -> Result + Output: a, c + One-Time Filter: false +(24 rows) + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a = 1 AND a = 2) t1 FULL JOIN prt2 t2 ON t1.a = t2.b WHERE t2.a % 25 = 0 ORDER BY t1.a, t2.b; + QUERY PLAN +--------------------------------------------------- + Sort + Output: a, c, t2.b, t2.c + Sort Key: a, t2.b + -> Hash Left Join + Output: a, c, t2.b, t2.c + Hash Cond: (t2.b = a) + -> Append + -> Seq Scan on public.prt2 t2 + Output: t2.b, t2.c + Filter: ((t2.a % 25) = 0) + -> Seq Scan on public.prt2_p1 t2_1 + Output: t2_1.b, t2_1.c + Filter: ((t2_1.a % 25) = 0) + -> Seq Scan on public.prt2_p2 t2_2 + Output: t2_2.b, t2_2.c + Filter: ((t2_2.a % 25) = 0) + -> Seq Scan on public.prt2_p3 t2_3 + Output: t2_3.b, t2_3.c + Filter: ((t2_3.a % 25) = 0) + -> Hash + Output: a, c + -> Result + Output: a, c + One-Time Filter: false +(24 rows) + +CREATE TABLE prt1_n (a int, b int, c varchar) PARTITION BY RANGE(c); +CREATE TABLE prt1_n_p1 PARTITION OF prt1_n FOR VALUES START ('0000') END ('0250'); +CREATE TABLE prt1_n_p2 PARTITION OF prt1_n FOR VALUES START ('0250') END ('0500'); +INSERT INTO prt1_n SELECT i, i, to_char(i, 'FM0000') FROM generate_series(0, 499, 2) i; +ANALYZE prt1_n; +ANALYZE prt1_n_p1; +ANALYZE prt1_n_p2; +CREATE TABLE prt2_n (a int, b int, c text) PARTITION BY LIST(c); +CREATE TABLE prt2_n_p1 PARTITION OF prt2_n FOR VALUES IN ('0000', '0003', '0004', '0010', '0006', '0007'); +CREATE TABLE prt2_n_p2 PARTITION OF prt2_n FOR VALUES IN ('0001', '0005', '0002', '0009', '0008', '0011'); +INSERT INTO prt2_n SELECT i, i, to_char(i/50, 'FM0000') FROM generate_series(0, 599, 2) i; +ANALYZE prt2_n; +ANALYZE prt2_n_p1; +ANALYZE prt2_n_p2; +CREATE TABLE prt3_n (a int, b int, c text) PARTITION BY LIST(c); +CREATE TABLE prt3_n_p1 PARTITION OF prt3_n FOR VALUES IN ('0000', '0004', '0006', '0007'); +CREATE TABLE prt3_n_p2 PARTITION OF prt3_n FOR VALUES IN ('0001', '0002', '0008', '0010'); +CREATE TABLE prt3_n_p3 PARTITION OF prt3_n FOR VALUES IN ('0003', '0005', '0009', '0011'); +INSERT INTO prt2_n SELECT i, i, to_char(i/50, 'FM0000') FROM generate_series(0, 599, 2) i; +ANALYZE prt3_n; +ANALYZE prt3_n_p1; +ANALYZE prt3_n_p2; +ANALYZE prt3_n_p3; +CREATE TABLE prt4_n (a int, b int, c text) PARTITION BY RANGE(a); +CREATE TABLE prt4_n_p1 PARTITION OF prt4_n FOR VALUES START (0) END (300); +CREATE TABLE prt4_n_p2 PARTITION OF prt4_n FOR VALUES START (300) END (500); +CREATE TABLE prt4_n_p3 PARTITION OF prt4_n FOR VALUES START (500) END (600); +INSERT INTO prt4_n SELECT i, i, to_char(i, 'FM0000') FROM generate_series(0, 599, 2) i; +ANALYZE prt4_n; +ANALYZE prt4_n_p1; +ANALYZE prt4_n_p2; +ANALYZE prt4_n_p3; +-- partition-wise join can not be applied if the partition ranges differ +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt4_n t2 WHERE t1.a = t2.a; + QUERY PLAN +---------------------------------------------- + Hash Join + Hash Cond: (t1.a = t2.a) + -> Append + -> Seq Scan on prt1 t1 + -> Seq Scan on prt1_p1 t1_1 + -> Seq Scan on prt1_p3 t1_2 + -> Seq Scan on prt1_p2 t1_3 + -> Hash + -> Append + -> Seq Scan on prt4_n t2 + -> Seq Scan on prt4_n_p1 t2_1 + -> Seq Scan on prt4_n_p2 t2_2 + -> Seq Scan on prt4_n_p3 t2_3 +(13 rows) + +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1 FULL JOIN prt4_n t2 ON t1.a = t2.a; + QUERY PLAN +---------------------------------------------- + Hash Full Join + Hash Cond: (t1.a = t2.a) + -> Append + -> Seq Scan on prt1 t1 + -> Seq Scan on prt1_p1 t1_1 + -> Seq Scan on prt1_p3 t1_2 + -> Seq Scan on prt1_p2 t1_3 + -> Hash + -> Append + -> Seq Scan on prt4_n t2 + -> Seq Scan on prt4_n_p1 t2_1 + -> Seq Scan on prt4_n_p2 t2_2 + -> Seq Scan on prt4_n_p3 t2_3 +(13 rows) + +-- partition-wise 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 +--------------------------------------------------------- + Nested Loop Left Join + -> Append + -> Seq Scan on prt1 t1 + -> Seq Scan on prt1_p1 t1_1 + -> Seq Scan on prt1_p3 t1_2 + -> Seq Scan on prt1_p2 t1_3 + -> Append + -> Seq Scan on prt2 t2 + Filter: (t1.a < b) + -> Index Scan using iprt2_p1_b on prt2_p1 t2_1 + Index Cond: (t1.a < b) + -> Index Scan using iprt2_p2_b on prt2_p2 t2_2 + Index Cond: (t1.a < b) + -> Index Scan using iprt2_p3_b on prt2_p3 t2_3 + Index Cond: (t1.a < b) +(15 rows) + +-- equi-join with join condition on partial keys does not qualify for +-- partition-wise join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_m t1, prt2_m t2 WHERE t1.a = (t2.b + t2.a)/2 AND t1.a % 25 = 0; + QUERY PLAN +---------------------------------------------- + Hash Join + Hash Cond: (((t2.b + t2.a) / 2) = t1.a) + -> Append + -> Seq Scan on prt2_m t2 + -> Seq Scan on prt2_m_p1 t2_1 + -> Seq Scan on prt2_m_p2 t2_2 + -> Seq Scan on prt2_m_p3 t2_3 + -> Hash + -> Append + -> Seq Scan on prt1_m t1 + Filter: ((a % 25) = 0) + -> Seq Scan on prt1_m_p1 t1_1 + Filter: ((a % 25) = 0) + -> Seq Scan on prt1_m_p2 t1_2 + Filter: ((a % 25) = 0) + -> Seq Scan on prt1_m_p3 t1_3 + Filter: ((a % 25) = 0) +(17 rows) + +-- equi-join between out-of-order partition key columns does not qualify for +-- partition-wise join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_m t1 LEFT JOIN prt2_m t2 ON t1.a = t2.b WHERE t1.a % 25 = 0; + QUERY PLAN +---------------------------------------------- + Hash Right Join + Hash Cond: (t2.b = t1.a) + -> Append + -> Seq Scan on prt2_m t2 + -> Seq Scan on prt2_m_p1 t2_1 + -> Seq Scan on prt2_m_p2 t2_2 + -> Seq Scan on prt2_m_p3 t2_3 + -> Hash + -> Append + -> Seq Scan on prt1_m t1 + Filter: ((a % 25) = 0) + -> Seq Scan on prt1_m_p1 t1_1 + Filter: ((a % 25) = 0) + -> Seq Scan on prt1_m_p2 t1_2 + Filter: ((a % 25) = 0) + -> Seq Scan on prt1_m_p3 t1_3 + Filter: ((a % 25) = 0) +(17 rows) + +-- equi-join between non-key columns does not qualify for partition-wise join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_m t1 LEFT JOIN prt2_m t2 ON t1.c = t2.c WHERE t1.a % 25 = 0; + QUERY PLAN +---------------------------------------------- + Hash Right Join + Hash Cond: ((t2.c)::text = (t1.c)::text) + -> Append + -> Seq Scan on prt2_m t2 + -> Seq Scan on prt2_m_p1 t2_1 + -> Seq Scan on prt2_m_p2 t2_2 + -> Seq Scan on prt2_m_p3 t2_3 + -> Hash + -> Append + -> Seq Scan on prt1_m t1 + Filter: ((a % 25) = 0) + -> Seq Scan on prt1_m_p1 t1_1 + Filter: ((a % 25) = 0) + -> Seq Scan on prt1_m_p2 t1_2 + Filter: ((a % 25) = 0) + -> Seq Scan on prt1_m_p3 t1_3 + Filter: ((a % 25) = 0) +(17 rows) + +-- partition-wise join can not be applied for a join between list and range +-- partitioned table +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_n t1, prt2_n t2 WHERE t1.c = t2.c; + QUERY PLAN +---------------------------------------------- + Hash Join + Hash Cond: (t2.c = (t1.c)::text) + -> Append + -> Seq Scan on prt2_n t2 + -> Seq Scan on prt2_n_p1 t2_1 + -> Seq Scan on prt2_n_p2 t2_2 + -> Hash + -> Append + -> Seq Scan on prt1_n t1 + -> Seq Scan on prt1_n_p1 t1_1 + -> Seq Scan on prt1_n_p2 t1_2 +(11 rows) + +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_n t1 LEFT JOIN prt2_n t2 ON (t1.c = t2.c); + QUERY PLAN +---------------------------------------------- + Hash Right Join + Hash Cond: (t2.c = (t1.c)::text) + -> Append + -> Seq Scan on prt2_n t2 + -> Seq Scan on prt2_n_p1 t2_1 + -> Seq Scan on prt2_n_p2 t2_2 + -> Hash + -> Append + -> Seq Scan on prt1_n t1 + -> Seq Scan on prt1_n_p1 t1_1 + -> Seq Scan on prt1_n_p2 t1_2 +(11 rows) + +-- partition-wise join can not be applied between tables with different +-- partition lists +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_n t1 RIGHT JOIN prt1 t2 ON (t1.c = t2.c); + QUERY PLAN +---------------------------------------------- + Hash Left Join + Hash Cond: ((t2.c)::text = (t1.c)::text) + -> Append + -> Seq Scan on prt1 t2 + -> Seq Scan on prt1_p1 t2_1 + -> Seq Scan on prt1_p3 t2_2 + -> Seq Scan on prt1_p2 t2_3 + -> Hash + -> Append + -> Seq Scan on prt1_n t1 + -> Seq Scan on prt1_n_p1 t1_1 + -> Seq Scan on prt1_n_p2 t1_2 +(12 rows) + +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_n t1 FULL JOIN prt1 t2 ON (t1.c = t2.c); + QUERY PLAN +---------------------------------------------- + Hash Full Join + Hash Cond: ((t2.c)::text = (t1.c)::text) + -> Append + -> Seq Scan on prt1 t2 + -> Seq Scan on prt1_p1 t2_1 + -> Seq Scan on prt1_p3 t2_2 + -> Seq Scan on prt1_p2 t2_3 + -> Hash + -> Append + -> Seq Scan on prt1_n t1 + -> Seq Scan on prt1_n_p1 t1_1 + -> Seq Scan on prt1_n_p2 t1_2 +(12 rows) + diff --git a/src/test/regress/expected/rangefuncs.out b/src/test/regress/expected/rangefuncs.out index f06cfa4..16e7f56 100644 --- a/src/test/regress/expected/rangefuncs.out +++ b/src/test/regress/expected/rangefuncs.out @@ -1,25 +1,26 @@ SELECT name, setting FROM pg_settings WHERE name LIKE 'enable%'; - name | setting -----------------------+--------- - enable_bitmapscan | on - enable_hashagg | on - enable_hashjoin | on - enable_indexonlyscan | on - enable_indexscan | on - enable_material | on - enable_mergejoin | on - enable_nestloop | on - enable_seqscan | on - enable_sort | on - enable_tidscan | on -(11 rows) + name | setting +----------------------------+--------- + enable_bitmapscan | on + enable_hashagg | on + enable_hashjoin | on + enable_indexonlyscan | on + enable_indexscan | on + enable_material | on + enable_mergejoin | on + enable_nestloop | on + enable_partition_wise_join | on + enable_seqscan | on + enable_sort | on + enable_tidscan | on +(12 rows) CREATE TABLE foo2(fooid int, f2 int); INSERT INTO foo2 VALUES(1, 11); INSERT INTO foo2 VALUES(2, 22); INSERT INTO foo2 VALUES(1, 111); CREATE FUNCTION foot(int) returns setof foo2 as 'SELECT * FROM foo2 WHERE fooid = $1 ORDER BY f2;' LANGUAGE SQL; -- function with ORDINALITY select * from foot(1) with ordinality as z(a,b,ord); a | b | ord ---+-----+----- diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index 8641769..5ad149d 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -93,21 +93,21 @@ test: alter_generic alter_operator misc psql async dbsize misc_functions tsrf # rules cannot run concurrently with any test that creates a view test: rules psql_crosstab amutils # run by itself so it can run parallel workers test: select_parallel # ---------- # Another group of parallel tests # ---------- -test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combocid tsearch tsdicts foreign_data window xmlmap functional_deps advisory_lock json jsonb json_encoding indirect_toast equivclass +test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combocid tsearch tsdicts foreign_data window xmlmap functional_deps advisory_lock json jsonb json_encoding indirect_toast equivclass partition_join # ---------- # Another group of parallel tests # NB: temp.sql does a reconnect which transiently uses 2 connections, # so keep this parallel group to at most 19 tests # ---------- test: plancache limit plpgsql copy2 temp domain rangefuncs prepare without_oid conversion truncate alter_table sequence polymorphism rowtypes returning largeobject with xml # event triggers cannot run concurrently with any test that runs DDL test: event_trigger diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule index 835cf35..b4773b8 100644 --- a/src/test/regress/serial_schedule +++ b/src/test/regress/serial_schedule @@ -162,10 +162,11 @@ test: truncate test: alter_table test: sequence test: polymorphism test: rowtypes test: returning test: largeobject test: with test: xml test: event_trigger test: stats +test: partition_join diff --git a/src/test/regress/sql/partition_join.sql b/src/test/regress/sql/partition_join.sql new file mode 100644 index 0000000..fedaff4 --- /dev/null +++ b/src/test/regress/sql/partition_join.sql @@ -0,0 +1,782 @@ +-- +-- PARTITION_JOIN +-- Test partition-wise join between partitioned tables +-- + +-- +-- partitioned by a single column +-- +CREATE TABLE prt1 (a int, b int, c varchar) PARTITION BY RANGE(a); +CREATE TABLE prt1_p1 PARTITION OF prt1 FOR VALUES START (0) END (250); +CREATE TABLE prt1_p3 PARTITION OF prt1 FOR VALUES START (500) END (600); +CREATE TABLE prt1_p2 PARTITION OF prt1 FOR VALUES START (250) END (500); +INSERT INTO prt1 SELECT i, i, to_char(i, 'FM0000') FROM generate_series(0, 599, 2) i; +ANALYZE prt1; +ANALYZE prt1_p1; +ANALYZE prt1_p2; +ANALYZE prt1_p3; +-- TODO: This table is created only for testing the results. Remove once +-- results are tested. +CREATE TABLE uprt1 AS SELECT * FROM prt1; + +CREATE TABLE prt2 (a int, b int, c varchar) PARTITION BY RANGE(b); +CREATE TABLE prt2_p1 PARTITION OF prt2 FOR VALUES START (0) END (250); +CREATE TABLE prt2_p2 PARTITION OF prt2 FOR VALUES START (250) END (500); +CREATE TABLE prt2_p3 PARTITION OF prt2 FOR VALUES START (500) END (600); +INSERT INTO prt2 SELECT i, i, to_char(i, 'FM0000') FROM generate_series(0, 599, 3) i; +-- TODO: This table is created only for testing the results. Remove once +-- results are tested. +ANALYZE prt2; +ANALYZE prt2_p1; +ANALYZE prt2_p2; +ANALYZE prt2_p3; +CREATE TABLE uprt2 AS SELECT * FROM prt2; + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.a % 25 = 0 ORDER BY t1.a, t2.b; +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.a % 25 = 0 ORDER BY t1.a, t2.b; +SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1 t1, uprt2 t2 WHERE t1.a = t2.b AND t1.a % 25 = 0 ORDER BY t1.a, t2.b; + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b; +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b; +SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1 t1 LEFT JOIN uprt2 t2 ON t1.a = t2.b WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b; + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b; +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b; +SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1 t1 RIGHT JOIN uprt2 t2 ON t1.a = t2.b WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b; + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE prt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM prt2 WHERE prt2.b % 25 = 0) t2 ON (t1.a = t2.b) ORDER BY t1.a, t2.b; +SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE prt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM prt2 WHERE prt2.b % 25 = 0) t2 ON (t1.a = t2.b) ORDER BY t1.a, t2.b; +SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM uprt1 t1 WHERE t1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM uprt2 t2 WHERE t2.b % 25 = 0) t2 ON (t1.a = t2.b) ORDER BY t1.a, t2.b; + +-- Cases with non-nullable expressions in subquery results; +-- make sure these go to null as expected +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT 50 phv, * FROM prt1 WHERE prt1.a % 25 = 0) t1 FULL JOIN (SELECT 75 phv, * FROM prt2 WHERE prt2.b % 25 = 0) t2 ON (t1.a = t2.b) WHERE t1.phv = t1.b OR t2.phv = t2.b ORDER BY t1.a, t2.b; +SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT 50 phv, * FROM prt1 WHERE prt1.a % 25 = 0) t1 FULL JOIN (SELECT 75 phv, * FROM prt2 WHERE prt2.b % 25 = 0) t2 ON (t1.a = t2.b) WHERE t1.phv = t1.b OR t2.phv = t2.b ORDER BY t1.a, t2.b; +SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT 50 phv, * FROM uprt1 WHERE uprt1.a % 25 = 0) t1 FULL JOIN (SELECT 75 phv, * FROM uprt2 WHERE uprt2.b % 25 = 0) t2 ON (t1.a = t2.b) WHERE t1.phv = t1.b OR t2.phv = t2.b ORDER BY t1.a, t2.b; + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t1.phv, t2.b, t2.c, t2.phv FROM (SELECT 25 phv, * FROM prt1 WHERE prt1.a % 25 = 0) t1 FULL JOIN (SELECT 50 phv, * FROM prt2 WHERE prt2.b % 25 = 0) t2 ON (t1.a = t2.b) ORDER BY t1.a, t2.b; +SELECT t1.a, t1.c, t1.phv, t2.b, t2.c, t2.phv FROM (SELECT 25 phv, * FROM prt1 WHERE prt1.a % 25 = 0) t1 FULL JOIN (SELECT 50 phv, * FROM prt2 WHERE prt2.b % 25 = 0) t2 ON (t1.a = t2.b) ORDER BY t1.a, t2.b; +SELECT t1.a, t1.c, t1.phv, t2.b, t2.c, t2.phv FROM (SELECT 25 phv, * FROM uprt1 WHERE uprt1.a % 25 = 0) t1 FULL JOIN (SELECT 50 phv, * FROM uprt2 WHERE uprt2.b % 25 = 0) t2 ON (t1.a = t2.b) ORDER BY t1.a, t2.b; + +-- Join with pruned partitions from joining relations +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.a < 450 AND t2.b > 250 AND t1.a % 25 = 0 ORDER BY t1.a, t2.b; +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.a < 450 AND t2.b > 250 AND t1.a % 25 = 0 ORDER BY t1.a, t2.b; +SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1 t1, uprt2 t2 WHERE t1.a = t2.b AND t1.a < 450 AND t2.b > 250 AND t1.a % 25 = 0 ORDER BY t1.a, t2.b; + +EXPLAIN (VERBOSE, COSTS OFF) +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.a % 25 = 0 ORDER BY t1.a, t2.b; +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.a % 25 = 0 ORDER BY t1.a, t2.b; +SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM uprt1 WHERE a < 450) t1 LEFT JOIN (SELECT * FROM uprt2 WHERE b > 250) t2 ON t1.a = t2.b WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b; + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a < 450) t1 RIGHT JOIN (SELECT * FROM prt2 WHERE b > 250) t2 ON t1.a = t2.b WHERE t2.a % 25 = 0 ORDER BY t1.a, t2.b; +SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a < 450) t1 RIGHT JOIN (SELECT * FROM prt2 WHERE b > 250) t2 ON t1.a = t2.b WHERE t2.a % 25 = 0 ORDER BY t1.a, t2.b; +SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM uprt1 WHERE a < 450) t1 RIGHT JOIN (SELECT * FROM uprt2 WHERE b > 250) t2 ON t1.a = t2.b WHERE t2.a % 25 = 0 ORDER BY t1.a, t2.b; + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a < 450 AND a % 25 = 0) t1 FULL JOIN (SELECT * FROM prt2 WHERE b > 250 AND b % 25 = 0) t2 ON t1.a = t2.b ORDER BY t1.a, t2.b; +SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a < 450 AND a % 25 = 0) t1 FULL JOIN (SELECT * FROM prt2 WHERE b > 250 AND b % 25 = 0) t2 ON t1.a = t2.b ORDER BY t1.a, t2.b; +SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM uprt1 WHERE a < 450 AND a % 25 = 0) t1 FULL JOIN (SELECT * FROM uprt2 WHERE b > 250 AND b % 25 = 0) t2 ON t1.a = t2.b ORDER BY t1.a, t2.b; + +-- Semi-join +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1 WHERE t1.b % 25 = 0) AND t1.a % 25 = 0 ORDER BY t1.a; +SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1 WHERE t1.b % 25 = 0) AND t1.a % 25 = 0 ORDER BY t1.a; +SELECT t1.* FROM uprt1 t1 WHERE t1.a IN (SELECT t1.b FROM uprt2 t1 WHERE t1.b % 25 = 0) AND t1.a % 25 = 0 ORDER BY t1.a; + +-- lateral reference +EXPLAIN (VERBOSE, COSTS OFF) +SELECT * FROM prt1 t1 LEFT JOIN LATERAL + (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM prt1 t2 JOIN prt2 t3 ON (t2.a = t3.b)) ss + ON t1.a = ss.t2a WHERE t1.a % 25 = 0 ORDER BY t1.a; +SELECT * FROM prt1 t1 LEFT JOIN LATERAL + (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM prt1 t2 JOIN prt2 t3 ON (t2.a = t3.b)) ss + ON t1.a = ss.t2a WHERE t1.a % 25 = 0 ORDER BY t1.a; +SELECT * FROM uprt1 t1 LEFT JOIN LATERAL + (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM uprt1 t2 JOIN uprt2 t3 ON (t2.a = t3.b)) ss + ON t1.a = ss.t2a WHERE t1.a % 25 = 0 ORDER BY t1.a; + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT * FROM prt1 t1 LEFT JOIN LATERAL + (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM prt1 t2 JOIN prt2 t3 ON (t2.a = t3.b)) ss + ON t1.b = ss.t2a WHERE t1.a % 25 = 0 ORDER BY t1.a; +SELECT * FROM prt1 t1 LEFT JOIN LATERAL + (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM prt1 t2 JOIN prt2 t3 ON (t2.a = t3.b)) ss + ON t1.b = ss.t2a WHERE t1.a % 25 = 0 ORDER BY t1.a; +SELECT * FROM uprt1 t1 LEFT JOIN LATERAL + (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM uprt1 t2 JOIN uprt2 t3 ON (t2.a = t3.b)) ss + ON t1.b = ss.t2a WHERE t1.a % 25 = 0 ORDER BY t1.a; + +-- +-- partitioned by expression +-- +CREATE TABLE prt1_e (a int, b int, c varchar) PARTITION BY RANGE(((a + b)/2)); +CREATE TABLE prt1_e_p1 PARTITION OF prt1_e FOR VALUES START (0) END (250); +CREATE TABLE prt1_e_p2 PARTITION OF prt1_e FOR VALUES START (250) END (500); +CREATE TABLE prt1_e_p3 PARTITION OF prt1_e FOR VALUES START (500) END (600); +INSERT INTO prt1_e SELECT i, i, to_char(i, 'FM0000') FROM generate_series(0, 599, 2) i; +ANALYZE prt1_e; +ANALYZE prt1_e_p1; +ANALYZE prt1_e_p2; +ANALYZE prt1_e_p3; +-- TODO: This table is created only for testing the results. Remove once +-- results are tested. +CREATE TABLE uprt1_e AS SELECT * FROM prt1_e; + +CREATE TABLE prt2_e (a int, b int, c varchar) PARTITION BY RANGE(((b + a)/2)); +CREATE TABLE prt2_e_p1 PARTITION OF prt2_e FOR VALUES START (0) END (250); +CREATE TABLE prt2_e_p2 PARTITION OF prt2_e FOR VALUES START (250) END (500); +CREATE TABLE prt2_e_p3 PARTITION OF prt2_e FOR VALUES START (500) END (600); +INSERT INTO prt2_e SELECT i, i, to_char(i, 'FM0000') FROM generate_series(0, 599, 3) i; +ANALYZE prt2_e; +ANALYZE prt2_e_p1; +ANALYZE prt2_e_p2; +ANALYZE prt2_e_p3; +-- TODO: This table is created only for testing the results. Remove once +-- results are tested. +CREATE TABLE uprt2_e AS SELECT * FROM prt2_e; + +EXPLAIN (VERBOSE, 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.a % 25 = 0 ORDER BY t1.a, t2.b; +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.a % 25 = 0 ORDER BY t1.a, t2.b; +SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1_e t1, uprt2_e t2 WHERE (t1.a + t1.b)/2 = (t2.b + t2.a)/2 AND t1.a % 25 = 0 ORDER BY t1.a, t2.b; + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_e t1 LEFT JOIN prt2_e t2 ON (t1.a + t1.b)/2 = (t2.b + t2.a)/2 WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b; +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_e t1 LEFT JOIN prt2_e t2 ON (t1.a + t1.b)/2 = (t2.b + t2.a)/2 WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b; +SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1_e t1 LEFT JOIN uprt2_e t2 ON (t1.a + t1.b)/2 = (t2.b + t2.a)/2 WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b; + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_e t1 RIGHT JOIN prt2_e t2 ON (t1.a + t1.b)/2 = (t2.b + t2.a)/2 WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b; +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_e t1 RIGHT JOIN prt2_e t2 ON (t1.a + t1.b)/2 = (t2.b + t2.a)/2 WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b; +SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1_e t1 RIGHT JOIN uprt2_e t2 ON (t1.a + t1.b)/2 = (t2.b + t2.a)/2 WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b; + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_e WHERE prt1_e.a % 25 = 0) t1 FULL JOIN (SELECT * FROM prt2_e WHERE prt2_e.b % 25 = 0) t2 ON ((t1.a + t1.b)/2 = (t2.b + t2.a)/2) ORDER BY t1.a, t2.b; +SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_e WHERE prt1_e.a % 25 = 0) t1 FULL JOIN (SELECT * FROM prt2_e WHERE prt2_e.b % 25 = 0) t2 ON ((t1.a + t1.b)/2 = (t2.b + t2.a)/2) ORDER BY t1.a, t2.b; +SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM uprt1_e t1 WHERE t1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM uprt2_e t2 WHERE t2.b % 25 = 0) t2 ON ((t1.a + t1.b)/2 = (t2.b + t2.a)/2) ORDER BY t1.a, t2.b; + +-- +-- N-way join +-- +EXPLAIN (VERBOSE, COSTS OFF) +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.a % 25 = 0 ORDER BY t1.a, t2.b; +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.a % 25 = 0 ORDER BY t1.a, t2.b; +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM uprt1 t1, uprt2 t2, uprt1_e t3 WHERE t1.a = t2.b AND t1.a = (t3.a + t3.b)/2 AND t1.a % 25 = 0 ORDER BY t1.a, t2.b; + +EXPLAIN (VERBOSE, 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.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; +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.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uprt1 t1 LEFT JOIN uprt2 t2 ON t1.a = t2.b) LEFT JOIN uprt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; + +EXPLAIN (VERBOSE, 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 (t2.b = (t3.a + t3.b)/2) WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; +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 (t2.b = (t3.a + t3.b)/2) WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uprt1 t1 LEFT JOIN uprt2 t2 ON t1.a = t2.b) LEFT JOIN uprt1_e t3 ON (t2.b = (t3.a + t3.b)/2) WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; + +EXPLAIN (VERBOSE, 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.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; +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.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uprt1 t1 LEFT JOIN uprt2 t2 ON t1.a = t2.b) RIGHT JOIN uprt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t2.b = (t3.a + t3.b)/2) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t2.b = (t3.a + t3.b)/2) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uprt1 t1 RIGHT JOIN uprt2 t2 ON t1.a = t2.b) RIGHT JOIN uprt1_e t3 ON (t2.b = (t3.a + t3.b)/2) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM ((SELECT * FROM prt1 WHERE prt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM prt2 WHERE prt2.b % 25 = 0) t2 ON (t1.a = t2.b)) FULL JOIN (SELECT * FROM prt1_e WHERE prt1_e.a % 25 = 0) t3 ON (t1.a = (t3.a + t3.b)/2) ORDER BY t1.a, t2.b, t3.a + t3.b; +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM ((SELECT * FROM prt1 WHERE prt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM prt2 WHERE prt2.b % 25 = 0) t2 ON (t1.a = t2.b)) FULL JOIN (SELECT * FROM prt1_e WHERE prt1_e.a % 25 = 0) t3 ON (t1.a = (t3.a + t3.b)/2) ORDER BY t1.a, t2.b, t3.a + t3.b; +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM ((SELECT * FROM uprt1 WHERE uprt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM uprt2 WHERE uprt2.b % 25 = 0) t2 ON (t1.a = t2.b)) FULL JOIN (SELECT * FROM uprt1_e WHERE uprt1_e.a % 25 = 0) t3 ON (t1.a = (t3.a + t3.b)/2) ORDER BY t1.a, t2.b, t3.a + t3.b; + +-- Cases with non-nullable expressions in subquery results; +-- make sure these go to null as expected +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.phv, t2.b, t2.phv, t3.a + t3.b, t3.phv FROM ((SELECT 50 phv, * FROM prt1 WHERE prt1.a % 25 = 0) t1 FULL JOIN (SELECT 75 phv, * FROM prt2 WHERE prt2.b % 25 = 0) t2 ON (t1.a = t2.b)) FULL JOIN (SELECT 50 phv, * FROM prt1_e WHERE prt1_e.a % 25 = 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; +SELECT t1.a, t1.phv, t2.b, t2.phv, t3.a + t3.b, t3.phv FROM ((SELECT 50 phv, * FROM prt1 WHERE prt1.a % 25 = 0) t1 FULL JOIN (SELECT 75 phv, * FROM prt2 WHERE prt2.b % 25 = 0) t2 ON (t1.a = t2.b)) FULL JOIN (SELECT 50 phv, * FROM prt1_e WHERE prt1_e.a % 25 = 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; +SELECT t1.a, t1.phv, t2.b, t2.phv, t3.a + t3.b, t3.phv FROM ((SELECT 50 phv, * FROM uprt1 WHERE uprt1.a % 25 = 0) t1 FULL JOIN (SELECT 75 phv, * FROM uprt2 WHERE uprt2.b % 25 = 0) t2 ON (t1.a = t2.b)) FULL JOIN (SELECT 50 phv, * FROM uprt1_e WHERE uprt1_e.a % 25 = 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; + +-- Semi-join +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1, prt1_e t2 WHERE t1.b % 25 = 0 AND t1.b = (t2.a + t2.b)/2) AND t1.a % 25 = 0 ORDER BY t1.a; +SELECT t1.* FROM prt1 t1 WHERE t1.a IN (SELECT t1.b FROM prt2 t1, prt1_e t2 WHERE t1.b % 25 = 0 AND t1.b = (t2.a + t2.b)/2) AND t1.a % 25 = 0 ORDER BY t1.a; +SELECT t1.* FROM uprt1 t1 WHERE t1.a IN (SELECT t1.b FROM uprt2 t1, uprt1_e t2 WHERE t1.b % 25 = 0 AND t1.b = (t2.a + t2.b)/2) AND t1.a % 25 = 0 ORDER BY t1.a; + +EXPLAIN (VERBOSE, 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.a %25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a; +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.a %25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a; +SELECT t1.* FROM uprt1 t1 WHERE t1.a IN (SELECT t1.b FROM uprt2 t1 WHERE t1.b IN (SELECT (t1.a + t1.b)/2 FROM uprt1_e t1 WHERE t1.a %25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a; + +-- test merge joins with and without using indexes +SET enable_hashjoin TO off; +SET enable_nestloop TO off; + +CREATE INDEX iprt1_a on prt1(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 iprt2_b on prt2(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 iprt1_e_ab2 on prt1_e(((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)); + +ANALYZE prt1; +ANALYZE prt1_p1; +ANALYZE prt1_p2; +ANALYZE prt1_p3; +ANALYZE prt2; +ANALYZE prt2_p1; +ANALYZE prt2_p2; +ANALYZE prt2_p3; +ANALYZE prt1_e; +ANALYZE prt1_e_p1; +ANALYZE prt1_e_p2; +ANALYZE prt1_e_p3; + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t2.b = (t3.a + t3.b)/2) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t2.b = (t3.a + t3.b)/2) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uprt1 t1 RIGHT JOIN uprt2 t2 ON t1.a = t2.b) RIGHT JOIN uprt1_e t3 ON (t2.b = (t3.a + t3.b)/2) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; + +EXPLAIN (VERBOSE, 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.a %25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a; +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.a %25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a; +SELECT t1.* FROM uprt1 t1 WHERE t1.a IN (SELECT t1.b FROM uprt2 t1 WHERE t1.b IN (SELECT (t1.a + t1.b)/2 FROM uprt1_e t1 WHERE t1.a %25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a; + +EXPLAIN (VERBOSE, 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.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; +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.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uprt1 t1 LEFT JOIN uprt2 t2 ON t1.a = t2.b) RIGHT JOIN uprt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; + +SET enable_seqscan TO off; + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t2.b = (t3.a + t3.b)/2) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t2.b = (t3.a + t3.b)/2) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uprt1 t1 RIGHT JOIN uprt2 t2 ON t1.a = t2.b) RIGHT JOIN uprt1_e t3 ON (t2.b = (t3.a + t3.b)/2) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; + +EXPLAIN (VERBOSE, 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.a %25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a; +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.a %25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a; +SELECT t1.* FROM uprt1 t1 WHERE t1.a IN (SELECT t1.b FROM uprt2 t1 WHERE t1.b IN (SELECT (t1.a + t1.b)/2 FROM uprt1_e t1 WHERE t1.a %25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a; + +EXPLAIN (VERBOSE, 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.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; +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.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uprt1 t1 LEFT JOIN uprt2 t2 ON t1.a = t2.b) RIGHT JOIN uprt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; + +-- lateral references and parameterized paths +EXPLAIN (VERBOSE, COSTS OFF) +SELECT * FROM prt1 t1 LEFT JOIN LATERAL + (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM prt1 t2 JOIN prt2 t3 ON (t2.a = t3.b)) ss + ON t1.a = ss.t2a WHERE t1.a % 25 = 0 ORDER BY t1.a; +SELECT * FROM prt1 t1 LEFT JOIN LATERAL + (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM prt1 t2 JOIN prt2 t3 ON (t2.a = t3.b)) ss + ON t1.a = ss.t2a WHERE t1.a % 25 = 0 ORDER BY t1.a; +SELECT * FROM uprt1 t1 LEFT JOIN LATERAL + (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM uprt1 t2 JOIN uprt2 t3 ON (t2.a = t3.b)) ss + ON t1.a = ss.t2a WHERE t1.a % 25 = 0 ORDER BY t1.a; + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT * FROM prt1 t1 LEFT JOIN LATERAL + (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM prt1 t2 JOIN prt2 t3 ON (t2.a = t3.b)) ss + ON t1.b = ss.t2a WHERE t1.a % 25 = 0 ORDER BY t1.a; +SELECT * FROM prt1 t1 LEFT JOIN LATERAL + (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM prt1 t2 JOIN prt2 t3 ON (t2.a = t3.b)) ss + ON t1.b = ss.t2a WHERE t1.a % 25 = 0 ORDER BY t1.a; +SELECT * FROM uprt1 t1 LEFT JOIN LATERAL + (SELECT t2.a AS t2a, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM uprt1 t2 JOIN uprt2 t3 ON (t2.a = t3.b)) ss + ON t1.b = ss.t2a WHERE t1.a % 25 = 0 ORDER BY t1.a; + +RESET enable_hashjoin; +RESET enable_nestloop; +RESET enable_seqscan; + +-- +-- partitioned by multiple columns +-- +CREATE TABLE prt1_m (a int, b int, c varchar) PARTITION BY RANGE(a, ((a + b)/2)); +CREATE TABLE prt1_m_p1 PARTITION OF prt1_m FOR VALUES START (0, 0) END (250, 250); +CREATE TABLE prt1_m_p2 PARTITION OF prt1_m FOR VALUES START (250, 250) END (500, 500); +CREATE TABLE prt1_m_p3 PARTITION OF prt1_m FOR VALUES START (500, 500) END (600, 600); +INSERT INTO prt1_m SELECT i, i, to_char(i, 'FM0000') FROM generate_series(0, 599, 2) i; +ANALYZE prt1_m; +ANALYZE prt1_m_p1; +ANALYZE prt1_m_p2; +ANALYZE prt1_m_p3; +-- TODO: This table is created only for testing the results. Remove once +-- results are tested. +CREATE TABLE uprt1_m AS SELECT * FROM prt1_m; + +CREATE TABLE prt2_m (a int, b int, c varchar) PARTITION BY RANGE(((b + a)/2), b); +CREATE TABLE prt2_m_p1 PARTITION OF prt2_m FOR VALUES START (0, 0) END (250, 250); +CREATE TABLE prt2_m_p2 PARTITION OF prt2_m FOR VALUES START (250, 250) END (500, 500); +CREATE TABLE prt2_m_p3 PARTITION OF prt2_m FOR VALUES START (500, 500) END (600, 600); +INSERT INTO prt2_m SELECT i, i, to_char(i, 'FM0000') FROM generate_series(0, 599, 3) i; +ANALYZE prt2_m; +ANALYZE prt2_m_p1; +ANALYZE prt2_m_p2; +ANALYZE prt2_m_p3; +-- TODO: This table is created only for testing the results. Remove once +-- results are tested. +CREATE TABLE uprt2_m AS SELECT * FROM prt2_m; + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_m t1, prt2_m t2 WHERE t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2 AND t1.a % 25 = 0 ORDER BY t1.a, t2.b; +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_m t1, prt2_m t2 WHERE t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2 AND t1.a % 25 = 0 ORDER BY t1.a, t2.b; +SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1_m t1, uprt2_m t2 WHERE t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2 AND t1.a % 25 = 0 ORDER BY t1.a, t2.b; + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_m t1 LEFT JOIN prt2_m t2 ON t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2 WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b; +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_m t1 LEFT JOIN prt2_m t2 ON t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2 WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b; +SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1_m t1 LEFT JOIN uprt2_m t2 ON t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2 WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b; + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_m t1 RIGHT JOIN prt2_m t2 ON t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2 WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b; +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_m t1 RIGHT JOIN prt2_m t2 ON t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2 WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b; +SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1_m t1 RIGHT JOIN uprt2_m t2 ON t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2 WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b; + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_m WHERE prt1_m.a % 25 = 0) t1 FULL JOIN (SELECT * FROM prt2_m WHERE prt2_m.b % 25 = 0) t2 ON (t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2) ORDER BY t1.a, t2.b; +SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_m WHERE prt1_m.a % 25 = 0) t1 FULL JOIN (SELECT * FROM prt2_m WHERE prt2_m.b % 25 = 0) t2 ON (t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2) ORDER BY t1.a, t2.b; +SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM uprt1_m t1 WHERE t1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM uprt2_m t2 WHERE t2.b % 25 = 0) t2 ON (t1.a = (t2.b + t2.a)/2 AND t2.b = (t1.a + t1.b)/2) ORDER BY t1.a, t2.b; + +-- +-- multi-leveled partitions +-- +CREATE TABLE prt1_l (a int, b int, c varchar) PARTITION BY RANGE(a); +CREATE TABLE prt1_l_p1 PARTITION OF prt1_l FOR VALUES START (0) END (250) PARTITION BY RANGE (b); +CREATE TABLE prt1_l_p1_p1 PARTITION OF prt1_l_p1 FOR VALUES START (0) END (100); +CREATE TABLE prt1_l_p1_p2 PARTITION OF prt1_l_p1 FOR VALUES START (100) END (250); +CREATE TABLE prt1_l_p2 PARTITION OF prt1_l FOR VALUES START (250) END (500) PARTITION BY RANGE (c); +CREATE TABLE prt1_l_p2_p1 PARTITION OF prt1_l_p2 FOR VALUES START ('0250') END ('0400'); +CREATE TABLE prt1_l_p2_p2 PARTITION OF prt1_l_p2 FOR VALUES START ('0400') END ('0500'); +CREATE TABLE prt1_l_p3 PARTITION OF prt1_l FOR VALUES START (500) END (600) PARTITION BY RANGE ((b + a)); +CREATE TABLE prt1_l_p3_p1 PARTITION OF prt1_l_p3 FOR VALUES START (1000) END (1100); +CREATE TABLE prt1_l_p3_p2 PARTITION OF prt1_l_p3 FOR VALUES START (1100) END (1200); +INSERT INTO prt1_l SELECT i, i, to_char(i, 'FM0000') FROM generate_series(0, 599, 2) i; +ANALYZE prt1_l; +ANALYZE prt1_l_p1; +ANALYZE prt1_l_p1_p1; +ANALYZE prt1_l_p1_p2; +ANALYZE prt1_l_p2; +ANALYZE prt1_l_p2_p1; +ANALYZE prt1_l_p2_p2; +ANALYZE prt1_l_p3; +ANALYZE prt1_l_p3_p1; +ANALYZE prt1_l_p3_p2; +-- TODO: This table is created only for testing the results. Remove once +-- results are tested. +CREATE TABLE uprt1_l AS SELECT * FROM prt1_l; + +CREATE TABLE prt2_l (a int, b int, c varchar) PARTITION BY RANGE(b); +CREATE TABLE prt2_l_p1 PARTITION OF prt2_l FOR VALUES START (0) END (250) PARTITION BY RANGE (a); +CREATE TABLE prt2_l_p1_p1 PARTITION OF prt2_l_p1 FOR VALUES START (0) END (100); +CREATE TABLE prt2_l_p1_p2 PARTITION OF prt2_l_p1 FOR VALUES START (100) END (250); +CREATE TABLE prt2_l_p2 PARTITION OF prt2_l FOR VALUES START (250) END (500) PARTITION BY RANGE (c); +CREATE TABLE prt2_l_p2_p1 PARTITION OF prt2_l_p2 FOR VALUES START ('0250') END ('0400'); +CREATE TABLE prt2_l_p2_p2 PARTITION OF prt2_l_p2 FOR VALUES START ('0400') END ('0500'); +CREATE TABLE prt2_l_p3 PARTITION OF prt2_l FOR VALUES START (500) END (600) PARTITION BY RANGE ((a + b)); +CREATE TABLE prt2_l_p3_p1 PARTITION OF prt2_l_p3 FOR VALUES START (1000) END (1100); +CREATE TABLE prt2_l_p3_p2 PARTITION OF prt2_l_p3 FOR VALUES START (1100) END (1200); +INSERT INTO prt2_l SELECT i, i, to_char(i, 'FM0000') FROM generate_series(0, 599, 3) i; +ANALYZE prt2_l; +ANALYZE prt2_l_p1; +ANALYZE prt2_l_p1_p1; +ANALYZE prt2_l_p1_p2; +ANALYZE prt2_l_p2; +ANALYZE prt2_l_p2_p1; +ANALYZE prt2_l_p2_p2; +ANALYZE prt2_l_p3; +ANALYZE prt2_l_p3_p1; +ANALYZE prt2_l_p3_p2; +-- TODO: This table is created only for testing the results. Remove once +-- results are tested. +CREATE TABLE uprt2_l AS SELECT * FROM prt2_l; + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_l t1, prt2_l t2 WHERE t1.a = t2.b AND t1.b = t2.a AND t1.c = t2.c AND t1.b + t1.a = t2.a + t2.b AND t1.a % 25 = 0 ORDER BY t1.a, t2.b; +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_l t1, prt2_l t2 WHERE t1.a = t2.b AND t1.b = t2.a AND t1.c = t2.c AND t1.b + t1.a = t2.a + t2.b AND t1.a % 25 = 0 ORDER BY t1.a, t2.b; +SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1_l t1, uprt2_l t2 WHERE t1.a = t2.b AND t1.b = t2.a AND t1.c = t2.c AND t1.b + t1.a = t2.a + t2.b AND t1.a % 25 = 0 ORDER BY t1.a, t2.b; + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_l t1 LEFT JOIN prt2_l t2 ON t1.a = t2.b AND t1.b = t2.a AND t1.c = t2.c AND t1.b + t1.a = t2.a + t2.b WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b; +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_l t1 LEFT JOIN prt2_l t2 ON t1.a = t2.b AND t1.b = t2.a AND t1.c = t2.c AND t1.b + t1.a = t2.a + t2.b WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b; +SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1_l t1 LEFT JOIN uprt2_l t2 ON t1.a = t2.b AND t1.b = t2.a AND t1.c = t2.c AND t1.b + t1.a = t2.a + t2.b WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b; + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_l t1 RIGHT JOIN prt2_l t2 ON t1.a = t2.b AND t1.b = t2.a AND t1.c = t2.c AND t1.b + t1.a = t2.a + t2.b WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b; +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_l t1 RIGHT JOIN prt2_l t2 ON t1.a = t2.b AND t1.b = t2.a AND t1.c = t2.c AND t1.b + t1.a = t2.a + t2.b WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b; +SELECT t1.a, t1.c, t2.b, t2.c FROM uprt1_l t1 RIGHT JOIN uprt2_l t2 ON t1.a = t2.b AND t1.b = t2.a AND t1.c = t2.c AND t1.b + t1.a = t2.a + t2.b WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b; + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_l WHERE prt1_l.a % 25 = 0) t1 FULL JOIN (SELECT * FROM prt2_l WHERE prt2_l.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.b = t2.a AND t1.c = t2.c AND t1.b + t1.a = t2.a + t2.b) ORDER BY t1.a, t2.b; +SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1_l WHERE prt1_l.a % 25 = 0) t1 FULL JOIN (SELECT * FROM prt2_l WHERE prt2_l.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.b = t2.a AND t1.c = t2.c AND t1.b + t1.a = t2.a + t2.b) ORDER BY t1.a, t2.b; +SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM uprt1_l t1 WHERE t1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM uprt2_l t2 WHERE t2.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.b = t2.a AND t1.c = t2.c AND t1.b + t1.a = t2.a + t2.b) ORDER BY t1.a, t2.b; + +-- lateral reference +EXPLAIN (VERBOSE, COSTS OFF) +SELECT * FROM prt1_l t1 LEFT JOIN LATERAL + (SELECT t2.a AS t2a, t2.c AS t2c, t2.b AS t2b, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM prt1_l t2 JOIN prt2_l t3 ON (t2.a = t3.b AND t2.b = t3.a AND t2.c = t3.c AND t2.b + t2.a = t3.a + t3.b)) ss + ON t1.a = ss.t2a AND t1.b = ss.t2a AND t1.c = ss.t2c AND t1.b + t1.a = ss.t2a + ss.t2b WHERE t1.a % 25 = 0 ORDER BY t1.a; +SELECT * FROM prt1_l t1 LEFT JOIN LATERAL + (SELECT t2.a AS t2a, t2.c AS t2c, t2.b AS t2b, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM prt1_l t2 JOIN prt2_l t3 ON (t2.a = t3.b AND t2.b = t3.a AND t2.c = t3.c AND t2.b + t2.a = t3.a + t3.b)) ss + ON t1.a = ss.t2a AND t1.b = ss.t2a AND t1.c = ss.t2c AND t1.b + t1.a = ss.t2a + ss.t2b WHERE t1.a % 25 = 0 ORDER BY t1.a; +SELECT * FROM uprt1_l t1 LEFT JOIN LATERAL + (SELECT t2.a AS t2a, t2.c AS t2c, t2.b AS t2b, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM uprt1_l t2 JOIN uprt2_l t3 ON (t2.a = t3.b AND t2.b = t3.a AND t2.c = t3.c AND t2.b + t2.a = t3.a + t3.b)) ss + ON t1.a = ss.t2a AND t1.b = ss.t2a AND t1.c = ss.t2c AND t1.b + t1.a = ss.t2a + ss.t2b WHERE t1.a % 25 = 0 ORDER BY t1.a; + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT * FROM prt1_l t1 LEFT JOIN LATERAL + (SELECT t2.a AS t2a, t2.c AS t2c, t2.b AS t2b, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM prt1_l t2 JOIN prt2_l t3 ON (t2.a = t3.b AND t2.b = t3.a AND t2.c = t3.c AND t2.b + t2.a = t3.a + t3.b)) ss + ON t1.b = ss.t2a AND t1.b = ss.t2a AND t1.c = ss.t2c AND t1.b + t1.a = ss.t2a + ss.t2b WHERE t1.a % 25 = 0 ORDER BY t1.a; +SELECT * FROM prt1_l t1 LEFT JOIN LATERAL + (SELECT t2.a AS t2a, t2.c AS t2c, t2.b AS t2b, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM prt1_l t2 JOIN prt2_l t3 ON (t2.a = t3.b AND t2.b = t3.a AND t2.c = t3.c AND t2.b + t2.a = t3.a + t3.b)) ss + ON t1.b = ss.t2a AND t1.b = ss.t2a AND t1.c = ss.t2c AND t1.b + t1.a = ss.t2a + ss.t2b WHERE t1.a % 25 = 0 ORDER BY t1.a; +SELECT * FROM uprt1_l t1 LEFT JOIN LATERAL + (SELECT t2.a AS t2a, t2.c AS t2c, t2.b AS t2b, t3.a AS t3a, least(t1.a,t2.a,t3.a) FROM uprt1_l t2 JOIN uprt2_l t3 ON (t2.a = t3.b AND t2.b = t3.a AND t2.c = t3.c AND t2.b + t2.a = t3.a + t3.b)) ss + ON t1.b = ss.t2a AND t1.b = ss.t2a AND t1.c = ss.t2c AND t1.b + t1.a = ss.t2a + ss.t2b WHERE t1.a % 25 = 0 ORDER BY t1.a; + +-- +-- 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; +ANALYZE plt1_p1; +ANALYZE plt1_p2; +ANALYZE plt1_p3; +-- TODO: This table is created only for testing the results. Remove once +-- results are tested. +CREATE TABLE uplt1 AS SELECT * FROM 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; +ANALYZE plt2_p1; +ANALYZE plt2_p2; +ANALYZE plt2_p3; +-- TODO: This table is created only for testing the results. Remove once +-- results are tested. +CREATE TABLE uplt2 AS SELECT * FROM plt2; + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM plt1 t1, plt2 t2 WHERE t1.c = t2.c AND t1.a = t2.a AND t1.a % 25 = 0 ORDER BY t1.a, t2.b; +SELECT t1.a, t1.c, t2.b, t2.c FROM plt1 t1, plt2 t2 WHERE t1.c = t2.c AND t1.a = t2.a AND t1.a % 25 = 0 ORDER BY t1.a, t2.b; +SELECT t1.a, t1.c, t2.b, t2.c FROM uplt1 t1, uplt2 t2 WHERE t1.c = t2.c AND t1.a = t2.a AND t1.a % 25 = 0 ORDER BY t1.a, t2.b; + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.a = t2.a AND t1.c = t2.c WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b; +SELECT t1.a, t1.c, t2.b, t2.c FROM plt1 t1 LEFT JOIN plt2 t2 ON t1.a = t2.a AND t1.c = t2.c WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b; +SELECT t1.a, t1.c, t2.b, t2.c FROM uplt1 t1 LEFT JOIN uplt2 t2 ON t1.a = t2.a AND t1.c = t2.c WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b; + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b; +SELECT t1.a, t1.c, t2.b, t2.c FROM plt1 t1 RIGHT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b; +SELECT t1.a, t1.c, t2.b, t2.c FROM uplt1 t1 RIGHT JOIN uplt2 t2 ON t1.a = t2.b AND t1.c = t2.c WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b; + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM plt1 WHERE plt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM plt2 WHERE plt2.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c) ORDER BY t1.a, t2.b; +SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM plt1 WHERE plt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM plt2 WHERE plt2.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c) ORDER BY t1.a, t2.b; +SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM uplt1 t1 WHERE t1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM uplt2 t2 WHERE t2.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c) ORDER BY t1.a, t2.b; + +-- Cases with non-nullable expressions in subquery results; +-- make sure these go to null as expected +EXPLAIN (VERBOSE, COSTS OFF) +SELECT sum(t1.a), t1.c, avg(t2.b), t2.c FROM (SELECT 50 phv, * FROM plt1 WHERE plt1.a % 25 = 0) t1 FULL JOIN (SELECT 75 phv, * FROM plt2 WHERE plt2.b % 25 = 0) t2 ON (t1.c = t2.c AND t1.a = t2.b) GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c; +SELECT sum(t1.a), t1.c, avg(t2.b), t2.c FROM (SELECT 50 phv, * FROM plt1 WHERE plt1.a % 25 = 0) t1 FULL JOIN (SELECT 75 phv, * FROM plt2 WHERE plt2.b % 25 = 0) t2 ON (t1.c = t2.c AND t1.a = t2.b) GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c; +SELECT sum(t1.a), t1.c, avg(t2.b), t2.c FROM (SELECT 50 phv, * FROM uplt1 WHERE uplt1.a % 25 = 0) t1 FULL JOIN (SELECT 75 phv, * FROM uplt2 WHERE uplt2.b % 25 = 0) t2 ON (t1.c = t2.c AND t1.a = t2.b) GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c; + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT sum(t1.a), t1.c, sum(t1.phv), avg(t2.b), t2.c, avg(t2.phv) FROM (SELECT 25 phv, * FROM plt1 WHERE plt1.a % 25 = 0) t1 FULL JOIN (SELECT 50 phv, * FROM plt2 WHERE plt2.b % 25 = 0) t2 ON (t1.c = t2.c AND t1.a = t2.b) GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c; +SELECT sum(t1.a), t1.c, sum(t1.phv), avg(t2.b), t2.c, avg(t2.phv) FROM (SELECT 25 phv, * FROM plt1 WHERE plt1.a % 25 = 0) t1 FULL JOIN (SELECT 50 phv, * FROM plt2 WHERE plt2.b % 25 = 0) t2 ON (t1.c = t2.c AND t1.a = t2.b) GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c; +SELECT sum(t1.a), t1.c, sum(t1.phv), avg(t2.b), t2.c, avg(t2.phv) FROM (SELECT 25 phv, * FROM uplt1 WHERE uplt1.a % 25 = 0) t1 FULL JOIN (SELECT 50 phv, * FROM uplt2 WHERE uplt2.b % 25 = 0) t2 ON (t1.c = t2.c AND t1.a = t2.b) GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c; + +-- Join with pruned partitions from joining relations +EXPLAIN (VERBOSE, COSTS OFF) +SELECT sum(t1.a), t1.c, avg(t2.b), t2.c FROM plt1 t1, plt2 t2 WHERE t1.c = t2.c AND t1.c NOT IN ('0001', '0005', '0002', '0009') AND t2.c NOT IN ('0000', '0003', '0004', '0010') GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c; +SELECT sum(t1.a), t1.c, avg(t2.b), t2.c FROM plt1 t1, plt2 t2 WHERE t1.c = t2.c AND t1.c NOT IN ('0001', '0005', '0002', '0009') AND t2.c NOT IN ('0000', '0003', '0004', '0010') GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c; +SELECT sum(t1.a), t1.c, avg(t2.b), t2.c FROM uplt1 t1, uplt2 t2 WHERE t1.c = t2.c AND t1.c NOT IN ('0001', '0005', '0002', '0009') AND t2.c NOT IN ('0000', '0003', '0004', '0010') GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c; + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT sum(t1.a), t1.c, sum(t2.b), t2.c FROM (SELECT * FROM plt1 t1 WHERE t1.c NOT IN ('0001', '0005', '0002', '0009')) t1 LEFT JOIN (SELECT * FROM plt2 t2 WHERE t2.c NOT IN ('0000', '0003', '0004', '0010')) t2 ON t1.c = t2.c GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c; +SELECT sum(t1.a), t1.c, sum(t2.b), t2.c FROM (SELECT * FROM plt1 t1 WHERE t1.c NOT IN ('0001', '0005', '0002', '0009')) t1 LEFT JOIN (SELECT * FROM plt2 t2 WHERE t2.c NOT IN ('0000', '0003', '0004', '0010')) t2 ON t1.c = t2.c GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c; +SELECT sum(t1.a), t1.c, sum(t2.b), t2.c FROM (SELECT * FROM uplt1 t1 WHERE t1.c NOT IN ('0001', '0005', '0002', '0009')) t1 LEFT JOIN (SELECT * FROM uplt2 t2 WHERE t2.c NOT IN ('0000', '0003', '0004', '0010')) t2 ON t1.c = t2.c GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c; + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT sum(t1.a), t1.c, sum(t2.b), t2.c FROM (SELECT * FROM plt1 t1 WHERE t1.c NOT IN ('0001', '0005', '0002', '0009')) t1 RIGHT JOIN (SELECT * FROM plt2 t2 WHERE t2.c NOT IN ('0000', '0003', '0004', '0010')) t2 ON t1.c = t2.c GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c; +SELECT sum(t1.a), t1.c, sum(t2.b), t2.c FROM (SELECT * FROM plt1 t1 WHERE t1.c NOT IN ('0001', '0005', '0002', '0009')) t1 RIGHT JOIN (SELECT * FROM plt2 t2 WHERE t2.c NOT IN ('0000', '0003', '0004', '0010')) t2 ON t1.c = t2.c GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c; +SELECT sum(t1.a), t1.c, sum(t2.b), t2.c FROM (SELECT * FROM uplt1 t1 WHERE t1.c NOT IN ('0001', '0005', '0002', '0009')) t1 RIGHT JOIN (SELECT * FROM uplt2 t2 WHERE t2.c NOT IN ('0000', '0003', '0004', '0010')) t2 ON t1.c = t2.c GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c; + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT sum(t1.a), t1.c, sum(t2.b), t2.c FROM (SELECT * FROM plt1 t1 WHERE t1.c NOT IN ('0001', '0005', '0002', '0009')) t1 FULL JOIN (SELECT * FROM plt2 t2 WHERE t2.c NOT IN ('0000', '0003', '0004', '0010')) t2 ON t1.c = t2.c GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c; +SELECT sum(t1.a), t1.c, sum(t2.b), t2.c FROM (SELECT * FROM plt1 t1 WHERE t1.c NOT IN ('0001', '0005', '0002', '0009')) t1 FULL JOIN (SELECT * FROM plt2 t2 WHERE t2.c NOT IN ('0000', '0003', '0004', '0010')) t2 ON t1.c = t2.c GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c; +SELECT sum(t1.a), t1.c, sum(t2.b), t2.c FROM (SELECT * FROM uplt1 t1 WHERE t1.c NOT IN ('0001', '0005', '0002', '0009')) t1 FULL JOIN (SELECT * FROM uplt2 t2 WHERE t2.c NOT IN ('0000', '0003', '0004', '0010')) t2 ON t1.c = t2.c GROUP BY t1.c, t2.c ORDER BY t1.c, t2.c; + +-- Semi-join +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.* FROM plt1 t1 WHERE t1.c IN (SELECT t1.c FROM plt2 t1 WHERE t1.b % 25 = 0) AND t1.a % 25 = 0 ORDER BY t1.a; +SELECT t1.* FROM plt1 t1 WHERE t1.c IN (SELECT t1.c FROM plt2 t1 WHERE t1.b % 25 = 0) AND t1.a % 25 = 0 ORDER BY t1.a; +SELECT t1.* FROM uplt1 t1 WHERE t1.c IN (SELECT t1.c FROM uplt2 t1 WHERE t1.b % 25 = 0) AND t1.a % 25 = 0 ORDER BY t1.a; + +-- +-- 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; +ANALYZE plt1_e_p1; +ANALYZE plt1_e_p2; +ANALYZE plt1_e_p3; +-- TODO: This table is created only for testing the results. Remove once +-- results are tested. +CREATE TABLE uplt1_e AS SELECT * FROM plt1_e; + +CREATE TABLE plt2_e (a int, b int, c text) PARTITION BY LIST(ltrim(c, 'A')); +CREATE TABLE plt2_e_p1 PARTITION OF plt2_e FOR VALUES IN ('0000', '0003', '0004', '0010'); +CREATE TABLE plt2_e_p2 PARTITION OF plt2_e FOR VALUES IN ('0001', '0005', '0002', '0009'); +CREATE TABLE plt2_e_p3 PARTITION OF plt2_e FOR VALUES IN ('0006', '0007', '0008', '0011'); +INSERT INTO plt2_e SELECT i, i, 'A' || to_char(i/50, 'FM0000') FROM generate_series(0, 599, 3) i; +ANALYZE plt2_e; +ANALYZE plt2_e_p1; +ANALYZE plt2_e_p2; +ANALYZE plt2_e_p3; +-- TODO: This table is created only for testing the results. Remove once +-- results are tested. +CREATE TABLE uplt2_e AS SELECT * FROM plt2_e; + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM plt1_e t1, plt2_e t2 WHERE t1.a = t2.b AND ltrim(t1.c, 'A') = ltrim(t2.c, 'A') AND t1.a % 25 = 0 ORDER BY t1.a, t2.b; +SELECT t1.a, t1.c, t2.b, t2.c FROM plt1_e t1, plt2_e t2 WHERE t1.a = t2.b AND ltrim(t1.c, 'A') = ltrim(t2.c, 'A') AND t1.a % 25 = 0 ORDER BY t1.a, t2.b; +SELECT t1.a, t1.c, t2.b, t2.c FROM uplt1_e t1, uplt2_e t2 WHERE t1.a = t2.b AND ltrim(t1.c, 'A') = ltrim(t2.c, 'A') AND t1.a % 25 = 0 ORDER BY t1.a, t2.b; + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM plt1_e t1 LEFT JOIN plt2_e t2 ON t1.a = t2.b AND ltrim(t1.c, 'A') = ltrim(t2.c, 'A') WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b; +SELECT t1.a, t1.c, t2.b, t2.c FROM plt1_e t1 LEFT JOIN plt2_e t2 ON t1.a = t2.b AND ltrim(t1.c, 'A') = ltrim(t2.c, 'A') WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b; +SELECT t1.a, t1.c, t2.b, t2.c FROM uplt1_e t1 LEFT JOIN uplt2_e t2 ON t1.a = t2.b AND ltrim(t1.c, 'A') = ltrim(t2.c, 'A') WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b; + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM plt1_e t1 RIGHT JOIN plt2_e t2 ON t1.a = t2.b AND ltrim(t1.c, 'A') = ltrim(t2.c, 'A') WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b; +SELECT t1.a, t1.c, t2.b, t2.c FROM plt1_e t1 RIGHT JOIN plt2_e t2 ON t1.a = t2.b AND ltrim(t1.c, 'A') = ltrim(t2.c, 'A') WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b; +SELECT t1.a, t1.c, t2.b, t2.c FROM uplt1_e t1 RIGHT JOIN uplt2_e t2 ON t1.a = t2.b AND ltrim(t1.c, 'A') = ltrim(t2.c, 'A') WHERE t2.b % 25 = 0 ORDER BY t1.a, t2.b; + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM plt1_e WHERE plt1_e.a % 25 = 0) t1 FULL JOIN (SELECT * FROM plt2_e WHERE plt2_e.b % 25 = 0) t2 ON (t1.a = t2.b AND ltrim(t1.c, 'A') = ltrim(t2.c, 'A')) ORDER BY t1.a, t2.b; +SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM plt1_e WHERE plt1_e.a % 25 = 0) t1 FULL JOIN (SELECT * FROM plt2_e WHERE plt2_e.b % 25 = 0) t2 ON (t1.a = t2.b AND ltrim(t1.c, 'A') = ltrim(t2.c, 'A')) ORDER BY t1.a, t2.b; +SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM uplt1_e t1 WHERE t1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM uplt2_e t2 WHERE t2.b % 25 = 0) t2 ON (t1.a = t2.b AND ltrim(t1.c, 'A') = ltrim(t2.c, 'A')) ORDER BY t1.a, t2.b; + +-- +-- N-way join +-- +EXPLAIN (VERBOSE, 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.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.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 uplt1 t1, uplt2 t2, uplt1_e t3 WHERE 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; + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (plt1 t1 LEFT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c) LEFT JOIN plt1_e t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (plt1 t1 LEFT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c) LEFT JOIN plt1_e t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uplt1 t1 LEFT JOIN uplt2 t2 ON t1.a = t2.b AND t1.c = t2.c) LEFT JOIN uplt1_e t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (plt1 t1 LEFT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c) LEFT JOIN plt1_e t3 ON (t2.b = t3.a AND t2.c = ltrim(t3.c, 'A')) WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (plt1 t1 LEFT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c) LEFT JOIN plt1_e t3 ON (t2.b = t3.a AND t2.c = ltrim(t3.c, 'A')) WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uplt1 t1 LEFT JOIN uplt2 t2 ON t1.a = t2.b AND t1.c = t2.c) LEFT JOIN uplt1_e t3 ON (t2.b = t3.a AND t2.c = ltrim(t3.c, 'A')) WHERE t1.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (plt1 t1 LEFT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c) RIGHT JOIN plt1_e t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (plt1 t1 LEFT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c) RIGHT JOIN plt1_e t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uplt1 t1 LEFT JOIN uplt2 t2 ON t1.a = t2.b AND t1.c = t2.c) RIGHT JOIN uplt1_e t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (plt1 t1 RIGHT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c) RIGHT JOIN plt1_e t3 ON (t2.b = t3.a AND t2.c = ltrim(t3.c, 'A')) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (plt1 t1 RIGHT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c) RIGHT JOIN plt1_e t3 ON (t2.b = t3.a AND t2.c = ltrim(t3.c, 'A')) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uplt1 t1 RIGHT JOIN uplt2 t2 ON t1.a = t2.b AND t1.c = t2.c) RIGHT JOIN uplt1_e t3 ON (t2.b = t3.a AND t2.c = ltrim(t3.c, 'A')) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM ((SELECT * FROM plt1 WHERE plt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM plt2 WHERE plt2.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c)) FULL JOIN (SELECT * FROM plt1_e WHERE plt1_e.a % 25 = 0) t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) ORDER BY t1.a, t2.b, t3.a + t3.b; +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM ((SELECT * FROM plt1 WHERE plt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM plt2 WHERE plt2.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c)) FULL JOIN (SELECT * FROM plt1_e WHERE plt1_e.a % 25 = 0) t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) ORDER BY t1.a, t2.b, t3.a + t3.b; +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM ((SELECT * FROM uplt1 WHERE uplt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM uplt2 WHERE uplt2.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c)) FULL JOIN (SELECT * FROM uplt1_e WHERE uplt1_e.a % 25 = 0) t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) ORDER BY t1.a, t2.b, t3.a + t3.b; + +-- Semi-join +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.* FROM plt1 t1 WHERE t1.c IN (SELECT t1.c FROM plt2 t1, plt1_e t2 WHERE t1.c = ltrim(t2.c, 'A')) AND t1.a % 25 = 0 ORDER BY t1.a; +SELECT t1.* FROM plt1 t1 WHERE t1.c IN (SELECT t1.c FROM plt2 t1, plt1_e t2 WHERE t1.c = ltrim(t2.c, 'A')) AND t1.a % 25 = 0 ORDER BY t1.a; +SELECT t1.* FROM uplt1 t1 WHERE t1.c IN (SELECT t1.c FROM uplt2 t1, uplt1_e t2 WHERE t1.c = ltrim(t2.c, 'A')) AND t1.a % 25 = 0 ORDER BY t1.a; + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.* FROM plt1 t1 WHERE t1.c IN (SELECT t1.c FROM plt2 t1 WHERE t1.c IN (SELECT ltrim(t1.c, 'A') FROM plt1_e t1 WHERE t1.a % 25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a; +SELECT t1.* FROM plt1 t1 WHERE t1.c IN (SELECT t1.c FROM plt2 t1 WHERE t1.c IN (SELECT ltrim(t1.c, 'A') FROM plt1_e t1 WHERE t1.a % 25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a; +SELECT t1.* FROM uplt1 t1 WHERE t1.c IN (SELECT t1.c FROM uplt2 t1 WHERE t1.c IN (SELECT ltrim(t1.c, 'A') FROM uplt1_e t1 WHERE t1.a % 25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a; + +-- test merge join with and without index scan +CREATE INDEX iplt1_c on plt1(c); +CREATE INDEX iplt1_p1_c on plt1_p1(c); +CREATE INDEX iplt1_p2_c on plt1_p2(c); +CREATE INDEX iplt1_p3_c on plt1_p3(c); +CREATE INDEX iplt2_c on plt2(c); +CREATE INDEX iplt2_p1_c on plt2_p1(c); +CREATE INDEX iplt2_p2_c on plt2_p2(c); +CREATE INDEX iplt2_p3_c on plt2_p3(c); +CREATE INDEX iplt1_e_c on plt1_e(ltrim(c, 'A')); +CREATE INDEX iplt1_e_p1_c on plt1_e_p1(ltrim(c, 'A')); +CREATE INDEX iplt1_e_p2_c on plt1_e_p2(ltrim(c, 'A')); +CREATE INDEX iplt1_e_p3_c on plt1_e_p3(ltrim(c, 'A')); + +ANALYZE plt1; +ANALYZE plt1_p1; +ANALYZE plt1_p2; +ANALYZE plt1_p3; +ANALYZE plt2; +ANALYZE plt2_p1; +ANALYZE plt2_p2; +ANALYZE plt2_p3; +ANALYZE plt1_e; +ANALYZE plt1_e_p1; +ANALYZE plt1_e_p2; +ANALYZE plt1_e_p3; + +SET enable_hashjoin TO off; +SET enable_nestloop TO off; + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (plt1 t1 RIGHT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c) RIGHT JOIN plt1_e t3 ON (t2.b = t3.a AND t2.c = ltrim(t3.c, 'A')) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (plt1 t1 RIGHT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c) RIGHT JOIN plt1_e t3 ON (t2.b = t3.a AND t2.c = ltrim(t3.c, 'A')) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uplt1 t1 RIGHT JOIN uplt2 t2 ON t1.a = t2.b AND t1.c = t2.c) RIGHT JOIN uplt1_e t3 ON (t2.b = t3.a AND t2.c = ltrim(t3.c, 'A')) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM ((SELECT * FROM plt1 WHERE plt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM plt2 WHERE plt2.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c)) FULL JOIN (SELECT * FROM plt1_e WHERE plt1_e.a % 25 = 0) t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) ORDER BY t1.a, t2.b, t3.a + t3.b; +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM ((SELECT * FROM plt1 WHERE plt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM plt2 WHERE plt2.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c)) FULL JOIN (SELECT * FROM plt1_e WHERE plt1_e.a % 25 = 0) t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) ORDER BY t1.a, t2.b, t3.a + t3.b; +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM ((SELECT * FROM uplt1 WHERE uplt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM uplt2 WHERE uplt2.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c)) FULL JOIN (SELECT * FROM uplt1_e WHERE uplt1_e.a % 25 = 0) t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) ORDER BY t1.a, t2.b, t3.a + t3.b; + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.* FROM plt1 t1 WHERE t1.c IN (SELECT t1.c FROM plt2 t1 WHERE t1.c IN (SELECT ltrim(t1.c, 'A') FROM plt1_e t1 WHERE t1.a % 25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a; +SELECT t1.* FROM plt1 t1 WHERE t1.c IN (SELECT t1.c FROM plt2 t1 WHERE t1.c IN (SELECT ltrim(t1.c, 'A') FROM plt1_e t1 WHERE t1.a % 25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a; +SELECT t1.* FROM uplt1 t1 WHERE t1.c IN (SELECT t1.c FROM uplt2 t1 WHERE t1.c IN (SELECT ltrim(t1.c, 'A') FROM uplt1_e t1 WHERE t1.a % 25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a; + +SET enable_seqscan TO off; + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (plt1 t1 RIGHT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c) RIGHT JOIN plt1_e t3 ON (t2.b = t3.a AND t2.c = ltrim(t3.c, 'A')) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (plt1 t1 RIGHT JOIN plt2 t2 ON t1.a = t2.b AND t1.c = t2.c) RIGHT JOIN plt1_e t3 ON (t2.b = t3.a AND t2.c = ltrim(t3.c, 'A')) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (uplt1 t1 RIGHT JOIN uplt2 t2 ON t1.a = t2.b AND t1.c = t2.c) RIGHT JOIN uplt1_e t3 ON (t2.b = t3.a AND t2.c = ltrim(t3.c, 'A')) WHERE t3.a % 25 = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM ((SELECT * FROM plt1 WHERE plt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM plt2 WHERE plt2.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c)) FULL JOIN (SELECT * FROM plt1_e WHERE plt1_e.a % 25 = 0) t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) ORDER BY t1.a, t2.b, t3.a + t3.b; +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM ((SELECT * FROM plt1 WHERE plt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM plt2 WHERE plt2.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c)) FULL JOIN (SELECT * FROM plt1_e WHERE plt1_e.a % 25 = 0) t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) ORDER BY t1.a, t2.b, t3.a + t3.b; +SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM ((SELECT * FROM uplt1 WHERE uplt1.a % 25 = 0) t1 FULL JOIN (SELECT * FROM uplt2 WHERE uplt2.b % 25 = 0) t2 ON (t1.a = t2.b AND t1.c = t2.c)) FULL JOIN (SELECT * FROM uplt1_e WHERE uplt1_e.a % 25 = 0) t3 ON (t1.a = t3.a AND ltrim(t3.c, 'A') = t1.c) ORDER BY t1.a, t2.b, t3.a + t3.b; + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.* FROM plt1 t1 WHERE t1.c IN (SELECT t1.c FROM plt2 t1 WHERE t1.c IN (SELECT ltrim(t1.c, 'A') FROM plt1_e t1 WHERE t1.a % 25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a; +SELECT t1.* FROM plt1 t1 WHERE t1.c IN (SELECT t1.c FROM plt2 t1 WHERE t1.c IN (SELECT ltrim(t1.c, 'A') FROM plt1_e t1 WHERE t1.a % 25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a; +SELECT t1.* FROM uplt1 t1 WHERE t1.c IN (SELECT t1.c FROM uplt2 t1 WHERE t1.c IN (SELECT ltrim(t1.c, 'A') FROM uplt1_e t1 WHERE t1.a % 25 = 0)) AND t1.a % 25 = 0 ORDER BY t1.a; + +RESET enable_hashjoin; +RESET enable_nestloop; +RESET enable_seqscan; + +-- +-- negative testcases +-- + +-- joins where one of the relations is proven empty +EXPLAIN (VERBOSE, 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; + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a = 1 AND a = 2) t1 LEFT JOIN prt2 t2 ON t1.a = t2.b; + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a = 1 AND a = 2) t1 RIGHT JOIN prt2 t2 ON t1.a = t2.b WHERE t2.a % 25 = 0 ORDER BY t1.a, t2.b; + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a = 1 AND a = 2) t1 FULL JOIN prt2 t2 ON t1.a = t2.b WHERE t2.a % 25 = 0 ORDER BY t1.a, t2.b; + +CREATE TABLE prt1_n (a int, b int, c varchar) PARTITION BY RANGE(c); +CREATE TABLE prt1_n_p1 PARTITION OF prt1_n FOR VALUES START ('0000') END ('0250'); +CREATE TABLE prt1_n_p2 PARTITION OF prt1_n FOR VALUES START ('0250') END ('0500'); +INSERT INTO prt1_n SELECT i, i, to_char(i, 'FM0000') FROM generate_series(0, 499, 2) i; +ANALYZE prt1_n; +ANALYZE prt1_n_p1; +ANALYZE prt1_n_p2; + +CREATE TABLE prt2_n (a int, b int, c text) PARTITION BY LIST(c); +CREATE TABLE prt2_n_p1 PARTITION OF prt2_n FOR VALUES IN ('0000', '0003', '0004', '0010', '0006', '0007'); +CREATE TABLE prt2_n_p2 PARTITION OF prt2_n FOR VALUES IN ('0001', '0005', '0002', '0009', '0008', '0011'); +INSERT INTO prt2_n SELECT i, i, to_char(i/50, 'FM0000') FROM generate_series(0, 599, 2) i; +ANALYZE prt2_n; +ANALYZE prt2_n_p1; +ANALYZE prt2_n_p2; + +CREATE TABLE prt3_n (a int, b int, c text) PARTITION BY LIST(c); +CREATE TABLE prt3_n_p1 PARTITION OF prt3_n FOR VALUES IN ('0000', '0004', '0006', '0007'); +CREATE TABLE prt3_n_p2 PARTITION OF prt3_n FOR VALUES IN ('0001', '0002', '0008', '0010'); +CREATE TABLE prt3_n_p3 PARTITION OF prt3_n FOR VALUES IN ('0003', '0005', '0009', '0011'); +INSERT INTO prt2_n SELECT i, i, to_char(i/50, 'FM0000') FROM generate_series(0, 599, 2) i; +ANALYZE prt3_n; +ANALYZE prt3_n_p1; +ANALYZE prt3_n_p2; +ANALYZE prt3_n_p3; + +CREATE TABLE prt4_n (a int, b int, c text) PARTITION BY RANGE(a); +CREATE TABLE prt4_n_p1 PARTITION OF prt4_n FOR VALUES START (0) END (300); +CREATE TABLE prt4_n_p2 PARTITION OF prt4_n FOR VALUES START (300) END (500); +CREATE TABLE prt4_n_p3 PARTITION OF prt4_n FOR VALUES START (500) END (600); +INSERT INTO prt4_n SELECT i, i, to_char(i, 'FM0000') FROM generate_series(0, 599, 2) i; +ANALYZE prt4_n; +ANALYZE prt4_n_p1; +ANALYZE prt4_n_p2; +ANALYZE prt4_n_p3; + +-- partition-wise join can not be applied if the partition ranges differ +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt4_n t2 WHERE t1.a = t2.a; +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1 FULL JOIN prt4_n t2 ON t1.a = t2.a; +-- partition-wise 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); +-- equi-join with join condition on partial keys does not qualify for +-- partition-wise join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_m t1, prt2_m t2 WHERE t1.a = (t2.b + t2.a)/2 AND t1.a % 25 = 0; +-- equi-join between out-of-order partition key columns does not qualify for +-- partition-wise join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_m t1 LEFT JOIN prt2_m t2 ON t1.a = t2.b WHERE t1.a % 25 = 0; +-- equi-join between non-key columns does not qualify for partition-wise join +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_m t1 LEFT JOIN prt2_m t2 ON t1.c = t2.c WHERE t1.a % 25 = 0; +-- partition-wise join can not be applied for a join between list and range +-- partitioned table +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_n t1, prt2_n t2 WHERE t1.c = t2.c; +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_n t1 LEFT JOIN prt2_n t2 ON (t1.c = t2.c); +-- partition-wise join can not be applied between tables with different +-- partition lists +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_n t1 RIGHT JOIN prt1 t2 ON (t1.c = t2.c); +EXPLAIN (COSTS OFF) +SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_n t1 FULL JOIN prt1 t2 ON (t1.c = t2.c);