diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 01f381e9bada55257ff7451ceeb1feff79804b94..1f2bb6cc72f1242f14d55eee7cdc8e0e0d0775a9 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -1733,6 +1733,7 @@ _outRelOptInfo(StringInfo str, const RelOptInfo *node)
 	WRITE_BITMAPSET_FIELD(relids);
 	WRITE_FLOAT_FIELD(rows, "%.0f");
 	WRITE_INT_FIELD(width);
+	WRITE_BOOL_FIELD(consider_startup);
 	WRITE_NODE_FIELD(reltargetlist);
 	WRITE_NODE_FIELD(pathlist);
 	WRITE_NODE_FIELD(ppilist);
diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c
index 8677ff20fd545c805825e0a14769d9dfb10d6a00..d827b4959b6e910aefa9bfae87b9eada2eb98d56 100644
--- a/src/backend/optimizer/util/pathnode.c
+++ b/src/backend/optimizer/util/pathnode.c
@@ -139,6 +139,9 @@ compare_fractional_path_costs(Path *path1, Path *path2,
  * total cost, we just say that their costs are "different", since neither
  * dominates the other across the whole performance spectrum.
  *
+ * If consider_startup is false, then we don't care about keeping paths with
+ * good startup cost, so we'll never return COSTS_DIFFERENT.
+ *
  * This function also includes special hacks to support a policy enforced
  * by its sole caller, add_path(): paths that have any parameterization
  * cannot win comparisons on the grounds of having cheaper startup cost,
@@ -146,7 +149,8 @@ compare_fractional_path_costs(Path *path1, Path *path2,
  * (Unparameterized paths are more common, so we check for this case last.)
  */
 static PathCostComparison
-compare_path_costs_fuzzily(Path *path1, Path *path2, double fuzz_factor)
+compare_path_costs_fuzzily(Path *path1, Path *path2, double fuzz_factor,
+						   bool consider_startup)
 {
 	/*
 	 * Check total cost first since it's more likely to be different; many
@@ -155,7 +159,8 @@ compare_path_costs_fuzzily(Path *path1, Path *path2, double fuzz_factor)
 	if (path1->total_cost > path2->total_cost * fuzz_factor)
 	{
 		/* path1 fuzzily worse on total cost */
-		if (path2->startup_cost > path1->startup_cost * fuzz_factor &&
+		if (consider_startup &&
+			path2->startup_cost > path1->startup_cost * fuzz_factor &&
 			path1->param_info == NULL)
 		{
 			/* ... but path2 fuzzily worse on startup, so DIFFERENT */
@@ -167,7 +172,8 @@ compare_path_costs_fuzzily(Path *path1, Path *path2, double fuzz_factor)
 	if (path2->total_cost > path1->total_cost * fuzz_factor)
 	{
 		/* path2 fuzzily worse on total cost */
-		if (path1->startup_cost > path2->startup_cost * fuzz_factor &&
+		if (consider_startup &&
+			path1->startup_cost > path2->startup_cost * fuzz_factor &&
 			path2->param_info == NULL)
 		{
 			/* ... but path1 fuzzily worse on startup, so DIFFERENT */
@@ -177,6 +183,7 @@ compare_path_costs_fuzzily(Path *path1, Path *path2, double fuzz_factor)
 		return COSTS_BETTER1;
 	}
 	/* fuzzily the same on total cost */
+	/* (so we may as well compare startup cost, even if !consider_startup) */
 	if (path1->startup_cost > path2->startup_cost * fuzz_factor &&
 		path2->param_info == NULL)
 	{
@@ -360,6 +367,9 @@ set_cheapest(RelOptInfo *parent_rel)
  *	  reduce the number of parameterized paths that are kept.  See discussion
  *	  in src/backend/optimizer/README.
  *
+ *	  Another policy that is enforced here is that we only consider cheap
+ *	  startup cost to be interesting if parent_rel->consider_startup is true.
+ *
  *	  The pathlist is kept sorted by total_cost, with cheaper paths
  *	  at the front.  Within this routine, that's simply a speed hack:
  *	  doing it that way makes it more likely that we will reject an inferior
@@ -423,7 +433,8 @@ add_path(RelOptInfo *parent_rel, Path *new_path)
 		 * Do a fuzzy cost comparison with 1% fuzziness limit.	(XXX does this
 		 * percentage need to be user-configurable?)
 		 */
-		costcmp = compare_path_costs_fuzzily(new_path, old_path, 1.01);
+		costcmp = compare_path_costs_fuzzily(new_path, old_path, 1.01,
+											 parent_rel->consider_startup);
 
 		/*
 		 * If the two paths compare differently for startup and total cost,
@@ -488,8 +499,10 @@ add_path(RelOptInfo *parent_rel, Path *new_path)
 									remove_old = true;	/* new dominates old */
 								else if (new_path->rows > old_path->rows)
 									accept_new = false; /* old dominates new */
-								else if (compare_path_costs_fuzzily(new_path, old_path,
-											  1.0000000001) == COSTS_BETTER1)
+								else if (compare_path_costs_fuzzily(new_path,
+																	old_path,
+																	1.0000000001,
+																	parent_rel->consider_startup) == COSTS_BETTER1)
 									remove_old = true;	/* new dominates old */
 								else
 									accept_new = false; /* old equals or
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index 680184c0e9f2a9ca9dbb9f17be72b2d17a6f273f..3878c3752fef56e43bbe12d8c1613698993d0d1d 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -99,6 +99,8 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptKind reloptkind)
 	rel->relids = bms_make_singleton(relid);
 	rel->rows = 0;
 	rel->width = 0;
+	/* cheap startup cost is interesting iff not all tuples to be retrieved */
+	rel->consider_startup = (root->tuple_fraction > 0);
 	rel->reltargetlist = NIL;
 	rel->pathlist = NIL;
 	rel->ppilist = NIL;
@@ -354,6 +356,8 @@ build_join_rel(PlannerInfo *root,
 	joinrel->relids = bms_copy(joinrelids);
 	joinrel->rows = 0;
 	joinrel->width = 0;
+	/* cheap startup cost is interesting iff not all tuples to be retrieved */
+	joinrel->consider_startup = (root->tuple_fraction > 0);
 	joinrel->reltargetlist = NIL;
 	joinrel->pathlist = NIL;
 	joinrel->ppilist = NIL;
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index 7b4eaf4f5e540158ac2f88eba232ad91e17dc8f2..af9425ad9b309fbecd65d1a83a229ddfd447f382 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -299,6 +299,8 @@ typedef struct PlannerInfo
  *			   clauses have been applied (ie, output rows of a plan for it)
  *		width - avg. number of bytes per tuple in the relation after the
  *				appropriate projections have been done (ie, output width)
+ *		consider_startup - true if there is any value in keeping paths for
+ *						   this rel on the basis of having cheap startup cost
  *		reltargetlist - List of Var and PlaceHolderVar nodes for the values
  *						we need to output from this relation.
  *						List is in no particular order, but all rels of an
@@ -405,6 +407,9 @@ typedef struct RelOptInfo
 	double		rows;			/* estimated number of result tuples */
 	int			width;			/* estimated avg width of result tuples */
 
+	/* per-relation planner control flags */
+	bool		consider_startup;	/* keep cheap-startup-cost paths? */
+
 	/* materialization information */
 	List	   *reltargetlist;	/* Vars to be output by scan of relation */
 	List	   *pathlist;		/* Path structures */
diff --git a/src/test/regress/expected/join.out b/src/test/regress/expected/join.out
index 914a6fd84473fa97302e3c3ca8320d35dea3fcb3..299ea06de37385579b29d68cab41987a715b015f 100644
--- a/src/test/regress/expected/join.out
+++ b/src/test/regress/expected/join.out
@@ -3437,6 +3437,7 @@ select v.* from
 
 create temp table dual();
 insert into dual default values;
+analyze dual;
 select v.* from
   (int8_tbl x left join (select q1,(select coalesce(q2,0)) q2 from int8_tbl) y on x.q2 = y.q1)
   left join int4_tbl z on z.f1 = x.q2,
@@ -3446,21 +3447,21 @@ select v.* from
                123 |                  
                456 |                  
                123 |  4567890123456789
-  4567890123456789 |               123
+  4567890123456789 | -4567890123456789
                123 |  4567890123456789
   4567890123456789 |  4567890123456789
                123 |  4567890123456789
-  4567890123456789 | -4567890123456789
   4567890123456789 |               123
-               123 |               456
   4567890123456789 |               123
                123 |  4567890123456789
-  4567890123456789 |  4567890123456789
   4567890123456789 |               123
+               123 |               456
   4567890123456789 |  4567890123456789
+  4567890123456789 | -4567890123456789
   4567890123456789 |  4567890123456789
   4567890123456789 |  4567890123456789
-  4567890123456789 | -4567890123456789
+  4567890123456789 |  4567890123456789
+  4567890123456789 |               123
   4567890123456789 |                  
  -4567890123456789 |                  
 (20 rows)
diff --git a/src/test/regress/sql/join.sql b/src/test/regress/sql/join.sql
index fcc6572709de95739c65760d20e07437389335fc..c425be917aa916ae37d0544d725c5c775fe59630 100644
--- a/src/test/regress/sql/join.sql
+++ b/src/test/regress/sql/join.sql
@@ -937,6 +937,7 @@ select v.* from
   lateral (select x.q1,y.q1 union all select x.q2,y.q2) v(vx,vy);
 create temp table dual();
 insert into dual default values;
+analyze dual;
 select v.* from
   (int8_tbl x left join (select q1,(select coalesce(q2,0)) q2 from int8_tbl) y on x.q2 = y.q1)
   left join int4_tbl z on z.f1 = x.q2,