00:00

MySQL Copy Table

Last updated: 5 min read

Copying a table is useful for backups before a risky change, for test data, for archiving, and for experimenting without touching the original. MySQL has no single COPY TABLE command, but a few statements together let you copy exactly what you need: structure only, data only, or both.

1. The Source Table

Table: products (5 rows)

idskunamecategoryprice
1TEA-01Assam TeaBeverages320.00
2COF-02Filter CoffeeBeverages410.00
3BIS-03Oat BiscuitsSnacks95.00
4NUT-04Roasted AlmondsSnacks560.00
5HON-05Wild HoneyPantry450.00
Two ways to copy a tableMethod 1: CREATE TABLE LIKE copies the structure including primary key and indexes, then INSERT SELECT copies the rows. Method 2: CREATE TABLE AS SELECT copies columns and rows in one step but loses the primary key, indexes and AUTO_INCREMENT.productscolumnsPRIMARY KEYindexesAUTO_INCREMENTrows① CREATE TABLE copy LIKE products;INSERT INTO copy SELECT * FROM products;copies columns, keys, indexes, AUTO_INCREMENT and rows ✔② CREATE TABLE copy AS SELECT * FROM products;copies columns and rows only, no keys or indexes ✘
Figure 1: use method ① for a faithful copy; method ② is handy for quick snapshots and reports.

2. Method 1: Full Copy with LIKE + INSERT … SELECT

This is the most faithful copy. LIKE copies the exact definition (columns, defaults, primary key, unique and normal indexes, AUTO_INCREMENT); INSERT … SELECT copies the rows:

full-copy.sql
CREATE TABLE products_backup LIKE products;
INSERT INTO products_backup SELECT * FROM products;

SELECT COUNT(*) AS copied_rows FROM products_backup;
SHOW INDEX FROM products_backup;

Result: 1 row

copied_rows
5

Result: 3 rows

TableKey_nameColumn_nameNon_unique
products_backupPRIMARYid0
products_backupskusku0
products_backupidx_categorycategory1
LIKE does not copy foreign keys or triggers. Add them afterwards if the copy needs them.

3. Method 2: Quick Copy with CREATE TABLE … AS SELECT

ctas.sql
CREATE TABLE products_snapshot AS SELECT * FROM products;
SHOW CREATE TABLE products_snapshot;

Result: 1 row

TableCreate Table
products_snapshotCREATE TABLE `products_snapshot` ( `id` int NOT NULL DEFAULT '0', `sku` varchar(12) NOT NULL, `name` varchar(50) NOT NULL, `category` varchar(20) DEFAULT NULL, `price` decimal(8,2) NOT NULL DEFAULT '0.00' ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci

The columns and rows are there, but there is no primary key, no unique key, no index and no AUTO_INCREMENT. That's fine for a read-only snapshot or report, but not for a table your application will keep using.

4. Copying Only the Structure

structure.sql
CREATE TABLE products_template LIKE products;
SELECT COUNT(*) AS rows_in_template FROM products_template;

Result: 1 row

rows_in_template
0

An older trick, CREATE TABLE t AS SELECT * FROM products WHERE 1 = 0;, also gives an empty table, but without keys and indexes.

5. Copying Some Rows or Columns

Add a WHERE clause, pick columns, or even calculate new values while copying:

partial.sql
CREATE TABLE snack_prices AS
SELECT sku, name, price, ROUND(price * 0.9, 2) AS sale_price
FROM products
WHERE category = 'Snacks';

SELECT * FROM snack_prices;

Result: 2 rows

skunamepricesale_price
BIS-03Oat Biscuits95.0085.50
NUT-04Roasted Almonds560.00504.00

To add more rows to an existing copy, use INSERT … SELECT with a column list:

append.sql
INSERT INTO snack_prices (sku, name, price, sale_price)
SELECT sku, name, price, ROUND(price * 0.85, 2) FROM products WHERE category = 'Pantry';
SELECT * FROM snack_prices;

Result: 3 rows

skunamepricesale_price
BIS-03Oat Biscuits95.0085.50
NUT-04Roasted Almonds560.00504.00
HON-05Wild Honey450.00382.50

6. Re-running a Copy Without Duplicates

Running INSERT … SELECT again into a table with a primary key fails on the first duplicate:

query.sql
INSERT INTO products_backup SELECT * FROM products;
Output
ERROR 1062 (23000): Duplicate entry '1' for key 'products_backup.PRIMARY'

Use INSERT IGNORE to skip existing rows, or an upsert to refresh them:

refresh.sql
UPDATE products SET price = 99 WHERE sku = 'BIS-03';

INSERT INTO products_backup SELECT * FROM products AS src
ON DUPLICATE KEY UPDATE price = src.price;

SELECT sku, price FROM products_backup WHERE sku = 'BIS-03';

Result: 1 row

skuprice
BIS-0399.00

7. Copying to Another Database

other-db.sql
CREATE DATABASE reporting;
CREATE TABLE reporting.products LIKE shopdb.products;
INSERT INTO reporting.products SELECT * FROM shopdb.products;
SELECT COUNT(*) AS rows_in_reporting FROM reporting.products;

Result: 1 row

rows_in_reporting
5

8. Copying to Another Server

Between servers, export and import with mysqldump:

Terminal
# one table: structure + data
mysqldump -u root -p shopdb products > products.sql
mysql -h other-server -u root -p targetdb < products.sql

# or stream it directly, without a file
mysqldump -u root -p shopdb products | mysql -h other-server -u root -p targetdb
For very large tables, MySQL Shell's util.dumpTables() and util.loadDump() copy in parallel and are much faster.

9. Copying Large Tables

  • INSERT … SELECT on a huge table runs as one big transaction; copy in batches by primary-key range (WHERE id BETWEEN 1 AND 100000, then the next range).
  • Create the copy's secondary indexes after loading the data, which is often faster than loading into an indexed table.
  • The copy needs as much disk space as the original; check free space first.

10. Which Method to Use

GoalStatement
Exact copy to keep usingCREATE TABLE new LIKE old; INSERT INTO new SELECT * FROM old;
Quick snapshot / report tableCREATE TABLE new AS SELECT …;
Empty table with same structureCREATE TABLE new LIKE old;
Some rows or columnsCREATE TABLE new AS SELECT cols FROM old WHERE …;
Another servermysqldump or MySQL Shell dump/load

11. Practice Exercises

Exercise 1: Make a full copy of products called products_v2 containing only Beverages, keeping keys and indexes.
solution.sql
CREATE TABLE products_v2 LIKE products;
INSERT INTO products_v2 SELECT * FROM products WHERE category = 'Beverages';
SELECT sku, name FROM products_v2;

Result: 2 rows

skuname
TEA-01Assam Tea
COF-02Filter Coffee
Exercise 2: Create a table category_summary holding each category with its product count and average price.
solution.sql
CREATE TABLE category_summary AS
SELECT category, COUNT(*) AS items, ROUND(AVG(price), 2) AS avg_price
FROM products GROUP BY category;
SELECT * FROM category_summary;

Result: 3 rows

categoryitemsavg_price
Beverages2365.00
Pantry1450.00
Snacks2329.50

12. Summary

  • Full copy: CREATE TABLE … LIKE + INSERT … SELECT.
  • CREATE TABLE … AS SELECT is quick but loses keys, indexes and AUTO_INCREMENT.
  • Filter rows, choose columns or compute values while copying.
  • Use INSERT IGNORE or ON DUPLICATE KEY UPDATE when copying again.
  • Qualify names to copy across databases; use mysqldump or MySQL Shell across servers.