Inserting data into a temporary table
Efficiently Inserting Data into Temporary Tables in SQL Server

Learn various methods for populating temporary tables in SQL Server, from basic INSERT statements to advanced techniques like SELECT INTO and CTEs, optimizing your database operations.
Temporary tables are a powerful feature in SQL Server, providing a way to store and process intermediate results within a session. They are particularly useful for breaking down complex queries, improving readability, and sometimes even enhancing performance by reducing the amount of data processed in a single step. This article explores the most common and efficient methods for inserting data into these transient tables, helping you choose the right approach for your specific needs.
Understanding Temporary Tables
Before diving into data insertion, it's crucial to understand the two main types of temporary tables in SQL Server: local temporary tables and global temporary tables. Their scope and visibility dictate how and when they can be accessed and when they are automatically dropped.
flowchart TD A[Start Session] --> B{Create Temp Table?} B -->|Local #table| C[Local Temp Table] C --> D[Visible only to current session] D --> E[Dropped when session ends] B -->|Global ##table| F[Global Temp Table] F --> G[Visible to all sessions] G --> H[Dropped when all referencing sessions end] E & H --> I[End Session]
Lifecycle and Scope of Local vs. Global Temporary Tables
#
) (e.g., #MyTempTable
), while global temporary tables are prefixed with a double hash (##
) (e.g., ##GlobalTempTable
). Local temp tables are automatically dropped when the session that created them ends, whereas global temp tables persist until all sessions referencing them have disconnected.Methods for Inserting Data
There are several ways to populate a temporary table, each with its own advantages. The choice often depends on whether the table already exists, the source of the data, and performance considerations.
1. SELECT INTO
(Create and Populate in One Step)
This is often the simplest and most efficient method when you need to create a temporary table and populate it with data from an existing query result set. SELECT INTO
creates the table structure based on the source query's columns and data types, then inserts the data.
SELECT
CustomerID,
OrderDate,
TotalAmount
INTO
#RecentOrders
FROM
Sales.Orders
WHERE
OrderDate >= DATEADD(month, -1, GETDATE());
Using SELECT INTO to create and populate a local temporary table.
SELECT INTO
, the temporary table must not already exist. If it does, SQL Server will throw an error. This method is generally faster than CREATE TABLE
followed by INSERT INTO
for initial population, as it minimizes transaction logging.2. CREATE TABLE
followed by INSERT INTO
This two-step approach gives you explicit control over the temporary table's schema, including column names, data types, nullability, and constraints, before inserting data. This is ideal when you need a specific table structure that might differ from the source query or when you plan to insert data from multiple sources.
CREATE TABLE #ProductSummary (
ProductID INT PRIMARY KEY,
ProductName NVARCHAR(255) NOT NULL,
TotalSales DECIMAL(18, 2),
LastSaleDate DATE
);
INSERT INTO #ProductSummary (ProductID, ProductName, TotalSales, LastSaleDate)
SELECT
p.ProductID,
p.Name AS ProductName,
SUM(od.OrderQty * od.UnitPrice) AS TotalSales,
MAX(o.OrderDate) AS LastSaleDate
FROM
Production.Product AS p
JOIN
Sales.SalesOrderDetail AS od ON p.ProductID = od.ProductID
JOIN
Sales.SalesOrderHeader AS o ON od.SalesOrderID = o.SalesOrderID
GROUP BY
p.ProductID, p.Name;
Creating a temporary table with a defined schema, then inserting aggregated data.
3. INSERT INTO ... VALUES
For inserting a small, fixed set of rows, or when you need to manually add specific data, the INSERT INTO ... VALUES
statement is appropriate. This is less common for populating temporary tables with large datasets but useful for testing or adding default values.
CREATE TABLE #ConfigSettings (
SettingName NVARCHAR(50) PRIMARY KEY,
SettingValue NVARCHAR(255)
);
INSERT INTO #ConfigSettings (SettingName, SettingValue)
VALUES
('MaxRetries', '5'),
('TimeoutSeconds', '30'),
('LogLevel', 'INFO');
Inserting specific values into a temporary configuration table.
4. Using Common Table Expressions (CTEs) with INSERT INTO
While CTEs themselves don't directly insert into temporary tables, they are often used in conjunction with INSERT INTO
to define complex subqueries or recursive logic, making the data source for the temporary table more readable and manageable.
CREATE TABLE #EmployeeHierarchy (
EmployeeID INT,
EmployeeName NVARCHAR(255),
ManagerID INT,
Level INT
);
WITH EmployeeCTE AS (
-- Anchor member: Top-level employees (no manager)
SELECT
e.BusinessEntityID AS EmployeeID,
p.FirstName + ' ' + p.LastName AS EmployeeName,
e.OrganizationNode.GetAncestor(1).GetLevel() AS ManagerID, -- Simplified for example
0 AS Level
FROM
HumanResources.Employee AS e
JOIN
Person.Person AS p ON e.BusinessEntityID = p.BusinessEntityID
WHERE
e.OrganizationNode.GetAncestor(1) IS NULL -- Top-level employees
UNION ALL
-- Recursive member: Employees reporting to managers
SELECT
e.BusinessEntityID,
p.FirstName + ' ' + p.LastName,
e.OrganizationNode.GetAncestor(1).GetLevel(), -- Simplified for example
cte.Level + 1
FROM
HumanResources.Employee AS e
JOIN
Person.Person AS p ON e.BusinessEntityID = p.BusinessEntityID
JOIN
EmployeeCTE AS cte ON e.OrganizationNode.GetAncestor(1).GetLevel() = cte.EmployeeID -- Simplified join
)
INSERT INTO #EmployeeHierarchy (EmployeeID, EmployeeName, ManagerID, Level)
SELECT
EmployeeID,
EmployeeName,
ManagerID,
Level
FROM
EmployeeCTE;
Populating a temporary table with hierarchical data using a recursive CTE.
DECLARE @MyTable TABLE (...)
) or even permanent staging tables if the data needs to persist beyond a single session or transaction.Best Practices and Performance Considerations
When working with temporary tables, optimizing your data insertion strategy can significantly impact performance.
1. Choose the Right Method
For initial population where the table doesn't exist, SELECT INTO
is generally the fastest. If you need a specific schema or plan to insert data incrementally, CREATE TABLE
followed by INSERT INTO
is more appropriate.
2. Index Temporary Tables
If you plan to join or filter on columns in your temporary table, create appropriate indexes after inserting the data. This can dramatically improve query performance. For SELECT INTO
, you can add indexes in a separate CREATE INDEX
statement.
3. Minimize Data Movement
Only insert the columns and rows you truly need into the temporary table. Avoid SELECT *
if you only use a few columns, as this reduces I/O and memory usage.
4. Consider Table Variables for Small Datasets
For very small datasets (typically less than a few thousand rows) that don't require indexes or statistics, table variables (DECLARE @MyTable TABLE (...)
) can be a lighter-weight alternative to temporary tables, as they are memory-resident and have less overhead.
5. Explicitly Drop Temporary Tables (Optional)
While temporary tables are automatically dropped, explicitly dropping them with DROP TABLE #MyTempTable
when they are no longer needed can free up resources sooner, especially in long-running processes or loops.