---
title: "MariaDB to MySQL migration guide"
description: "Convert a MariaDB dump into a MySQL script with the same tables, keys and data: how to dump, convert, load, and every rule the converter applies."
canonical: https://intosql.com/docs/migrations/mariadb-to-mysql
last_updated: 2026-10-15
---

# MariaDB to MySQL migration guide

IntoSQL reads a MariaDB dump and writes one MySQL script with the same tables, columns, keys, indexes, foreign keys, defaults, counters, sequences, comments and rows. It reads the dump itself, without a database server, and lists in the report every value MySQL can't hold as it is and every object it doesn't carry. The script loads on MySQL 5.7 and later.

**Convert in the browser:** [MariaDB to MySQL](/mariadb-to-mysql) opens the converter set up for this pair.

## Dump for the best result

Any dump from mariadb-dump, mysqldump, phpMyAdmin or Adminer from MariaDB 10.3 on works, as `.sql` or `.sql.gz`. These options keep more of your data:

```shell
mariadb-dump --default-character-set=utf8mb4 --hex-blob \
  --routines --triggers --events --single-transaction \
  shop | gzip > shop.sql.gz
```

- `--default-character-set=utf8mb4` keeps emoji and other 4-byte characters. A dump made with `utf8mb3` has already lost them, and the report says so (`dump_lost_four_byte_chars`).
- `--hex-blob` writes binary columns and `VECTOR` values as hexadecimal, which survives copying between systems.
- `--routines --triggers --events` add those objects to the dump, so the report can list them by name.

A dump made with `--no-create-info` still converts: the types are read from the values, and the report says the schema was missing (`no_schema`). MariaDB's `JSON` columns, which the dump writes as `longtext` with a `json_valid` check, are read as JSON again.

## Convert

In the browser, open [MariaDB to MySQL](/mariadb-to-mysql) or the converter on the home page, drop the dump and choose MySQL. It reads the file from your disk in pieces, twice, and writes the script as it goes, so the dump is never uploaded and its size isn't limited by memory. The [desktop app](/docs/desktop) does the same with the file on disk.

With the [command line](/docs/cli):

```shell
intosql inspect shop.sql.gz
intosql convert shop.sql.gz --to mysql --output shop.mysql.sql --report shop.report.json
```

`inspect` lists the tables, their rows and what won't be converted, without writing anything. `convert` writes the script and, with `--report`, the report as JSON.

Through the [API](/docs/api), a dump converts like any other file: open a conversion with its name ending in `.sql` or `.sql.gz` and `"output": {"dialect": "mysql"}`.

## Load into MySQL

```shell
mysql -e "CREATE DATABASE shop"
mysql --default-character-set=utf8mb4 shop < shop.mysql.sql
```

The script sets the character set, the time zone and a SQL mode that accepts the dump's zero and impossible dates, then creates each table with its keys and copies the rows with foreign key and unique checks off. Each `AUTO_INCREMENT` counter continues where the dump left it. MariaDB's collations that MySQL lacks (`utf8mb4_uca1400_ai_ci`) are replaced and reported (`collation_replaced`). `UUID` becomes `char(36)`, `INET4` and `INET6` become `varchar`, and `VECTOR` a `json` array of its floats (`type_replaced`). MySQL has no sequences, so they and the defaults that draw from them are reported (`sequence_not_converted`, `default_not_translated`); system-versioned tables keep their current rows (`system_versioning_dropped`).

### Or load it straight in

`intosql load` and the [desktop app](/docs/desktop) load the dump into MySQL without a script, with the same tables, keys and indexes the script creates:

```shell
intosql load shop.sql.gz --into mysql://ana@localhost/shop
```

