diff --git a/core/modules/taxonomy/src/Plugin/views/argument/IndexTidDepthJoin.php b/core/modules/taxonomy/src/Plugin/views/argument/IndexTidDepthJoin.php
new file mode 100644
index 0000000..a2b6294
--- /dev/null
+++ b/core/modules/taxonomy/src/Plugin/views/argument/IndexTidDepthJoin.php
@@ -0,0 +1,118 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\taxonomy\Plugin\views\argument\IndexTidDepthJoin.
+ */
+
+namespace Drupal\taxonomy\Plugin\views\argument;
+
+use Drupal\views\Views;
+
+/**
+ * Argument handler for taxonomy terms with depth using joins.
+ *
+ * This handler can be more performant than the normal one due to the fact that
+ * it uses joins instead of subqueries.
+ *
+ * @ingroup views_argument_handlers
+ *
+ * @ViewsArgument("taxonomy_index_tid_depth_join")
+ */
+class IndexTidDepthJoin extends IndexTidDepth {
+
+  public function query($group_by = FALSE) {
+    $this->ensureMyTable();
+
+    if (!empty($this->options['break_phrase'])) {
+      $break = static::breakString($this->argument);
+      if ($break->value === array(-1)) {
+        return FALSE;
+      }
+
+      $operator = (count($break->value) > 1) ? 'IN' : '=';
+      $tids = $break->value;
+    }
+    else {
+      $operator = "=";
+      $tids = $this->argument;
+    }
+
+    // The tids variable can be an integer or an array of integers.
+    $tids = is_array($tids) ? $tids : array($tids);
+
+    if ($this->options['depth'] > 0) {
+      // When the depth is positive search the children.
+      foreach ($tids as $tid) {
+        // The term must be loaded to get vid for use in taxonomy_get_tree().
+        if ($term = $this->termStorage->load($tid)) {
+          // For every tid argument find all the children down to the depth set
+          // in the options and save the tids for the condition.
+          $tree = $this->termStorage->loadTree($term->getVocabularyId(), $term->id(), (int) $this->options['depth']);
+          $tids = array_merge($tids, array_map(function ($term) {
+            return $term->id();
+          }, $tree));
+        }
+      }
+    }
+    elseif ($this->options['depth'] < 0) {
+      // When the depth is negative search the parents.
+      foreach ($tids as $tid) {
+        // For every tid argument find all the parents up to the depth set
+        // in the options and add the tids into the array. Since there is
+        // no taxonomy function to get all parents with a depth limit it
+        // is done here building a multidimensional array.
+        if ($term = $this->termStorage->load($tid)) {
+          // A variable is needed to track the current depth level.
+          $n = 0;
+          // Initialise our depth based parents array with the leaf term.
+          $parents[$n--][] = $term;
+          while ($n >= $this->options['depth']) {
+            // At each depth find the parents of the current terms.
+            // It is important to note that it is possible for a term to have
+            // multiple parents so get the parents of every parent and so on.
+            $parents[$n] = array();
+            foreach ($parents[$n + 1] as $term) {
+              $parents[$n] += $this->termStorage->loadParents($term->id());
+            }
+            // Save all the tids for the condition.
+            $tids = array_merge($tids, array_map(function ($term) {
+              return $term->id();
+            }, $parents[$n]));
+            $n--;
+          }
+        }
+      }
+    }
+
+    // Check the size of the array and set the operator accordingly.
+    if (count($tids) > 1) {
+      $operator = 'IN';
+    }
+    else {
+      $tids = current($tids);
+      $operator = '=';
+    }
+
+    // Join on taxonomy index table.
+    $configuration = array(
+      'table' => 'taxonomy_index',
+      'field' => 'nid',
+      'left_table' => $this->tableAlias,
+      'left_field' => $this->realField,
+      'operator' => $operator,
+      'extra' => array(
+        0 => array(
+          'field' => 'tid',
+          'value' => $tids,
+        ),
+      ),
+    );
+    $join = Views::pluginManager('join')->createInstance('standard', $configuration);
+    $this->query->addRelationship('taxonomy_index', $join, 'node');
+
+    // Distinct is required to prevent duplicate rows.
+    $this->query->distinct = TRUE;
+  }
+
+}
diff --git a/core/modules/taxonomy/src/Plugin/views/filter/TaxonomyIndexTidDepthJoin.php b/core/modules/taxonomy/src/Plugin/views/filter/TaxonomyIndexTidDepthJoin.php
new file mode 100644
index 0000000..7ec557d
--- /dev/null
+++ b/core/modules/taxonomy/src/Plugin/views/filter/TaxonomyIndexTidDepthJoin.php
@@ -0,0 +1,127 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\taxonomy\Plugin\views\filter\TaxonomyIndexTidDepthJoin.
+ */
+
+namespace Drupal\taxonomy\Plugin\views\filter;
+
+use Drupal\views\Views;
+
+/**
+ * Filter handler for taxonomy terms with depth using joins.
+ *
+ * This handler can be more performant than the normal one due to the fact that
+ * it uses joins instead of subqueries.
+ *
+ * @ingroup views_filter_handlers
+ *
+ * @ViewsFilter("taxonomy_index_tid_depth_join")
+ */
+class TaxonomyIndexTidDepthJoin extends TaxonomyIndexTidDepth {
+
+  public function query() {
+    // If no filter values are present, then do nothing.
+    if (count($this->value) == 0) {
+      return;
+    }
+    elseif (count($this->value) == 1) {
+      // Sometimes $this->value is an array with a single element so convert it.
+      if (is_array($this->value)) {
+        $this->value = current($this->value);
+      }
+      $operator = '=';
+    }
+    else {
+      $operator = 'IN';# " IN (" . implode(', ', array_fill(0, sizeof($this->value), '%d')) . ")";
+    }
+
+    // The normal use of ensureMyTable() here breaks Views.
+    // So instead we trick the filter into using the alias of the base table.
+    //   See https://www.drupal.org/node/271833.
+    // If a relationship is set, we must use the alias it provides.
+    if (!empty($this->relationship)) {
+      $this->tableAlias = $this->relationship;
+    }
+    // If no relationship, then use the alias of the base table.
+    else {
+      $this->tableAlias = $this->query->ensureTable($this->view->storage->get('base_table'));
+    }
+
+    // The tids variable can be an integer or an array of integers.
+    $tids = is_array($this->value) ?  $this->value : array($this->value);
+
+    if ($this->options['depth'] > 0) {
+      // When the depth is positive search the children.
+      foreach ($tids as $tid) {
+        // The term must be loaded to get vid for use in taxonomy_get_tree().
+        if ($term = $this->termStorage->load($tid)) {
+          // For every tid argument find all the children down to the depth set
+          // in the options and save the tids for the condition.
+          $tree = $this->termStorage->loadTree($term->getVocabularyId(), $term->id(), (int) $this->options['depth']);
+          $tids = array_merge($tids, array_map(function ($term) {
+            return $term->id();
+          }, $tree));
+        }
+      }
+    }
+    elseif ($this->options['depth'] < 0) {
+      // When the depth is negative search the parents.
+      foreach ($tids as $tid) {
+        // For every tid argument find all the parents up to the depth set
+        // in the options and add the tids into the array. Since there is
+        // no taxonomy function to get all parents with a depth limit it
+        // is done here building a multidimensional array.
+        if ($term = $this->termStorage->load($tid)) {
+          // A variable is needed to track the current depth level.
+          $n = 0;
+          // Initialise our depth based parents array with the leaf term.
+          $parents[$n--][] = $term;
+          while ($n >= $this->options['depth']) {
+            // At each depth find the parents of the current terms.
+            // It is important to note that it is possible for a term to have
+            // multiple parents so get the parents of every parent and so on.
+            $parents[$n] = array();
+            foreach ($parents[$n + 1] as $term) {
+              $parents[$n] += $this->termStorage->loadParents($term->id());
+            }
+            // Save all the tids for the condition.
+            $tids = array_merge($tids, array_map(function ($term) {
+              return $term->id();
+            }, $parents[$n]));
+            $n--;
+          }
+        }
+      }
+    }
+
+    // Check the size of the array and set the operator accordingly.
+    if (count($tids) > 1) {
+      $operator = 'IN';
+    }
+    else {
+      $tids = current($tids);
+      $operator = '=';
+    }
+
+    // Join on taxonomy index table.
+    $configuration = array(
+      'table' => 'taxonomy_index',
+      'field' => 'nid',
+      'left_table' => $this->tableAlias,
+      'left_field' => $this->realField,
+      'operator' => $operator,
+      'extra' => array(
+        0 => array(
+          'field' => 'tid',
+          'value' => $tids,
+        ),
+      ),
+    );
+    $join = Views::pluginManager('join')->createInstance('standard', $configuration);
+
+    $this->query->addRelationship('taxonomy_index', $join, 'node');
+  }
+
+}
diff --git a/core/modules/taxonomy/taxonomy.views.inc b/core/modules/taxonomy/taxonomy.views.inc
index 5360d50..789526e 100644
--- a/core/modules/taxonomy/taxonomy.views.inc
+++ b/core/modules/taxonomy/taxonomy.views.inc
@@ -42,6 +42,20 @@ function taxonomy_views_data_alter(&$data) {
     ),
   );
 
+  $data['node_field_data']['term_node_tid_depth_join'] = array(
+    'help' => t('Display content if it has the selected taxonomy terms, or children of the selected terms. Due to additional complexity, this has fewer options than the versions without depth.'),
+    'real field' => 'nid',
+    'argument' => array(
+      'title' => t('Has taxonomy term ID with depth (using joins)'),
+      'id' => 'taxonomy_index_tid_depth_join',
+      'accept depth modifier' => TRUE,
+    ),
+    'filter' => array(
+      'title' => t('Has taxonomy terms with depth (using joins)'),
+      'id' => 'taxonomy_index_tid_depth_join',
+    ),
+  );
+
   $data['node_field_data']['term_node_tid_depth_modifier'] = array(
     'title' => t('Has taxonomy term ID depth modifier'),
     'help' => t('Allows the "depth" for Taxonomy: Term ID (with depth) to be modified via an additional contextual filter value.'),
