Deduping Views by forcing Views to add a simple GROUP BY to a query
It turns out that the solution is similar to what I tried: you do need to use hook_views_query_alter.  But playing with GROUP BY is useless unless you can guarantee that you will not be using ORDER BY at all (i.e., you will do no sorting beyond your GROUP BY key).  GROUP BY will contain not only the variable you want to group by; it will also contain every variable you use in ORDER BY.  This appears to be required by the current SQL standard.

The way to do this is this is build your view around the "parent" type ("Course" in my example), and do any filtering you need to do in hook_views_query_alter, using a correlated subquery.  This will look something like this:

function my_module_views_query_alter(ViewExecutable $view, QueryPluginBase $query) {

  if ($view-&gt;id() == 'course_listing') {
    $where_clause = &lt;&lt;&lt;WHERE
    (
      SELECT COUNT(*)
      FROM {node__field_session_course} link_to_course
      WHERE node_field_data.nid = link_to_course.entity_id
    ) &gt; 0
WHERE;

    $query-&gt;addWhereExpression(0, $where_clause);
  }
}


where node_field_data is the base table for Course.

Since there is no left joins or inner joins with the Session table, there are no duplicates, making the deduping problem go away.