diff --git a/core/lib/Drupal/Core/Database/Query/Select.php b/core/lib/Drupal/Core/Database/Query/Select.php
index a7e340b..0d296e0 100644
--- a/core/lib/Drupal/Core/Database/Query/Select.php
+++ b/core/lib/Drupal/Core/Database/Query/Select.php
@@ -479,6 +479,20 @@ public function execute() {
   /**
    * {@inheritdoc}
    */
+  public function executeTemporary() {
+    // If validation fails, simply return NULL. Note that validation routines in
+    // preExecute() may throw exceptions instead.
+    if (!$this->preExecute()) {
+      return NULL;
+    }
+
+    $args = $this->getArguments();
+    return $this->connection->queryTemporary((string) $this, $args, $this->queryOptions);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   public function distinct($distinct = TRUE) {
     $this->distinct = $distinct;
     return $this;
diff --git a/core/lib/Drupal/Core/Database/Query/SelectExtender.php b/core/lib/Drupal/Core/Database/Query/SelectExtender.php
index 808593c..73952d0 100644
--- a/core/lib/Drupal/Core/Database/Query/SelectExtender.php
+++ b/core/lib/Drupal/Core/Database/Query/SelectExtender.php
@@ -320,6 +320,20 @@ public function execute() {
   /**
    * {@inheritdoc}
    */
+  public function executeTemporary() {
+    // By calling preExecute() here, we force it to preprocess the extender
+    // object rather than just the base query object.  That means
+    // hook_query_alter() gets access to the extended object.
+    if (!$this->preExecute($this)) {
+      return NULL;
+    }
+
+    return $this->query->executeTemporary();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   public function distinct($distinct = TRUE) {
     $this->query->distinct($distinct);
     return $this;
diff --git a/core/lib/Drupal/Core/Database/Query/SelectInterface.php b/core/lib/Drupal/Core/Database/Query/SelectInterface.php
index 248c4aa..cf62795 100644
--- a/core/lib/Drupal/Core/Database/Query/SelectInterface.php
+++ b/core/lib/Drupal/Core/Database/Query/SelectInterface.php
@@ -514,6 +514,14 @@ public function preExecute(SelectInterface $query = NULL);
   public function execute();
 
   /**
+   * Executes a SELECT query string and saves the results in a temporary table.
+   *
+   * @return string|null
+   *   The name of the temporary table, or NULL if the query was not successful.
+   */
+  public function executeTemporary();
+
+  /**
    * Helper function to build most common HAVING conditional clauses.
    *
    * This method can take a variable number of parameters. If called with two
diff --git a/core/modules/system/src/Tests/Database/TemporaryQueryTest.php b/core/modules/system/src/Tests/Database/TemporaryQueryTest.php
index 1bfa754..9a3fc9a 100644
--- a/core/modules/system/src/Tests/Database/TemporaryQueryTest.php
+++ b/core/modules/system/src/Tests/Database/TemporaryQueryTest.php
@@ -37,21 +37,16 @@ function testTemporaryQuery() {
       $this->fail('The creation of the temporary table failed.');
     }
 
-    // Now try to run two db_query_temporary() in the same request.
-    $table_name_test = db_query_temporary('SELECT name FROM {test}', array());
-    $table_name_task = db_query_temporary('SELECT pid FROM {test_task}', array());
+    // Now try to run two temporary table queries in the same request.
+    $query = db_select('test');
+    $query->addField('test', 'name');
+    $table_name_test = $query->executeTemporary();
+    $query = db_select('test_task');
+    $query->addField('test_task', 'pid');
+    $table_name_task = $query->executeTemporary();
 
     $this->assertEqual($this->countTableRows($table_name_test), $this->countTableRows('test'), 'A temporary table was created successfully in this request.');
     $this->assertEqual($this->countTableRows($table_name_task), $this->countTableRows('test_task'), 'A second temporary table was created successfully in this request.');
-
-    // Check that leading whitespace and comments do not cause problems
-    // in the modified query.
-    $sql = "
-      -- Let's select some rows into a temporary table
-      SELECT name FROM {test}
-    ";
-    $table_name_test = db_query_temporary($sql, array());
-    $this->assertEqual($this->countTableRows($table_name_test), $this->countTableRows('test'), 'Leading white space and comments do not interfere with temporary table creation.');
   }
 
 }
diff --git a/core/tests/Drupal/KernelTests/Core/Database/SelectTest.php b/core/tests/Drupal/KernelTests/Core/Database/SelectTest.php
index b524863..c004f1a 100644
--- a/core/tests/Drupal/KernelTests/Core/Database/SelectTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Database/SelectTest.php
@@ -527,4 +527,22 @@ function testEmptyInCondition() {
     }
   }
 
+  /**
+   * Tests Select::executeTemporary().
+   */
+  function testExecuteTemporary() {
+    $query = db_select('test');
+    $query->addField('test', 'name');
+    $query->addField('test', 'age', 'age');
+    $temporary_table = $query->executeTemporary();
+    $this->assertTrue($temporary_table, 'Temporary table name returned.');
+
+    $query = db_select($temporary_table);
+    $query->addField($temporary_table, 'name');
+    $query->addField($temporary_table, 'age');
+
+    $num_records = $query->countQuery()->execute()->fetchField();
+    $this->assertEquals(4, $num_records, 'Returned the correct number of rows.');
+  }
+
 }
