diff --git a/sql/opt_group_by_cardinality.cc b/sql/opt_group_by_cardinality.cc
index 8b58096cfd1..9fa95e208f3 100644
--- a/sql/opt_group_by_cardinality.cc
+++ b/sql/opt_group_by_cardinality.cc
@@ -30,6 +30,10 @@
 static
 double estimate_table_group_cardinality(JOIN *join, Item ***group_list,
                                         Item* const *end);
+static
+double estimate_item_list_cardinality(JOIN *join,
+                                      Dynamic_array<Item*> &group_cols,
+                                      double join_output_card);
 
 inline bool has_one_bit_set(table_map val)
 {
@@ -52,6 +56,40 @@ int cmp_items_by_used_tables(const void *a_val, const void *b_val)
 }
 
 
+/*
+  Estimate how many distinct rows the select inside subq_pred would produce
+
+  @param  join              Parent join (subq_pred is a semi-join merged into
+                            this join)
+  @param  subq_pred         The subquery
+  @param  ncols             Number of elements in subq_pred's select list
+  @param  subq_output_size  Subquery output size, including duplicates.
+
+  @return
+    Number of distinct rows in subquery's output.
+*/
+
+double estimate_distinct_cardinality(JOIN *join, Item_in_subselect *subq_pred,
+                                     uint ncols, double subq_output_size)
+{
+  Dynamic_array<Item*> group_cols(join->thd->mem_root);
+  st_select_lex *subq_lex= subq_pred->unit->first_select();
+
+  for (uint i=0; i < ncols; i++)
+  {
+    Item *item= subq_lex->ref_pointer_array[i];
+    table_map map= item->used_tables();
+    if ((map & PSEUDO_TABLE_BITS) || !has_one_bit_set(map))
+    {
+      /* Can't estimate */
+      return subq_output_size;
+    }
+    group_cols.append(item);
+  }
+  DBUG_ASSERT(group_cols.size());
+  return estimate_item_list_cardinality(join, group_cols, subq_output_size);
+}
+
 /*
   @brief
     Given a SELECT with GROUP BY clause, estimate the cardinality of output
@@ -91,10 +129,6 @@ double estimate_post_group_cardinality(JOIN *join, double join_output_card)
   Dynamic_array<Item*> group_cols(join->thd->mem_root);
   ORDER *cur_group;
 
-  Json_writer_object wrapper(join->thd);
-  Json_writer_object trace(join->thd, "materialized_output_cardinality");
-  trace.add("join_output_cardinality", join_output_card);
-
   /*
     Walk the GROUP BY list and put items into group_cols array. Array is
     easier to work with: we will sort it and then produce estimates for
@@ -113,7 +147,17 @@ double estimate_post_group_cardinality(JOIN *join, double join_output_card)
     group_cols.append(item);
   }
   DBUG_ASSERT(group_cols.size());
+  return estimate_item_list_cardinality(join, group_cols, join_output_card);
+}
+
 
+static
+double estimate_item_list_cardinality(JOIN *join, Dynamic_array<Item*> &group_cols,
+                                      double join_output_card)
+{
+  Json_writer_object wrapper(join->thd);
+  Json_writer_object trace(join->thd, "materialized_output_cardinality");
+  trace.add("join_output_cardinality", join_output_card);
   group_cols.sort(cmp_items_by_used_tables);
 
   double new_card= 1.0;
@@ -214,19 +258,20 @@ double estimate_table_group_cardinality(JOIN *join, Item ***group_list,
 
   table_map table_bit= (**group_list)->used_tables();
   /*
-    join->map2table is not set yet, so find our table in JOIN_TABs.
+    join->map2table is not set yet, so find the table in leaf_tables list
   */
-  for (JOIN_TAB *tab= join->join_tab;
-       tab < join->join_tab + join->top_join_tab_count;
-       tab++)
+  List_iterator<TABLE_LIST> li(join->select_lex->leaf_tables);
+  TABLE_LIST *tbl;
+  while ((tbl= li++))
   {
-    if (tab->table->map == table_bit)
+    if (tbl->table->map == table_bit)
     {
-      table= tab->table;
-      table_records_after_where= rows2double(tab->found_records);
+      table= tbl->table;
+      table_records_after_where= rows2double(tbl->table->reginfo.join_tab->found_records);
       break;
     }
   }
+
   DBUG_ASSERT(table);
 
   Json_writer_object trace_obj(join->thd);
diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc
index d43a3f10ec4..59c9c0e0f3b 100644
--- a/sql/opt_subselect.cc
+++ b/sql/opt_subselect.cc
@@ -2560,6 +2560,8 @@ bool optimize_semijoin_nests(JOIN *join, table_map all_table_map)
     /* semi-join nests with only constant tables are not valid */
    /// DBUG_ASSERT(sj_nest->sj_inner_tables & ~join->const_table_map);
 
