diff --git a/core/lib/Drupal/Core/Entity/ContentEntityNullStorage.php b/core/lib/Drupal/Core/Entity/ContentEntityNullStorage.php
index 12aeca3..f31b80e 100644
--- a/core/lib/Drupal/Core/Entity/ContentEntityNullStorage.php
+++ b/core/lib/Drupal/Core/Entity/ContentEntityNullStorage.php
@@ -138,4 +138,11 @@ public function countFieldData($storage_definition, $as_bool = FALSE) {
     return $as_bool ? FALSE : 0;
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function hasData() {
+    return FALSE;
+  }
+
 }
diff --git a/core/lib/Drupal/Core/Entity/DynamicallyFieldableEntityStorageInterface.php b/core/lib/Drupal/Core/Entity/DynamicallyFieldableEntityStorageInterface.php
index 6d12c98..1d20217 100644
--- a/core/lib/Drupal/Core/Entity/DynamicallyFieldableEntityStorageInterface.php
+++ b/core/lib/Drupal/Core/Entity/DynamicallyFieldableEntityStorageInterface.php
@@ -85,6 +85,13 @@ public function purgeFieldData(FieldDefinitionInterface $field_definition, $batc
   public function countFieldData($storage_definition, $as_bool = FALSE);
 
   /**
+   * Determines if the storage contains any data.
+   *
+   * @return bool
+   */
+  public function hasData();
+
+  /**
    * Performs final cleanup after all data of a field has been purged.
    *
    * @param \Drupal\Core\Field\FieldStorageDefinitionInterface $storage_definition
diff --git a/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php b/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php
index b8db248..2ace5c7 100644
--- a/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php
+++ b/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php
@@ -1765,6 +1765,17 @@ public function countFieldData($storage_definition, $as_bool = FALSE) {
   }
 
   /**
+   * {@inheritdoc}
+   */
+  public function hasData() {
+    return (bool) $this->database->select($this->getBaseTable(), 'base')
+      ->fields('base')
+      ->countQuery()
+      ->execute()
+      ->fetchField();
+  }
+
+  /**
    * Returns whether the passed field has been already deleted.
    *
    * @param \Drupal\Core\Field\FieldStorageDefinitionInterface $storage_definition
diff --git a/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorageSchema.php b/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorageSchema.php
index dac6584..229dad1 100644
--- a/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorageSchema.php
+++ b/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorageSchema.php
@@ -178,23 +178,19 @@ public function requiresFieldStorageSchemaChanges(FieldStorageDefinitionInterfac
    * {@inheritdoc}
    */
   public function requiresEntityDataMigration(EntityTypeInterface $entity_type, EntityTypeInterface $original) {
-    // If we're updating from NULL storage, then there's no stored data that
-    // requires migration.
-    // @todo Remove in https://www.drupal.org/node/2335879.
     $original_storage_class = $original->getStorageClass();
-    $null_storage_class = 'Drupal\Core\Entity\ContentEntityNullStorage';
-    if ($original_storage_class == $null_storage_class || is_subclass_of($original_storage_class, $null_storage_class)) {
-      return FALSE;
-    }
 
-    return
-      // If the original storage class is different, then there might be
-      // existing entities in that storage even if the new storage's base
-      // table is empty.
-      // @todo Ask the old storage handler rather than assuming:
-      //   https://www.drupal.org/node/2335879.
-      $entity_type->getStorageClass() != $original_storage_class ||
-      !$this->isTableEmpty($this->storage->getBaseTable());
+    // If the original storage class is different, then there might be
+    // existing entities in that storage even if the new storage's base
+    // table is empty.
+    if ($entity_type->getStorageClass() != $original_storage_class) {
+      if (!class_exists($original_storage_class)) {
+        return TRUE;
+      }
+      $original_storage = $original_storage_class::createInstance(\Drupal::getContainer(), $entity_type);
+      return $original_storage->hasData();
+    }
+    return $this->storage->hasData();
   }
 
   /**
diff --git a/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageTest.php b/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageTest.php
index 3589a63..7ff89a7 100644
--- a/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageTest.php
+++ b/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageTest.php
@@ -1181,6 +1181,71 @@ public function testLoadMultiplePersistentCacheMiss() {
   }
 
   /**
+   * @covers ::hasData
+   */
+  public function testHasData() {
+    $select_fetch_field = $this->getMockBuilder('\Drupal\Core\Database\StatementInterface')
+      ->disableOriginalConstructor()
+      ->getMock();
+
+    $select_fetch_field->expects($this->once())
+      ->method('fetchField')
+      ->will($this->returnValue(TRUE));
+
+    $select_execute = $this->getMockBuilder('\Drupal\Core\Database\Query\Select')
+      ->disableOriginalConstructor()
+      ->getMock();
+
+    $select_execute->expects($this->once())
+      ->method('execute')
+      ->will($this->returnValue($select_fetch_field));
+
+    $select_fields = $this->getMockBuilder('\Drupal\Core\Database\Query\Select')
+      ->disableOriginalConstructor()
+      ->getMock();
+
+    $select_fields->expects($this->once())
+      ->method('countQuery')
+      ->will($this->returnValue($select_execute));
+
+    $database_select = $this->getMockBuilder('\Drupal\Core\Database\Query\Select')
+      ->disableOriginalConstructor()
+      ->getMock();
+
+    $database_select->expects($this->once())
+      ->method('fields')
+      ->will($this->returnValue($select_fields));
+
+    $database = $this->getMockBuilder('Drupal\Core\Database\Connection')
+      ->disableOriginalConstructor()
+      ->getMock();
+
+    $database->expects($this->once())
+      ->method('select')
+      ->will($this->returnValue($database_select));
+
+    $this->container->set('database', $database);
+
+    $this->entityManager->expects($this->any())
+      ->method('getDefinition')
+      ->will($this->returnValue($this->entityType));
+
+    $this->entityManager->expects($this->any())
+      ->method('getFieldStorageDefinitions')
+      ->will($this->returnValue($this->fieldDefinitions));
+
+    $this->entityManager->expects($this->any())
+      ->method('getBaseFieldDefinitions')
+      ->will($this->returnValue($this->fieldDefinitions));
+
+    $this->entityStorage = new SqlContentEntityStorage($this->entityType, $database, $this->entityManager, $this->cache);
+
+    $result = $this->entityStorage->hasData();
+
+    $this->assertTrue($result, 'hasData returned TRUE');
+  }
+
+  /**
    * Tests entity ID sanitization.
    */
   public function testCleanIds() {
