Skip to main content

πŸ“¦ Lesson 20: Importing & Exporting Data

Databases don't exist in isolation. You'll need to back them up before risky changes, migrate data between servers, share datasets with colleagues, load spreadsheet data into tables, and export query results for reports. This lesson covers the essential tools and techniques for moving data in and out of MySQL β€” from full database dumps to row-level CSV imports.

🎯 Learning Objectives

By the end of this lesson, you will be able to:

  • Create full and partial database backups with mysqldump
  • Restore databases and tables from .sql dump files
  • Export query results to CSV files with SELECT ... INTO OUTFILE
  • Import CSV data into tables with LOAD DATA INFILE
  • Use mysqlimport for command-line CSV loading
  • Import and export through phpMyAdmin
  • Design a practical backup strategy

Estimated Time: 45 minutes

Prerequisites: User Management & Permissions (Lesson 19)

πŸ“‘ In This Lesson

The Big Picture: Why Import/Export?

Here are the most common scenarios where you'll need to move data in or out of MySQL:

Scenario Direction Tool
Back up a database before making changes Export mysqldump
Restore a database after a disaster Import mysql (CLI)
Migrate a database to a new server Both mysqldump β†’ mysql
Load spreadsheet data into a table Import LOAD DATA INFILE
Export query results for a report Export SELECT ... INTO OUTFILE
Share data with a non-technical colleague Export CSV via phpMyAdmin or INTO OUTFILE
graph LR subgraph export["Export (Out of MySQL)"] D["mysqldump
.sql files"] C["INTO OUTFILE
.csv files"] P1["phpMyAdmin
GUI export"] end subgraph mysql["MySQL Server"] DB["Databases & Tables"] end subgraph import_["Import (Into MySQL)"] I1["mysql CLI
.sql files"] I2["LOAD DATA INFILE
.csv files"] I3["mysqlimport
.csv files"] I4["phpMyAdmin
GUI import"] end DB --> D DB --> C DB --> P1 I1 --> DB I2 --> DB I3 --> DB I4 --> DB

Exporting with mysqldump

mysqldump is the most important backup tool in the MySQL toolkit. It creates a .sql file containing all the SQL statements needed to recreate your database β€” CREATE TABLE, INSERT INTO, indexes, constraints, everything. Run it from your terminal (not inside the MySQL prompt).

Dump an Entire Database


# Dump the entire bookstore database to a file
mysqldump -u root -p bookstore > bookstore_backup.sql
                

This creates a file called bookstore_backup.sql in your current directory. The file contains:

  • DROP TABLE IF EXISTS statements (so the restore can replace existing tables)
  • CREATE TABLE statements with all columns, constraints, and indexes
  • INSERT INTO statements with all row data
  • View definitions, triggers, and routines (if present)

Dump Specific Tables


# Dump only the books and authors tables
mysqldump -u root -p bookstore books authors > books_authors_backup.sql
                

Dump Multiple Databases


# Dump two databases into one file
mysqldump -u root -p --databases bookstore school > multi_db_backup.sql
                

Dump All Databases


# Dump every database on the server (full server backup)
mysqldump -u root -p --all-databases > full_server_backup.sql
                

Structure Only (No Data)


# Dump table definitions only β€” useful for creating empty copies
mysqldump -u root -p --no-data bookstore > bookstore_schema.sql
                

Data Only (No Structure)


# Dump row data only β€” tables must already exist at the destination
mysqldump -u root -p --no-create-info bookstore > bookstore_data.sql
                

Useful Options

Option Purpose
--no-data Schema only, no row data
--no-create-info Data only, no CREATE TABLE
--databases db1 db2 Include CREATE DATABASE statements
--all-databases Dump every database on the server
--routines Include stored procedures and functions
--triggers Include triggers (on by default)
--single-transaction Consistent snapshot without locking (InnoDB)
--add-drop-database Add DROP DATABASE before each CREATE
--result-file=path Write to a specific file (Windows-friendly)

Production-Quality Dump


# A robust backup command for InnoDB databases
mysqldump -u root -p \
    --single-transaction \
    --routines \
    --triggers \
    --databases bookstore \
    > bookstore_$(date +%Y%m%d_%H%M%S).sql
                

The $(date +%Y%m%d_%H%M%S) part adds a timestamp to the filename, producing something like bookstore_20260416_143000.sql. This makes it easy to identify when each backup was taken.

πŸ’‘ --single-transaction

This is critical for production backups of InnoDB tables. Without it, mysqldump locks tables while dumping, which blocks writes and can stall your application. With --single-transaction, the dump uses a consistent snapshot β€” no locks, no downtime, and the data is consistent even if writes happen during the dump.

⚠️ Terminal, Not MySQL Prompt

mysqldump is a command-line utility β€” run it from your terminal/bash, not from inside the mysql> prompt. If you see ERROR 1064, you're probably inside MySQL. Type exit; first, then run the command.

Restoring from a Dump File

A .sql dump file is just a script full of SQL statements. To restore it, you feed it into the mysql client.

Restore an Entire Database


# Create the database first (if the dump doesn't include CREATE DATABASE)
mysql -u root -p -e "CREATE DATABASE IF NOT EXISTS bookstore;"

# Restore the dump into the bookstore database
mysql -u root -p bookstore < bookstore_backup.sql
                

Restore from a Multi-Database Dump


# If the dump used --databases or --all-databases, it includes
# CREATE DATABASE statements β€” no need to specify a database name
mysql -u root -p < multi_db_backup.sql
                

Restore a Single Table

If you only need one table from a full database dump, you can't directly extract it with the mysql client. Instead, use grep or a text editor to pull out the relevant section, or use a purpose-built approach:


# Dump just the one table you need (easier than extracting from a full dump)
mysqldump -u root -p bookstore books > books_only.sql

# Then restore it
mysql -u root -p bookstore < books_only.sql
                

Test Your Backups!

A backup you've never restored is a backup you can't trust. Periodically test your dumps:


# Create a temporary test database
mysql -u root -p -e "CREATE DATABASE bookstore_test;"

# Restore the backup into the test database
mysql -u root -p bookstore_test < bookstore_backup.sql

# Verify the data
mysql -u root -p -e "SELECT COUNT(*) FROM bookstore_test.books;"

# Clean up
mysql -u root -p -e "DROP DATABASE bookstore_test;"
                

βœ… The Golden Rule of Backups

Untested backups are not backups. Schedule regular test restores to confirm your dump files actually work. Discovering a corrupted backup during a real disaster is the worst possible time.

Exporting to CSV

CSV (Comma-Separated Values) files are the universal data exchange format. Everyone can open them β€” Excel, Google Sheets, Python, R, other databases. MySQL offers two main ways to export CSV.

Method 1: SELECT ... INTO OUTFILE

Run this inside the MySQL prompt:


-- Export all books to a CSV file
SELECT id, title, price, publication_year
INTO OUTFILE '/tmp/books_export.csv'
FIELDS TERMINATED BY ','
ENCLOSED BY '"'
LINES TERMINATED BY '\n'
FROM books;
                

This creates a clean CSV file at /tmp/books_export.csv:

Output File:


"1","The Great Gatsby","12.99","1925"
"2","To Kill a Mockingbird","14.99","1960"
"3","1984","11.99","1949"
                    

Adding a Header Row

INTO OUTFILE doesn't include column headers by default. Use a UNION to add them:


-- Export with a header row
SELECT 'id', 'title', 'price', 'publication_year'
UNION ALL
SELECT id, title, price, publication_year
INTO OUTFILE '/tmp/books_with_header.csv'
FIELDS TERMINATED BY ','
ENCLOSED BY '"'
LINES TERMINATED BY '\n'
FROM books;
                

⚠️ INTO OUTFILE Restrictions

  • The file must not already exist β€” MySQL won't overwrite. Delete the old file first or use a new name.
  • The file is written by the MySQL server process, so it goes where the server has write permission (typically /tmp or the MySQL data directory).
  • You need the FILE privilege to use INTO OUTFILE.
  • If secure_file_priv is set, you can only write to that directory.

Method 2: mysql Client with Redirect

From the terminal (not inside MySQL), use the -e flag with output redirection:


# Tab-separated output (default)
mysql -u root -p bookstore -e "SELECT id, title, price FROM books;" > books.tsv

# For true CSV, pipe through sed or tr
mysql -u root -p bookstore -e "SELECT id, title, price FROM books;" \
    | tr '\t' ',' > books.csv

# Batch mode with no headers
mysql -u root -p -N bookstore -e "SELECT id, title, price FROM books;" \
    | tr '\t' ',' > books_no_header.csv
                

πŸ’‘ Which Method to Choose?

INTO OUTFILE gives you precise control over delimiters, quoting, and escaping β€” ideal for clean, predictable CSV output. The mysql redirect method is simpler and works without FILE privileges, but the output is tab-separated by default and may need post-processing.

Check secure_file_priv

MySQL restricts where INTO OUTFILE and LOAD DATA INFILE can read/write files. Check this setting:


SHOW VARIABLES LIKE 'secure_file_priv';
                

Possible Results:


+------------------+------------------------+
| Variable_name    | Value                  |
+------------------+------------------------+
| secure_file_priv | /var/lib/mysql-files/   |  -- Can only use this directory
| secure_file_priv |                        |  -- Empty = any directory
| secure_file_priv | NULL                   |  -- Disabled entirely
+------------------+------------------------+
                    

Importing from CSV

Got a spreadsheet full of data? Save it as CSV and load it directly into MySQL with LOAD DATA INFILE.

Basic CSV Import


-- Import a CSV file into the books table
LOAD DATA INFILE '/tmp/new_books.csv'
INTO TABLE books
FIELDS TERMINATED BY ','
ENCLOSED BY '"'
LINES TERMINATED BY '\n'
IGNORE 1 ROWS;    -- Skip the header row
                

The IGNORE 1 ROWS tells MySQL to skip the first line β€” which is typically column headers in a CSV.

Map CSV Columns to Table Columns

If the CSV columns don't match the table columns in order (or you only want to import some columns):


-- CSV has: title, author_name, price, year
-- Table has: id (auto_increment), title, author_id, price, publication_year
LOAD DATA INFILE '/tmp/new_books.csv'
INTO TABLE books
FIELDS TERMINATED BY ','
ENCLOSED BY '"'
LINES TERMINATED BY '\n'
IGNORE 1 ROWS
(title, @author_name, price, publication_year)
SET author_id = (SELECT id FROM authors WHERE name = @author_name LIMIT 1);
                

The @author_name is a user variable β€” the CSV value is loaded into it, and the SET clause uses a subquery to look up the matching author_id.

Using LOAD DATA LOCAL INFILE

If the CSV file is on your client machine (not the MySQL server), use LOCAL:


-- Load from the client's local filesystem
LOAD DATA LOCAL INFILE '/home/practicalace/data/new_books.csv'
INTO TABLE books
FIELDS TERMINATED BY ','
ENCLOSED BY '"'
LINES TERMINATED BY '\n'
IGNORE 1 ROWS;
                

⚠️ LOCAL INFILE Security

LOAD DATA LOCAL INFILE must be enabled on both the server and client:


# Check if it's enabled
mysql -u root -p -e "SHOW VARIABLES LIKE 'local_infile';"

# Enable it on the server (requires restart or SET GLOBAL)
SET GLOBAL local_infile = 1;

# Connect with LOCAL enabled
mysql -u root -p --local-infile=1 bookstore
                    

Handling Common CSV Issues

Issue Solution
Windows line endings (\r\n) LINES TERMINATED BY '\r\n'
Tab-separated (TSV) FIELDS TERMINATED BY '\t'
Semicolon-separated (European CSVs) FIELDS TERMINATED BY ';'
Fields with commas inside quotes ENCLOSED BY '"' handles this
Escaped quotes inside fields ESCAPED BY '\\'
Empty values should be NULL Use SET col = NULLIF(@var, '')
Skip bad rows instead of aborting Check warnings: SHOW WARNINGS;

Handling NULLs and Empty Values


-- Treat empty strings as NULL for specific columns
LOAD DATA INFILE '/tmp/books.csv'
INTO TABLE books
FIELDS TERMINATED BY ',' ENCLOSED BY '"'
LINES TERMINATED BY '\n'
IGNORE 1 ROWS
(title, @price, @pub_year)
SET
    price = NULLIF(@price, ''),
    publication_year = NULLIF(@pub_year, '');
                

mysqlimport (CLI CSV Loading)

mysqlimport is a command-line wrapper around LOAD DATA INFILE. It's convenient for quick imports from the terminal.

Basic Usage


# The filename (minus extension) must match the table name!
# books.csv β†’ imported into the "books" table
mysqlimport -u root -p \
    --local \
    --fields-terminated-by=',' \
    --fields-enclosed-by='"' \
    --lines-terminated-by='\n' \
    --ignore-lines=1 \
    bookstore /tmp/books.csv
                

πŸ’‘ File Name = Table Name

mysqlimport determines the target table from the filename. A file called books.csv loads into the books table. A file called order_items.csv loads into the order_items table. Rename your file if needed before importing.

Useful mysqlimport Options

Option Purpose
--local Read file from the client machine
--ignore-lines=N Skip the first N lines (headers)
--replace Replace rows with duplicate keys
--ignore Skip rows with duplicate keys
--columns=col1,col2 Specify which columns the CSV maps to
--delete Empty the table before importing

Import/Export with phpMyAdmin

If you prefer a GUI, phpMyAdmin provides point-and-click import and export. This is especially handy for quick one-off tasks.

Exporting via phpMyAdmin

  1. Select your database in the left sidebar
  2. Click the Export tab
  3. Choose export method:
    • Quick β€” exports everything with defaults (usually fine)
    • Custom β€” pick tables, format, options
  4. Choose format: SQL (for backups) or CSV (for spreadsheets)
  5. Click Go β€” the file downloads to your computer

Importing via phpMyAdmin

  1. Select your database in the left sidebar
  2. Click the Import tab
  3. Click Choose File and select your .sql or .csv file
  4. For CSV imports, set the format options (delimiter, enclosure, escape character)
  5. Click Go

⚠️ phpMyAdmin File Size Limits

phpMyAdmin is limited by PHP's upload_max_filesize and post_max_size settings (often 2 MB or 8 MB by default). For large databases, use the command-line tools (mysqldump / mysql) instead. You can increase the limits in php.ini if needed.

βœ… When to Use phpMyAdmin vs CLI

phpMyAdmin is great for small files, quick exports, and when you want to visually verify the data. Command-line tools are better for large databases, automated backups, scripted workflows, and anything in production.

Backup Strategies

Having the tools is one thing β€” using them wisely is another. A good backup strategy answers: How often? Where do I store them? How long do I keep them? How fast can I restore?

The 3-2-1 Rule

A widely-used framework for data protection:

  • 3 copies of your data (the original + 2 backups)
  • 2 different storage types (e.g., local disk + cloud)
  • 1 copy offsite (in case of physical disaster)
graph TD ORIG["Original Database
(MySQL Server)"] B1["Backup Copy 1
(Local disk / NAS)"] B2["Backup Copy 2
(Cloud storage / S3)"] ORIG --> B1 ORIG --> B2

Automated Daily Backups (Cron Job)

On Linux, use cron to schedule automatic backups:


# Create a backup script
# File: /home/practicalace/scripts/mysql_backup.sh

#!/bin/bash
BACKUP_DIR="/home/practicalace/backups/mysql"
DATE=$(date +%Y%m%d_%H%M%S)
DB_NAME="bookstore"

# Create backup directory if it doesn't exist
mkdir -p "$BACKUP_DIR"

# Dump the database
mysqldump -u backup_user -pBackupPass123 \
    --single-transaction \
    --routines \
    --triggers \
    "$DB_NAME" > "$BACKUP_DIR/${DB_NAME}_${DATE}.sql"

# Compress the backup
gzip "$BACKUP_DIR/${DB_NAME}_${DATE}.sql"

# Delete backups older than 30 days
find "$BACKUP_DIR" -name "*.sql.gz" -mtime +30 -delete

echo "Backup completed: ${DB_NAME}_${DATE}.sql.gz"
                

# Make the script executable
chmod +x /home/practicalace/scripts/mysql_backup.sh

# Add to crontab (runs daily at 2:00 AM)
crontab -e
# Add this line:
0 2 * * * /home/practicalace/scripts/mysql_backup.sh >> /home/practicalace/logs/backup.log 2>&1
                

⚠️ Passwords in Scripts

Putting passwords in scripts is a security risk. For production, use a MySQL option file (~/.my.cnf) instead:


# File: ~/.my.cnf (chmod 600!)
[mysqldump]
user=backup_user
password=BackupPass123
                    

Then mysqldump reads credentials automatically β€” no -p flag needed.

Backup Comparison

Approach Speed Size Flexibility Best For
mysqldump (logical) Slower Larger (text) Very flexible Small–medium databases, portability
mysqldump + gzip Slower Much smaller Flexible Storage-conscious backups
File-system copy (physical) Fastest Exact size Less flexible Large databases, same server version
Replication Real-time Full copy Complex setup High availability, disaster recovery

Common Issues & Troubleshooting

❌ ERROR 1290: --secure-file-priv

Message: The MySQL server is running with the --secure-file-priv option so it cannot execute this statement

Cause: You're trying to use INTO OUTFILE or LOAD DATA INFILE with a path outside the allowed directory.

Fix: Check the allowed directory with SHOW VARIABLES LIKE 'secure_file_priv'; and use that path, or modify my.cnf to change it.

❌ ERROR 1064 with mysqldump

Message: Syntax error when running mysqldump.

Cause: You're running mysqldump from inside the MySQL prompt.

Fix: Type exit; to leave MySQL, then run mysqldump from the terminal.

❌ File Already Exists (INTO OUTFILE)

Message: File '/tmp/export.csv' already exists

Cause: INTO OUTFILE refuses to overwrite existing files for safety.

Fix: Delete the old file from the terminal: rm /tmp/export.csv, then re-run.

❌ LOAD DATA LOCAL INFILE Disabled

Message: Loading local data is disabled; this must be enabled on both the client and server side

Fix: Enable on the server: SET GLOBAL local_infile = 1; and reconnect with mysql --local-infile=1.

❌ Character Encoding Issues

Symptoms: Garbled characters (mojibake) after import.

Fix: Specify the character set during import:


LOAD DATA INFILE '/tmp/data.csv'
INTO TABLE my_table
CHARACTER SET utf8mb4
FIELDS TERMINATED BY ',' ENCLOSED BY '"'
LINES TERMINATED BY '\n';
                    

For mysqldump, use: mysqldump --default-character-set=utf8mb4

Exercises

πŸ‹οΈ Exercise 1: mysqldump Backup & Restore

Practice backing up and restoring the bookstore database:

  1. Dump the entire bookstore database to bookstore_backup.sql
  2. Dump only the books table to books_only.sql
  3. Dump the schema (no data) to bookstore_schema.sql
  4. Create a test database called bookstore_test and restore the full backup into it
  5. Verify the restore by querying the books table
  6. Clean up by dropping bookstore_test
βœ… Solution

# 1. Full dump
mysqldump -u root -p bookstore > bookstore_backup.sql

# 2. Single table
mysqldump -u root -p bookstore books > books_only.sql

# 3. Schema only
mysqldump -u root -p --no-data bookstore > bookstore_schema.sql

# 4. Create test DB and restore
mysql -u root -p -e "CREATE DATABASE bookstore_test;"
mysql -u root -p bookstore_test < bookstore_backup.sql

# 5. Verify
mysql -u root -p -e "SELECT COUNT(*) AS total_books FROM bookstore_test.books;"

# 6. Clean up
mysql -u root -p -e "DROP DATABASE bookstore_test;"
                        

πŸ‹οΈ Exercise 2: CSV Export

Export data from the bookstore database to CSV:

  1. Check your secure_file_priv setting
  2. Export the books table (id, title, price) to a CSV in the allowed directory
  3. Alternatively, use the mysql client redirect method to create a CSV in your home directory
βœ… Solution

-- 1. Check the allowed directory
SHOW VARIABLES LIKE 'secure_file_priv';
-- Let's assume: /var/lib/mysql-files/

-- 2. Export via INTO OUTFILE
SELECT id, title, price
INTO OUTFILE '/var/lib/mysql-files/books_export.csv'
FIELDS TERMINATED BY ','
ENCLOSED BY '"'
LINES TERMINATED BY '\n'
FROM books;
                        

# 3. Alternative: mysql client redirect
mysql -u root -p bookstore \
    -e "SELECT id, title, price FROM books;" \
    | tr '\t' ',' > ~/books_export.csv
                        

πŸ‹οΈ Exercise 3: CSV Import

Create a CSV file and import it into a new table:

  1. Create a CSV file called new_authors.csv with columns: name, country, birth_year
  2. Add 3–5 rows of data (with a header row)
  3. Create a table called imported_authors with matching columns
  4. Import the CSV using LOAD DATA INFILE (or LOCAL INFILE)
  5. Verify the import with a SELECT query
βœ… Solution

# 1-2. Create the CSV file
cat > /tmp/new_authors.csv << 'EOF'
name,country,birth_year
"Gabriel GarcΓ­a MΓ‘rquez","Colombia",1927
"Haruki Murakami","Japan",1949
"Chimamanda Ngozi Adichie","Nigeria",1977
"Isabel Allende","Chile",1942
EOF
                        

-- 3. Create the target table
CREATE TABLE imported_authors (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(100),
    country VARCHAR(50),
    birth_year INT
);

-- 4. Import the CSV
LOAD DATA INFILE '/tmp/new_authors.csv'
INTO TABLE imported_authors
FIELDS TERMINATED BY ',' ENCLOSED BY '"'
LINES TERMINATED BY '\n'
IGNORE 1 ROWS
(name, country, birth_year);

-- 5. Verify
SELECT * FROM imported_authors;
                        

🎯 Quick Quiz

Question 1: Where do you run mysqldump?

Question 2: What does --single-transaction do in a mysqldump command?

Question 3: What does IGNORE 1 ROWS do in a LOAD DATA INFILE statement?

Question 4: What is the 3-2-1 backup rule?

Summary

πŸŽ‰ Key Takeaways

  • mysqldump creates .sql backup files β€” run it from the terminal, not the MySQL prompt
  • Restore with mysql -u root -p database_name < backup.sql
  • Use --single-transaction for non-locking InnoDB backups
  • SELECT ... INTO OUTFILE exports query results to CSV (inside MySQL)
  • LOAD DATA INFILE imports CSV data into tables (inside MySQL)
  • IGNORE 1 ROWS skips the header row in CSV imports
  • mysqlimport is a CLI shortcut for LOAD DATA INFILE
  • phpMyAdmin offers GUI import/export for smaller files
  • Check secure_file_priv if file operations fail
  • Follow the 3-2-1 backup rule and always test your restores

Quick Reference

Task Command
Dump a database mysqldump -u root -p dbname > file.sql
Dump specific tables mysqldump -u root -p dbname tbl1 tbl2 > file.sql
Dump all databases mysqldump -u root -p --all-databases > file.sql
Schema only mysqldump -u root -p --no-data dbname > file.sql
Restore a dump mysql -u root -p dbname < file.sql
Export to CSV SELECT ... INTO OUTFILE '/path/file.csv' ...
Import from CSV LOAD DATA INFILE '/path/file.csv' INTO TABLE ...
CLI CSV import mysqlimport -u root -p --local dbname file.csv
Check file path restriction SHOW VARIABLES LIKE 'secure_file_priv';
Compress a backup gzip backup.sql β†’ backup.sql.gz

πŸ“š Additional Resources

πŸš€ What's Next?

In the final lesson, Lesson 21: Next Steps & Best Practices, we'll review everything you've learned across the entire course, highlight key best practices for working with MySQL, and map out where to go from here β€” from PHP and Python connectors to ORMs and beyond.

πŸŽ‰ Almost There!

You now know how to move data in and out of MySQL like a pro β€” backups, restores, CSV imports, and exports. One more lesson to go!