+
+    sj_nest->sj_fanout_ratio= 1.0;
     sj_nest->sj_mat_info= NULL;
     /*
       The statement may have been executed with 'semijoin=on' earlier.
@@ -2635,6 +2637,8 @@ bool optimize_semijoin_nests(JOIN *join, table_map all_table_map)
           }
           sjm->rows= MY_MIN(sjm->rows, rows);
         }
+        // Save the subquery's "fanout" (see below)
+        sj_nest->sj_fanout_ratio= subjoin_out_rows / sjm->rows;
         memcpy((uchar*) sjm->positions,
                (uchar*) (join->best_positions + join->const_tables),
                sizeof(POSITION) * n_tables);
@@ -2677,6 +2681,29 @@ bool optimize_semijoin_nests(JOIN *join, table_map all_table_map)
         sj_nest->sj_mat_info= sjm;
         DBUG_EXECUTE("opt", print_sjm(sjm););
       }
+      else if ((sj_nest->sj_inner_tables  & ~join->const_table_map))
+      {
+        /*
+          It's a correlated subquery.
+          Estimate its "fanout", ie. how many duplicate rows would be
+          produced on average for a record combination of outer tables'
+          records.
+        */
+        uint n_tables= my_count_bits(sj_nest->sj_inner_tables & ~join->const_table_map);
+
+        if (choose_plan(join, all_table_map &~join->const_table_map, sj_nest))
+          DBUG_RETURN(TRUE); /* purecov: inspected */
+        double subjoin_out_rows, subjoin_read_time;
+        join->get_prefix_cost_and_fanout(n_tables,
+                                         &subjoin_read_time,
+                                         &subjoin_out_rows);
+        double distinct_rows= 
+          estimate_distinct_cardinality(join, // parent join 
+                                        sj_nest->sj_subq_pred,
+                                        sj_nest->sj_in_exprs,
+                                        subjoin_out_rows);
+        sj_nest->sj_fanout_ratio= subjoin_out_rows / distinct_rows;
+      }
     }
   }
   DBUG_RETURN(FALSE);
@@ -3722,27 +3749,66 @@ bool Duplicate_weedout_picker::check_qep(JOIN *join,
         join->positions[first_tab - 1].prefix_record_count;
       temptable_rec_size= 8; /* This is not true but we'll make it so */
     }
-    
+    // Fanout of all tables in the join prefix.
+    double total_fanout= 1.0;
+    // Fanout that will be removed by duplicate elimination. 
+    double semi_join_fanout= 1.0;
     table_map dups_removed_fanout= 0;
+    double outer_prefix_fanout= 1.0;
     for (uint j= first_dupsweedout_table; j <= idx; j++)
     {
       POSITION *p= join->positions + j;
       dups_cost= COST_ADD(dups_cost, p->read_time);
+      total_fanout = COST_MULT(total_fanout, p->records_out);
 
       if (p->table->emb_sj_nest)
       {
-        sj_inner_fanout= COST_MULT(sj_inner_fanout, p->records_out);
+        if (!(dups_removed_fanout & p->table->table->map))
+        {
+          // We haven't seen a table from this semi-join.
+          // Put it into semi_join_fanout.
+          semi_join_fanout *= p->table->emb_sj_nest->sj_fanout_ratio;
+        }
         dups_removed_fanout |= p->table->table->map;
       }
       else
       {
-        sj_outer_fanout= COST_MULT(sj_outer_fanout, p->records_out);
+        if (dups_removed_fanout)
+          outer_prefix_fanout= -1.0; // Mark as not applicable
+        else
+        {
+          if (outer_prefix_fanout > 0)
+            outer_prefix_fanout *= p->records_out;
+        }
+
         /* Ensure that table supports comparable rowids */
         DBUG_ASSERT(!(p->table->table->file->ha_table_flags() & HA_NON_COMPARABLE_ROWID));
-
         temptable_rec_size += p->table->table->file->ref_length;
       }
     }
+#if 0
+    if (outer_prefix_fanout > 0.0)
+    {
+      /*
+        Suppose the join order is :
+          all_outer_tables,  all_inner_tables
+        In this case, can we just remove all fanout from all_inner_tables and
+        be done?
+        (A: note that fanout from all_inner_tables can be less than 1.0. Don't
+        remove it in this case.  If it is greater than 1.0, removing makes
+        sense).
+      */
+      sj_outer_fanout= outer_prefix_fanout;
+      sj_inner_fanout= total_fanout / outer_prefix_fanout;
+    }
+    else
+#endif      
+    {
+      // Fanout caused by duplicates generated by all semi-joins we cover:
+      sj_inner_fanout= semi_join_fanout;
+      // This is what will be left after duplicates are removed:
+      sj_outer_fanout= total_fanout / semi_join_fanout;
+    }
 
     /*
       Add the cost of temptable use. The table will have sj_outer_fanout
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index e49003a734f..2131babe2de 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -11459,7 +11459,7 @@ get_costs_for_tables(JOIN *join, table_map remaining_tables, uint idx,
   {
     table_map real_table_bit= s->table->map;
     if ((*allowed_tables & real_table_bit) &&
-        !(remaining_tables & s->dependent))
+        !(remaining_tables & join->allowed_tables & s->dependent))
     {
 #ifdef DBUG_ASSERT_EXISTS
       DBUG_ASSERT(!check_interleaving_with_nj(s));
diff --git a/sql/sql_select.h b/sql/sql_select.h
index 3bae2203b90..3a1130a7e6c 100644
--- a/sql/sql_select.h
+++ b/sql/sql_select.h
@@ -2713,6 +2713,8 @@ void propagate_new_equalities(THD *thd, Item *cond,
 #define PREV_BITS(type, N_BITS) ((type)my_set_bits(N_BITS))
 
 double estimate_post_group_cardinality(JOIN *join, double join_output_card);
+double estimate_distinct_cardinality(JOIN *join, Item_in_subselect *subq_pred,
+                                     uint ncols, double join_output_card);
 
 bool dbug_user_var_equals_str(THD *thd, const char *name, const char *value);
 
diff --git a/sql/table.h b/sql/table.h
index b25480e1a8e..9271888e752 100644
--- a/sql/table.h
+++ b/sql/table.h
@@ -2523,6 +2523,11 @@ struct TABLE_LIST
   uint jtbm_table_no;
 
   SJ_MATERIALIZATION_INFO *sj_mat_info;
+  /*
+    For semi-join nests: how much fanout this semi-join has: given one
+    record combination, how many matching rows it would produce.
+  */
+  double sj_fanout_ratio;
 
   /*
     The structure of ON expression presented in the member above
