π¦ 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
.sqldump files - Export query results to CSV files with
SELECT ... INTO OUTFILE - Import CSV data into tables with
LOAD DATA INFILE - Use
mysqlimportfor 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 |
.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 EXISTSstatements (so the restore can replace existing tables)CREATE TABLEstatements with all columns, constraints, and indexesINSERT INTOstatements 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
/tmpor the MySQL data directory). - You need the
FILEprivilege to useINTO OUTFILE. - If
secure_file_privis 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
- Select your database in the left sidebar
- Click the Export tab
- Choose export method:
- Quick β exports everything with defaults (usually fine)
- Custom β pick tables, format, options
- Choose format: SQL (for backups) or CSV (for spreadsheets)
- Click Go β the file downloads to your computer
Importing via phpMyAdmin
- Select your database in the left sidebar
- Click the Import tab
- Click Choose File and select your
.sqlor.csvfile - For CSV imports, set the format options (delimiter, enclosure, escape character)
- 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)
(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:
- Dump the entire
bookstoredatabase tobookstore_backup.sql - Dump only the
bookstable tobooks_only.sql - Dump the schema (no data) to
bookstore_schema.sql - Create a test database called
bookstore_testand restore the full backup into it - Verify the restore by querying the books table
- 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:
- Check your
secure_file_privsetting - Export the books table (id, title, price) to a CSV in the allowed directory
- Alternatively, use the
mysqlclient 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:
- Create a CSV file called
new_authors.csvwith columns: name, country, birth_year - Add 3β5 rows of data (with a header row)
- Create a table called
imported_authorswith matching columns - Import the CSV using
LOAD DATA INFILE(orLOCAL INFILE) - 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
mysqldumpcreates.sqlbackup files β run it from the terminal, not the MySQL prompt- Restore with
mysql -u root -p database_name < backup.sql - Use
--single-transactionfor non-locking InnoDB backups SELECT ... INTO OUTFILEexports query results to CSV (inside MySQL)LOAD DATA INFILEimports CSV data into tables (inside MySQL)IGNORE 1 ROWSskips the header row in CSV importsmysqlimportis a CLI shortcut forLOAD DATA INFILE- phpMyAdmin offers GUI import/export for smaller files
- Check
secure_file_privif 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
- MySQL Docs β mysqldump
- MySQL Docs β LOAD DATA Statement
- MySQL Docs β SELECT ... INTO OUTFILE
- MySQL Docs β mysqlimport
- MySQL Docs β Backup and Recovery
π 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!