INTO Clause (Transact-SQL)
SELECT…INTO creates a new table in the default filegroup and inserts the resulting rows from the query into it. To view the complete SELECT syntax, see SELECT (Transact-SQL).
When an existing identity column is selected into a new table, the new column inherits the IDENTITY property, unless one of the following conditions is true:
The SELECT statement contains a join, GROUP BY clause, or aggregate function.
Multiple SELECT statements are joined by using UNION.
The identity column is listed more than one time in the select list.
The identity column is part of an expression.
The identity column is from a remote data source.
If any one of these conditions is true, the column is created NOT NULL instead of inheriting the IDENTITY property. If an identity column is required in the new table but such a column is not available, or you want a seed or increment value that is different than the source identity column, define the column in the select list using the IDENTITY function. See "Creating an identity column using the IDENTITY function" in the Examples section below.
The following limitations and restrictions apply to the INTO clause:
You cannot specify a table variable or table-valued parameter as the new table.
You cannot use SELECT…INTO to create a partitioned table, even when the source table is partitioned. SELECT...INTO does not use the partition scheme of the source table; instead, the new table is created in the default filegroup. To insert rows into a partitioned table, you must first create the partitioned table and then use the INSERT INTO...SELECT FROM statement.
When a computed column is included in the select list, the corresponding column in the new table is not a computed column. The values in the new column are the values that were computed at the time SELECT...INTO was executed.
SELECT...INTO cannot be used with COMPUTE.
The FILESTREAM attribute does not transfer to the new table. FILESTREAM BLOBs are copied and stored in the new table as varbinary(max) BLOBs. Without the FILESTREAM attribute, the varbinary(max) data type has a limitation of 2 GB. If a FILESTREAM BLOB exceeds this value, error 7119 is raised and the statement is stopped.
Indexes, constraints, and triggers defined in the source table are not transferred to the new table, nor can they be specified in the SELECT...INTO statement. If these objects are required, you must create them after executing the SELECT...INTO statement.
Specifying an ORDER BY clause does not guarantee the rows are inserted in the specified order.
The amount of logging for SELECT...INTO depends on the recovery model in effect for the database. Under the simple recovery model or bulk-logged recovery model, bulk operations are minimally logged. With minimal logging, using the SELECT… INTO statement can be more efficient than creating a table and then populating the table with an INSERT statement. For more information, see Operations That Can Be Minimally Logged.
A. Creating a table by specifying columns from multiple sources
The following example creates the table dbo.EmployeeAddresses by selecting seven columns from various employee-related and address-related tables.
USE AdventureWorks; GO SELECT c.FirstName, c.LastName, e.Title, a.AddressLine1, a.City, sp.Name AS [State/Province], a.PostalCode INTO dbo.EmployeeAddresses FROM Person.Contact AS c JOIN HumanResources.Employee AS e ON e.ContactID = c.ContactID JOIN HumanResources.EmployeeAddress AS ea ON ea.EmployeeID = e.EmployeeID JOIN Person.Address AS a on a.AddressID = ea.AddressID JOIN Person.StateProvince as sp ON sp.StateProvinceID = a.StateProvinceID; GO
B. Inserting rows using minimal logging
The following example creates the table dbo.NewProducts and inserts rows from the Production.Product table. The example assumes that the recovery model of the AdventureWorks database is set to FULL. To ensure minimal logging is used, the recovery model of the AdventureWorks database is set to BULK_LOGGED before rows are inserted and reset to FULL after the SELECT...INTO statement. This process ensures that the SELECT...INTO statement uses minimal space in the transaction log and performs efficiently.
USE AdventureWorks; GO IF OBJECT_ID ('dbo.NewProducts', 'U') IS NOT NULL DROP TABLE dbo.NewProducts; GO ALTER DATABASE AdventureWorks SET RECOVERY BULK_LOGGED; GO SELECT * INTO dbo.NewProducts FROM Production.Product WHERE ListPrice > $25 AND ListPrice < $100; GO ALTER DATABASE AdventureWorks SET RECOVERY FULL; GO
C. Creating an identity column using the IDENTITY function
The following example uses the IDENTITY function to create an identity column in the new table Person.USAddress. This is required because the SELECT statement that defines the table contains a join, which causes the IDENTITY property to not transfer to the new table. Notice that the seed and increment values specified in the IDENTITY function are different from those of the AddressID column in the source table Person.Address.
USE AdventureWorks; GO -- Determine the IDENTITY status of the source column AddressID. SELECT OBJECT_NAME(object_id) AS TableName, name AS column_name, is_identity, seed_value, increment_value FROM sys.identity_columns WHERE name = 'AddressID'; -- Create a new table with columns from the existing table Person.Address. A new IDENTITY -- column is created by using the IDENTITY function. SELECT IDENTITY (int, 100, 5) AS AddressID, a.AddressLine1, a.City, b.Name AS State, a.PostalCode INTO Person.USAddress FROM Person.Address AS a INNER JOIN Person.StateProvince AS b ON a.StateProvinceID = b.StateProvinceID WHERE b.CountryRegionCode = N'US'; -- Verify the IDENTITY status of the AddressID columns in both tables. SELECT OBJECT_NAME(object_id) AS TableName, name AS column_name, is_identity, seed_value, increment_value FROM sys.identity_columns WHERE name = 'AddressID';
D. Creating a table by specifying columns from a remote data source
The following example demonstrates three methods of creating a new table on the local server from a remote data source. The example begins by creating a link to the remote data source. The linked server name, MyLinkServer, is then specified in the FROM clause of the first SELECT...INTO statement and in the OPENQUERY function of the second SELECT...INTO statement. The third SELECT...INTO statement uses the OPENDATASOURCE function, which specifies the remote data source directly instead of using the linked server name.
USE master; GO -- Create a link to the remote data source. -- Specify a valid server name for @datasrc as 'server_name' or 'server_name\instance_name'. EXEC sp_addlinkedserver @server = N'MyLinkServer', @srvproduct = N' ', @provider = N'SQLNCLI', @datasrc = N'server_name', @catalog = N'AdventureWorks'; GO USE AdventureWorks; GO -- Specify the remote data source in the FROM clause using a four-part name -- in the form linked_server.catalog.schema.object. SELECT * INTO dbo.Departments FROM MyLinkServer.AdventureWorks.HumanResources.Department GO -- Use the OPENQUERY function to access the remote data source. SELECT * INTO dbo.DepartmentsUsingOpenQuery FROM OPENQUERY(MyLinkServer, 'SELECT * FROM AdventureWorks.HumanResources.Department'); GO -- Use the OPENDATASOURCE function to specify the remote data source. -- Specify a valid server name for Data Source using the format server_name or server_name\instance_name. SELECT * INTO dbo.DepartmentsUsingOpenDataSource FROM OPENDATASOURCE('SQLNCLI', 'Data Source=server_name;Integrated Security=SSPI') .AdventureWorks.HumanResources.Department; GO