It stops before writing anything when one of the dump's tables already exists. MySQL can't roll back a table, so a failed load drops the tables it created. Loading into a database server is part of IntoSQL Pro, with a 14-day trial. See [Loading](/docs/loading#database-dumps).

## What isn't converted

Views, triggers, stored procedures, functions and events are written in MariaDB's own language. They are listed in the report by kind and name, never dropped silently, so you can recreate them in MySQL.


## Data types

[MariaDB to MySQL data types](/docs/types/mariadb-to-mysql) lists each type that changes in the script: what it becomes, why, and the report line.

## Every other rule

Generated from the engine, where each rule is decided.

| In MariaDB | In MySQL | Why | Report line |
| --- | --- | --- | --- |
| utf8mb4_0900_* and utf8mb4_uca1400_* collations | utf8mb4_unicode_520_ci (utf8mb4_bin for binary ones) | MySQL doesn't know the newer collations, the error that stops most MySQL 8 dumps from loading elsewhere. unicode_520 follows the same Unicode rules most closely. | `collation_replaced` |
| ON UPDATE CURRENT_TIMESTAMP | kept | The same clause, with the column's fractions of a second. | None |
| generated columns | VIRTUAL or STORED as in the source | The same formula, translated term by term. | None |
| generated columns with functions IntoSQL doesn't read | a plain column | A formula IntoSQL can't read leaves a plain column and a report line. | `generated_not_translated` |
| INVISIBLE columns | regular columns | MySQL has invisible columns from 8.0.23, MariaDB from 10.3. | `invisible_column` |
| CHECK constraints | kept (MySQL 5.7 parses and ignores them) | The same checks, translated term by term. | None |
| CHECK constraints with functions IntoSQL doesn't read | not created | A check IntoSQL can't read is left out and reported. | `check_skipped` |
| CHECK ... NOT ENFORCED | not created | MySQL 5.7 and MariaDB can't keep a check without applying it, and the rows may break it. | `check_not_enforced` |
| expression defaults, and defaults on TEXT, BLOB, JSON and GEOMETRY | no default | MySQL takes them from 8.0.13; the script loads on 5.7, so they are left out and reported. MariaDB takes them. | `default_not_translated` |
| functional indexes, KEY ((expr)) | not created | MySQL indexes expressions from 8.0.13 and MariaDB not at all; a generated column with an index does the same. | `index_skipped` |
| keys and indexes on a prefix, c(20) | kept | The same index. | None |
| FULLTEXT indexes | kept | The same index; MATCH ... AGAINST works as before. | None |
| SPATIAL indexes | kept | The same index. | None |
| foreign keys | kept, loaded with FOREIGN_KEY_CHECKS=0 | The same keys; rows load as they did from the source dump. | None |
| PARTITION BY | one table | The rows are the same; partition the table again if it needs it. | `partitions_dropped` |
| WITH SYSTEM VERSIONING (MariaDB) | a table with the current rows | A dump holds the current rows only; MySQL has no system versioning. | `system_versioning_dropped` |
| Aria, ColumnStore, CONNECT, Spider, RocksDB (MariaDB) | InnoDB | MySQL has none of these engines. Every other engine is kept. | `engine_dropped` |
| CREATE SEQUENCE (MariaDB) | not created | MySQL has no sequences. | `sequence_not_converted` |
| views, triggers, procedures, functions and events | listed in the report | They are listed for you to recreate; their code often names its definer and server features. | `not_converted_objects` |
| NOT NULL columns with values that become NULL | NOT NULL dropped | A column whose values had to become NULL can't keep NOT NULL, or the load would stop. | `not_null_relaxed` |
| text decoded twice (Ã±) | copied as it is | The dump holds exactly these characters, so they are copied; the report points them out. | `mojibake_suspected` |
| dumps without CREATE TABLE | types read from the values | A dump made with --no-create-info only has rows, so the types come from the values. | `no_schema` |
| SET NAMES utf8 from utf8mb4 tables | converted, with a warning | MySQL 5.x mysqldump writes 3-byte utf8 by default and replaces every emoji with a question mark before any converter sees the file. | `dump_lost_four_byte_chars` |
| dumps without SET NAMES whose text isn't UTF-8 | read as Latin-1 (Windows-1252) | MySQL's latin1 is Windows-1252, the usual encoding of such dumps. | `charset_guessed` |
| sjis, cp932, gbk, big5 dumps | read as UTF-8, with a warning | These encodings are not decoded yet. | `unsupported_charset` |
| UNHEX('..'), CONVERT(b'..', UNSIGNED), CONVERT(".." USING x), ST_GeomFromText('..') in rows | evaluated | Some dump tools write values as these calls; they are read as the values they produce. | None |
| other expressions in rows | NULL | A converter that doesn't run a database can't evaluate them. | `expression_value` |
| DEFAULT in VALUES | the column's default value | A literal default is copied into the row. | `default_keyword_filled` |
| DEFAULT in VALUES without a literal default | NULL | There is no value to copy. | `default_keyword_null` |
| REPLACE and INSERT IGNORE | plain rows | A dump has one row per key, so the rows are the same. | `replace_as_insert` |
