Copy Data from One Table to Another in SQL Server: SQL in Sixty Seconds #031

To copy data from one table to another, use INSERT…SELECT or SELECT…INTO. After my earlier article on INSERT INTO SELECT, readers asked for a video and an AdventureWorks example. This episode answers both, and the tests below go further.

Gouache painting of two wooden trays of blank cards connected by a short bridge, with cards crossing in one orderly group.

A Source Table to Practice On

The recording used the AdventureWorks2012 sample database. This script builds a small table of its own, so it runs on any server. If you want the sample data instead, see Install AdventureWorks and WideWorldImporters. Run everything in a scratch database.

The table has an identity column, a primary key, a default, a check constraint and an extra index. That gives us something to look for in the copy. One in three people has EmailPromotion = 2.

CREATE TABLE dbo.Customers
(
    CustomerId int IDENTITY(100,5) NOT NULL CONSTRAINT PK_Customers PRIMARY KEY,
    FirstName varchar(50) NOT NULL,
    LastName varchar(50) NOT NULL,
    EmailPromotion tinyint NOT NULL CONSTRAINT DF_Customers_Promo DEFAULT (0)
        CONSTRAINT CK_Customers_Promo CHECK (EmailPromotion BETWEEN 0 AND 2),
    City varchar(30) NULL
);
CREATE INDEX IX_Customers_LastName ON dbo.Customers (LastName);
WITH n AS (SELECT TOP (10000) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS rn
           FROM sys.all_objects a CROSS JOIN sys.all_objects b)
INSERT dbo.Customers (FirstName, LastName, EmailPromotion, City)
SELECT CONCAT('First', rn), CONCAT('Last', rn % 500), rn % 3, CASE WHEN rn % 7 = 0 THEN NULL ELSE 'Austin' END
FROM n;

Method 1: INSERT…SELECT

Create the destination first, then fill it. You control every column, type and name. This copied 3,333 rows.

CREATE TABLE dbo.PromoCopy1 (FirstName varchar(50) NOT NULL, LastName varchar(50) NOT NULL);
INSERT INTO dbo.PromoCopy1 (FirstName, LastName)
SELECT FirstName, LastName FROM dbo.Customers WHERE EmailPromotion = 2;

Method 2: SELECT…INTO

One statement creates the table and fills it. It also copied 3,333 rows.

SELECT CustomerId, FirstName, LastName, EmailPromotion, City
INTO dbo.PromoCopy2
FROM dbo.Customers WHERE EmailPromotion = 2;

Quick reference card: Copy Data from One Table to Another in SQL Server: SQL in Sixty Seconds #031

What SELECT…INTO Copies and What It Drops

The new table got the same column types, lengths and nullability. It also kept the identity property, with seed 100 and increment 5. The existing values were copied as they are, not renumbered, so the first ids were 105, 120 and 135. It dropped everything else.

TableIndexesKey constraintsCheck constraintsDefault constraints
dbo.Customers2111
dbo.PromoCopy20000

Add the missing pieces yourself when the copy needs them.

ALTER TABLE dbo.PromoCopy2 ADD CONSTRAINT PK_PromoCopy2 PRIMARY KEY (CustomerId);
CREATE INDEX IX_PromoCopy2_LastName ON dbo.PromoCopy2 (LastName);

After those two statements the copy had two indexes. To copy only the structure, add a condition that is never true. WHERE 1 = 0 created the table with zero rows.

Errors You Can Hit

Each of these failed in my tests. Know the message before you meet it at a bad moment.

MistakeResult
SELECT…INTO a table that existsMsg 2714: There is already an object named ‘PromoCopy2’ in the database.
Explicit values into an identity columnMsg 544: Cannot insert explicit value for identity column when IDENTITY_INSERT is set to OFF.
Select list longer than the column listMsg 121: The select list for the INSERT statement contains more items than the insert list.
NULL into a NOT NULL columnMsg 515: Cannot insert the value NULL into column ‘LastName’. The statement copied nothing.

For the identity case, turn the setting on around the copy. The next script copied the 3,333 rows with ids from 105 to 50085.

CREATE TABLE dbo.PromoCopy3 (CustomerId int IDENTITY(1,1) PRIMARY KEY, FirstName varchar(50) NOT NULL, LastName varchar(50) NOT NULL);
SET IDENTITY_INSERT dbo.PromoCopy3 ON;
INSERT INTO dbo.PromoCopy3 (CustomerId, FirstName, LastName)
SELECT CustomerId, FirstName, LastName FROM dbo.Customers WHERE EmailPromotion = 2;
SET IDENTITY_INSERT dbo.PromoCopy3 OFF;

Copy Into Another Database

Use a three-part name for the destination. This copied 3,333 rows into a table in a second database.

SELECT FirstName, LastName
INTO ArchiveDb.dbo.PromoElsewhere
FROM dbo.Customers WHERE EmailPromotion = 2;

Change the Data While You Copy

The SELECT can reshape the data on the way. This copy joins the names into one column and writes the city in capital letters. Expression columns get types from the expression, so check them afterwards.

SELECT CONCAT(FirstName, ' ', LastName) AS FullName, UPPER(City) AS City, EmailPromotion
INTO dbo.PromoNames
FROM dbo.Customers WHERE EmailPromotion = 2;

The new table held 3,333 rows. FullName came out as varchar(101), which is 50 plus 50 plus the space. Because the sources were NOT NULL, it is NOT NULL too. City stayed nullable, so a missing city stayed missing instead of becoming empty text.

