diff --git a/core/lib/Drupal/Core/Entity/Sql/DefaultTableMapping.php b/core/lib/Drupal/Core/Entity/Sql/DefaultTableMapping.php
index 472ed20..9c75f8f 100644
--- a/core/lib/Drupal/Core/Entity/Sql/DefaultTableMapping.php
+++ b/core/lib/Drupal/Core/Entity/Sql/DefaultTableMapping.php
@@ -152,7 +152,9 @@ public function getFieldTableName($field_name) {
       //   https://www.drupal.org/node/2274017.
       /** @var \Drupal\Core\Entity\Sql\SqlContentEntityStorage $storage */
       $storage = \Drupal::entityManager()->getStorage($this->entityType->id());
+      $storage_definition = $this->fieldStorageDefinitions[$field_name];
       $table_names = array(
+        $this->getDedicatedDataTableName($storage_definition),
         $storage->getDataTable(),
         $storage->getBaseTable(),
         $storage->getRevisionTable(),
@@ -160,7 +162,6 @@ public function getFieldTableName($field_name) {
 
       // Collect field columns.
       $field_columns = array();
-      $storage_definition = $this->fieldStorageDefinitions[$field_name];
       foreach (array_keys($storage_definition->getColumns()) as $property_name) {
         $field_columns[] = $this->getFieldColumnName($storage_definition, $property_name);
       }
diff --git a/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php b/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php
index 2b4f9a4..3f2bc0b 100644
--- a/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php
+++ b/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php
@@ -287,13 +287,13 @@ public function getTableMapping(array $storage_definitions = NULL) {
       $definitions = $storage_definitions ?: $this->entityManager->getFieldStorageDefinitions($this->entityTypeId);
       $table_mapping = new DefaultTableMapping($this->entityType, $definitions);
 
-      $definitions = array_filter($definitions, function (FieldStorageDefinitionInterface $definition) use ($table_mapping) {
+      $shared_table_definitions = array_filter($definitions, function (FieldStorageDefinitionInterface $definition) use ($table_mapping) {
         return $table_mapping->allowsSharedTableStorage($definition);
       });
 
       $key_fields = array_values(array_filter(array($this->idKey, $this->revisionKey, $this->bundleKey, $this->uuidKey, $this->langcodeKey)));
-      $all_fields = array_keys($definitions);
-      $revisionable_fields = array_keys(array_filter($definitions, function (FieldStorageDefinitionInterface $definition) {
+      $all_fields = array_keys($shared_table_definitions);
+      $revisionable_fields = array_keys(array_filter($shared_table_definitions, function (FieldStorageDefinitionInterface $definition) {
         return $definition->isRevisionable();
       }));
       // Make sure the key fields come first in the list of fields.
@@ -360,8 +360,8 @@ public function getTableMapping(array $storage_definitions = NULL) {
       }
 
       // Add dedicated tables.
-      $definitions = array_filter($definitions, function (FieldStorageDefinitionInterface $definition) use ($table_mapping) {
-        return $table_mapping->requiresDedicatedTableStorage($definition);
+      $dedicated_table_definitions = array_filter($definitions, function (FieldStorageDefinitionInterface $definition) use ($table_mapping) {
+        return $table_mapping->requiresDedicatedTableStorage($definition) && $definition->isBaseField();
       });
       $extra_columns = array(
         'bundle',
@@ -371,8 +371,12 @@ public function getTableMapping(array $storage_definitions = NULL) {
         'langcode',
         'delta',
       );
-      foreach ($definitions as $field_name => $definition) {
-        foreach (array($table_mapping->getDedicatedDataTableName($definition), $table_mapping->getDedicatedRevisionTableName($definition)) as $table_name) {
+      foreach ($dedicated_table_definitions as $field_name => $definition) {
+        $tables = [$table_mapping->getDedicatedDataTableName($definition)];
+        if ($revisionable && $definition->isRevisionable()) {
+          $tables[] = $table_mapping->getDedicatedRevisionTableName($definition);
+        }
+        foreach ($tables as $table_name) {
           $table_mapping->setFieldNames($table_name, array($field_name));
           $table_mapping->setExtraColumns($table_name, $extra_columns);
         }
@@ -1587,7 +1591,13 @@ public function finalizePurge(FieldStorageDefinitionInterface $storage_definitio
    * {@inheritdoc}
    */
   public function countFieldData($storage_definition, $as_bool = FALSE) {
-    $table_mapping = $this->getTableMapping();
+    // The existing table mapping cannot be relied upon because it may have been
+    // generated for after a schema update, so force the table mapping for the
+    // supplied storage definition.
+    // @todo This may be able to be removed once we are able to generate a fresh
+    //   table mapping in the schema handler. See
+    //   https://www.drupal.org/node/2274017.
+    $table_mapping = $this->getTableMapping([$storage_definition->getName() => $storage_definition]);
 
     if ($table_mapping->requiresDedicatedTableStorage($storage_definition)) {
       $is_deleted = $this->storageDefinitionIsDeleted($storage_definition);
diff --git a/core/modules/views/src/EntityViewsData.php b/core/modules/views/src/EntityViewsData.php
index bc355f9..0421d00 100644
--- a/core/modules/views/src/EntityViewsData.php
+++ b/core/modules/views/src/EntityViewsData.php
@@ -230,11 +230,21 @@ public function getViewsData() {
     // Load all typed data definitions of all fields. This should cover each of
     // the entity base, revision, data tables.
     $field_definitions = $this->entityManager->getBaseFieldDefinitions($this->entityType->id());
-    if ($table_mapping = $this->storage->getTableMapping()) {
+    if ($table_mapping = $this->storage->getTableMapping($field_definitions)) {
       // Fetch all fields that can appear in both the base table and the data
       // table.
       $entity_keys = $this->entityType->getKeys();
       $duplicate_fields = array_intersect_key($entity_keys, array_flip(['id', 'revision', 'bundle']));
+      // Make note of some information so that table joins can be applied for
+      // base fields that have dedicated tables.
+      $main_tables = array_filter([$base_table, $data_table, $revision_table, $revision_data_table]);
+      $dedicated_data_tables = [];
+      foreach ($field_definitions as $field_name => $field_definition) {
+        $storage_definition = $field_definition->getFieldStorageDefinition();
+        if ($revisionable && $storage_definition->isMultiple()) {
+          $dedicated_data_tables[] = $table_mapping->getFieldTableName($field_name);
+        }
+      }
       // Iterate over each table we have so far and collect field data for each.
       // Based on whether the field is in the field_definitions provided by the
       // entity manager.
@@ -242,6 +252,19 @@ public function getViewsData() {
       //   storage.
       // @todo https://www.drupal.org/node/2337511
       foreach ($table_mapping->getTableNames() as $table) {
+        // Provide joins for base fields with dedicated storage.
+        if (!in_array($table, $main_tables)) {
+          $data[$table]['table']['group'] = in_array($table, $dedicated_data_tables) ? $this->entityType->getLabel() : $this->t('@entity_type revision', ['@entity_type' => $this->entityType->getLabel()]);
+          $data[$table]['table']['provider'] = $this->entityType->getProvider();
+          $data[$table]['table']['join'][$views_base_table] = [
+            'left_field' => $base_field,
+            'field' => 'entity_id',
+            'extra' => array(
+              array('field' => 'deleted', 'value' => 0, 'numeric' => TRUE),
+            ),
+          ];
+        }
+
         foreach ($table_mapping->getFieldNames($table) as $field_name) {
           // To avoid confusing duplication in the user interface, for fields
           // that are on both base and data tables, only add them on the data
