VPS Snaps

How to import a SQL file into MySQL or MariaDB

Create the target database, then run mysql -u root -p mydb < mydb.sql. For a gzipped dump, use gunzip -c mydb.sql.gz | mysql -u root -p mydb; inside the client, source mydb.sql does the same. Failed imports almost always come down to a missing database, a version or collation mismatch, or privileged lines in the dump, and each has a short fix below.

11 min readUpdated Checked against official documentation

Check what kind of dump it is

A dump of one database loads into whatever database you name. A dump made with --databases or --all-databases has its own CREATE DATABASE and USE lines, which decide where the data goes. Check which you have (for a .gz file, pipe gunzip -c mydb.sql.gz into the same grep without the file name):

Terminal
grep -n -m 5 -E '^(CREATE DATABASE|USE )' mydb.sql

No output means a single-database dump. head -n 5 mydb.sql shows which program wrote it: -- MySQL dump or -- MariaDB dump, followed by the version.

A dump with USE lines writes into the database it names, so on the live server it overwrites the original even when you meant to restore a copy. Edit those lines first, or use a separate server.

Create the database and import

Making the dump is covered in MySQL backups with mysqldump. On MariaDB you can type mariadb wherever this guide says mysql.

Terminal
mysql -u root -p -e "CREATE DATABASE mydb CHARACTER SET utf8mb4"
Terminal
mysql -u root -p mydb < mydb.sql
  • -u root -p logs in as root and prompts for the password, keeping it out of shell history. Any account that can create tables, views, triggers and routines there will do.
  • mydb is the database the statements run in. Leave it out for a dump with its own USE lines.
  • If root logs in through the Unix socket (the Ubuntu and Debian default), run sudo mysql mydb < mydb.sql instead.

The client stops at the first error, reports the line of the failing statement, as in ERROR 1273 (HY000) at line 25: Unknown collation: 'utf8mb4_0900_ai_ci', and exits with status 1. Fix the cause and import again into an empty database. --force carries on past errors, but the result is missing whatever failed; use it only to list every problem in one pass.

Import with source inside the client

MySQL / MariaDB prompt
CREATE DATABASE IF NOT EXISTS mydb;
USE mydb;
source /home/deploy/mydb.sql

source (or \.) is a client command, not SQL, so the path is on the machine running mysql. Interactively, it prints each error and keeps going, so failures can scroll past unnoticed; for big files the shell form is easier to check.

Import a gzipped dump

Terminal
gunzip -c mydb.sql.gz | mysql -u root -p mydb

gunzip -c (or zcat) writes the SQL to the pipe and leaves the file untouched. Redirecting the .gz file straight into mysql fails with an ASCII '\0' appeared in the statement error.

If the .gz file is cut short, gunzip fails partway, but mysql loads whatever arrived and exits 0. Test the file with gzip -t mydb.sql.gz first, and use set -o pipefail in scripts so the pipeline fails when any part of it does.

Show progress on a large import

The client prints nothing while it works. pv (Pipe Viewer) shows how much of the file has been read, the speed and the time left. It is a separate package: sudo apt install pv on Debian and Ubuntu. Its progress bar and the password prompt garble each other, so put the login in ~/.my.cnf, which the client reads on its own, and chmod 600 the file:

~/.my.cnf
[client]
user=root
password="your-password-here"
Terminal
pv mydb.sql.gz | gunzip | mysql mydb

The percentage is the share of the compressed file read so far; heavily indexed tables load slower than their share. Delete ~/.my.cnf afterwards if you do not want the password left on disk.

Make large imports faster

Check what the dump already does. Unless made with --compact, mysqldump and mariadb-dump files switch off unique and foreign key checks near the top, switch them back on at the end, and use multi-row INSERTs, which the MySQL manual says import quickly as they are:

mydb.sql
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;

For SQL from other tools, or a --compact dump, set the same session variables yourself. --init-command runs one statement as the client connects:

Terminal
mysql --init-command="SET SESSION foreign_key_checks=0, unique_checks=0" -u root -p mydb < export.sql
  • unique_checks=0 lets InnoDB batch its secondary index writes. The manual's condition: be certain the data contains no duplicate keys.
  • foreign_key_checks=0 lets tables load in any order. Rows loaded this way are not re-checked when the checks come back on, so use it only for data that was consistent when dumped.
  • Both last for this connection only and need no special privilege.

On a brand-new, empty MySQL server, ALTER INSTANCE DISABLE INNODB REDO_LOG; speeds up the first load; ALTER INSTANCE ENABLE INNODB REDO_LOG; turns logging back on. Never on production: a crash while it is off can corrupt the instance.