Check the Copy

A copy is only done when you have compared it with the source. Compare the counts, then use EXCEPT to look for rows that did not arrive.

SELECT (SELECT COUNT(*) FROM dbo.Customers WHERE EmailPromotion = 2) AS SourceCount,
       (SELECT COUNT(*) FROM dbo.PromoCopy1) AS Copy1Count,
       (SELECT COUNT(*) FROM dbo.PromoCopy2) AS Copy2Count;
SELECT COUNT(*) AS MissingFromCopy
FROM (SELECT FirstName, LastName FROM dbo.Customers WHERE EmailPromotion = 2
      EXCEPT
      SELECT FirstName, LastName FROM dbo.PromoCopy1) AS x;

All three counts were 3,333 and nothing was missing. Remember that EXCEPT ignores duplicate rows, so the count check still matters.

A Quick Backup Before a Change

SELECT...INTO is a fast way to keep the old rows before an update or a delete. Copy the rows you are about to change into a new table. Run the change. If the result looks wrong, compare the two tables. Drop the backup table when the change is final.

Which Method to Use

Use INSERT...SELECT when the destination already exists or has rules you want to keep. Use SELECT...INTO for a quick working copy, a staging table or a backup of rows before a change. Plan the keys and indexes of that copy yourself.

Related Reading

A copy is not done when the rows arrive, it is done when you have checked them.

Published by Pinal Dave on SQLAuthority. More of my work at pinaldave.com.


Discover more from SQL Authority with Pinal Dave

Subscribe to get the latest posts sent to your email.

SQL Constraint and Keys, SQL in Sixty Seconds, SQL Scripts, SQL Table Operation
Previous Post
SQL Server Connection Errors: SQL in Sixty Seconds #030
Next Post
Concat Strings in SQL Server: SQL in Sixty Seconds #035

Related Posts

20 Comments. Leave new

  • Hi, Pinal. This is a nice way to copy data from one table to another, but wouldn’t it create problems when the source and destination tables have identity turned on?

    I guess, we have to turn off the identity off on the destination table, copy the data and then turn on the identity on again.

    Is there an easy way to do this ?

    Thanks.

    Reply
  • Look at SET IDENTITY_INSERT
    It allows you to insert identities.

    Reply
  • I would like to meet those developers personaly who were using cursor to copy data from one table to another table.

    Reply
  • Hi,

    I would like to meet those developers personaly who were using cursor to copy data from one table to another table.

    Thanks,
    Akash

    Reply
  • Hi Pinal ,
    with select into method I dont get the original table structure such as the key /primay .

    Reply
  • We have Sql server 2008 R2 and i would like to set up my new test server and copy production DB to new test Server

    We are taking back up – differential back up daily and during week end we take full back up.
    We have 4-6 DB on one server and couple of them kind of Data Warehousing (Dimensional and Fact Table).
    We don’t have that much sensitive data and we have only couple developers.
    Could you please let me know that i am taking correct steps?

    1) Will install Sql server 2008 R2 on new server
    2) Copy latest back up files for all the databases into new server
    3) Restored one by one database backup into new server
    4) Collect login script from source server and run into target server to handle orphan logins
    5) Run DBCC check command to verify the restore

    Reply
  • Fast and easy approach, Thanks Pinal..

    Reply
  • iamAkashSingh

    I would like to meet those developers personaly who were using cursor to copy data from one table to another table

    The best comment :))))

    Reply
  • Hi Pinal, can u help me out to get the query to copy table (WIth all constraints) to a diff scema with same tablename and in same db?

    …Sanjeeb

    Reply
  • Hi, Pinal, what if the table is large, I mean some million records? server hangs… “insufficient disk space” because the tempdb… how to deal with that? thanks

    Reply
  • Great article

    Reply
  • Hi Pinal
    I want to insert data in table A from table B .
    Both the tables have unique column ID .
    Can I do this bulk insertion operation based on condition “Where A.ID=B.ID”.

    Reply
  • Pinal,

    I am always pleased to see your articles when they come up in searches. Usually very helpful. Thank you for all the great articles.

    Option #1 although good from the point of minimal logging and giving the option to add table locks if needed. It does not do one key thing the option #2 does,

    One of the prime reasons I use option #2 is to copy a table structure and data without knowing the source table structure.

    Is there a better option when the table structures are dynamic or unknown and have the advantages of option #1 of minimal logging?

    Thanks,
    Rahul

    Reply
  • sadinesh12@gmail.com
    August 21, 2013 5:46 pm

    very clear explanation, thank u so much boss,

    cheers

    Reply
  • how if the 2 tables are not in the same database ? thx in advance

    Reply
  • Thank u sir …thank you so much

    Reply
  • Vishal Dixit
    July 29, 2015 8:24 pm

    Hello, what to do to create table from another table with specific collumn in mssql

    Reply
    • Use tempdb
      go
      Create table SourceTable (i int, j char(10), k char(10))
      go
      insert into SourceTable values (1,’Pinal’,’Dave’)
      go
      Select i, j into DestinationTable from SourceTable
      go
      Select * from DestinationTable

      Reply
  • Arun Vishnoi
    July 21, 2017 9:26 am

    Hello Sir,

    Can u tell me how to copy 1 core data into the same structure table 1 table 5 core data and another table blank how to move 1 core data in to anoter table fast.

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

Fill out this field
Fill out this field
Please enter a valid email address.