Run the import on the database server, not across a network. Beyond that, the format is the limit: every row is replayed and every index rebuilt. For very large databases, physical backups with XtraBackup restore far faster.

Character sets and --default-character-set

mysqldump and mariadb-dump write a SET NAMES line near the top, so client and server agree on the encoding. Files from other tools, --compact dumps and pieces cut from a dump may not have one. If grep -m 1 'SET NAMES' mydb.sql finds nothing, tell the client the encoding, or accented letters and emoji can be stored double-encoded (é where é should be):

Terminal
mysql --default-character-set=utf8mb4 -u root -p mydb < export.sql

Import a single table

A dump of just that table (mysqldump mydb orders > orders.sql) imports like any other: it drops and recreates orders and leaves the rest alone. To take one table out of a full dump, restore the dump into a scratch database and copy the table across. With the login in ~/.my.cnf as above:

Terminal
mysql -e "CREATE DATABASE mydb_scratch"
Terminal
gunzip -c mydb.sql.gz | mysql mydb_scratch
Terminal
mysqldump mydb_scratch orders | mysql mydb
Terminal
mysql -e "DROP DATABASE mydb_scratch"

If the dump is too big to restore twice, cut the table out. mysqldump starts each table with a -- Table structure for table comment, so this keeps everything from the orders header to the next one:

Terminal
gunzip -c mydb.sql.gz | sed -n '/^-- Table structure for table `orders`/,/^-- Table structure for table/p' > orders.sql

The piece lacks the dump header, so load it with the settings the header would have set:

Terminal
mysql --init-command="SET SESSION foreign_key_checks=0" --default-character-set=utf8mb4 -u root -p mydb < orders.sql

This needs the comments, which --compact and --skip-comments dumps lack; if orders is the last table, the piece also picks up the views and routines after it. For one database out of an --all-databases dump, create it and run mysql --one-database shop -u root -p < all-databases.sql; the manual calls the option rudimentary, since it filters only on USE lines.

Check the import worked

An import without errors is a good sign, not proof. Count the objects on the source and the copy; the numbers should match:

MySQL / MariaDB prompt
SELECT
  (SELECT COUNT(*) FROM information_schema.TABLES
     WHERE TABLE_SCHEMA = 'mydb' AND TABLE_TYPE = 'BASE TABLE') AS tables_count,
  (SELECT COUNT(*) FROM information_schema.TABLES
     WHERE TABLE_SCHEMA = 'mydb' AND TABLE_TYPE = 'VIEW') AS views_count,
  (SELECT COUNT(*) FROM information_schema.ROUTINES
     WHERE ROUTINE_SCHEMA = 'mydb') AS routines_count,
  (SELECT COUNT(*) FROM information_schema.TRIGGERS
     WHERE TRIGGER_SCHEMA = 'mydb') AS triggers_count;

Count rows in the tables that matter with SELECT COUNT(*), not with TABLE_ROWS from information_schema, which for InnoDB can be 40 to 50 percent off. To compare contents, CHECKSUM TABLE reads every row and returns one number:

MySQL / MariaDB prompt
CHECKSUM TABLE mydb.orders;

Different values mean the tables differ; equal values mean they almost certainly match. The value depends on row format, so compare the same server version, and the table is read-locked while it runs. Whether your application starts against the copy is the real test: see how to test a backup restore.

Errors and how to fix them

ErrorCause and fix
ERROR 1045 (28000): Access denied for user 'app'@'localhost' (using password: YES)Wrong user, password or host. (using password: NO) means you left out -p. If root uses socket login, run sudo mysql.
ERROR 1049 (42000): Unknown database 'mydb'The database does not exist yet. Create it first.
ERROR 1046 (3D000) at line 22: No database selectedA single-database dump has no USE line. Name the database on the command line.
ERROR 1050 (42S01) at line 25: Table 'orders' already existsThe dump has no DROP TABLE lines (made with --compact or --skip-add-drop-table). Import into an empty database.
ERROR 1064 (42000) at line 31: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '…' at line 1A statement this server does not understand: often a dump from a newer version or the other product, or a file that is not plain SQL. Parsing stopped at the text after near; view it with sed -n '31p' mydb.sql | cut -c1-300 and edit it. MariaDB says MariaDB server version.
ERROR 2006 (HY000) at line 2240: MySQL server has gone away or ERROR 2013 (HY000) at line 2240: Lost connection to MySQL server during queryUsually a statement bigger than max_allowed_packet (by default 64MB on the server, 16MB in the client): raise both (below). Otherwise check the server log for a crash or out-of-memory kill. MariaDB's client says Server has gone away.
ERROR 1227 (42000) at line 18: Access denied; you need (at least one of) the SUPER, SYSTEM_VARIABLES_ADMIN or SESSION_VARIABLES_ADMIN privilege(s) for this operationA dump from a MySQL server with GTIDs: it runs SET @@SESSION.SQL_LOG_BIN= 0; and sets @@GLOBAL.GTID_PURGED, which managed databases rarely allow. Re-dump with --set-gtid-purged=OFF, or strip the lines (below).
ERROR 1227 (42000) at line 1514: Access denied; you need (at least one of) the SUPER or SET_ANY_DEFINER privilege(s) for this operationA view, trigger or routine names another account as DEFINER (MySQL 8.0 says SUPER or SET_USER_ID). Import as an admin, create that account first, or rewrite the definers (below).
ERROR 1273 (HY000) at line 25: Unknown collation: 'utf8mb4_0900_ai_ci'A MySQL 8 dump going into MariaDB before 11.4.5, or into MySQL 5.7. Replace the collation (below).
ERROR 1824 (HY000) at line 40: Failed to open the referenced table 'customers'Foreign key checks are on and a child table came before its parent. Import with --init-command="SET SESSION foreign_key_checks=0". The same problem can show as ERROR 1215 (HY000): Cannot add foreign key constraint, or on MariaDB as error 1005 with errno: 150 "Foreign key constraint is incorrectly formed". Still failing with checks off? The column types must match and the referenced columns must be indexed.
ERROR at line 1: Unknown command '\-'.A dump from mariadb-dump 10.5.25, 10.6.18, 10.11.8, 11.0.6, 11.1.5, 11.2.4 or 11.4.2 starts with a sandbox-mode line the MySQL client does not understand. Skip line 1 (below).
ERROR at line 1: ASCII '\0' appeared in the statement, but this is not allowed unless option --binary-mode is enabled and mysql is run in non-interactive mode. …A compressed file went straight into mysql. Use gunzip -c in the pipe.

Raise the packet limit on the server (admin account; new connections pick it up) and on the client:

MySQL / MariaDB prompt
SET GLOBAL max_allowed_packet = 1073741824;
Terminal
mysql --max-allowed-packet=1G -u root -p mydb < mydb.sql

1G is the maximum. To keep it after a restart, add max_allowed_packet = 1G under [mysqld] in the server's config. To strip the GTID lines from a dump you cannot remake (the GTID set can span several lines, so the script skips to the closing semicolon):

Terminal
gunzip -c mydb.sql.gz | awk '/^SET @@GLOBAL.GTID_PURGED/ {skip=1} skip {if (/;$/) skip=0; next} /^SET @@SESSION.SQL_LOG_BIN/ {next} {print}' | mysql -u root -p mydb

To make every view, trigger and routine belong to the account running the import:

Terminal
sed -E 's/DEFINER=`[^`]+`@`[^`]+`/DEFINER=CURRENT_USER/g' mydb.sql > mydb-fixed.sql

To replace MySQL 8's default collation with one every version knows (grep -o 'utf8mb4_0900_[a-z_]*' mydb.sql | sort -u lists any other 0900 collations; on MariaDB 10.10 or later, utf8mb4_uca1400_ai_ci follows newer Unicode sorting rules, as MySQL's does):

Terminal
sed -i 's/utf8mb4_0900_ai_ci/utf8mb4_unicode_ci/g' mydb.sql

To skip the sandbox line of an affected MariaDB dump:

Terminal
tail -n +2 mydb.sql | mysql -u root -p mydb

Frequently asked questions

How do I import a large SQL file into MySQL?
Use the command-line client on the database server itself, watch it with pv, and raise max_allowed_packet if it fails with 'server has gone away'. For hundreds of gigabytes, a physical backup restores far faster.
How do I import a .sql.gz file into MySQL?
Decompress it in the pipe: gunzip -c file.sql.gz | mysql -u root -p mydb. The .gz file is left as it is.
Can I import a MySQL dump into MariaDB?
Usually. The common snag is MySQL 8's utf8mb4_0900_ai_ci collation, which MariaDB accepts only from 11.4.5; on older versions replace it with utf8mb4_unicode_ci.
How do I skip errors when importing a SQL file?
mysql --force continues past errors, but the database ends up missing whatever failed. Use it to list the problems, fix them, then import again into an empty database.

How this was checked

Commands, limits and prices were checked against these official pages, on October 4, 2026: