---
title: "Oracle to MariaDB migration guide"
description: "Convert an Oracle export from SQL Developer or SQLcl into a MariaDB script with the same tables, keys, indexes and data, and every rule the converter applies."
canonical: https://intosql.com/docs/migrations/oracle-to-mariadb
last_updated: 2026-10-15
---

# Oracle to MariaDB migration guide

IntoSQL reads the `.sql` file SQL Developer's Database Export or SQLcl writes, with DBMS_METADATA's table definitions and `Insert into` statements, and turns it into one MariaDB script with the same tables, columns, keys, indexes, foreign keys, defaults, counters and rows. It evaluates what those exports write for values: `to_date` and `to_timestamp` with their format, `TO_CLOB` and `HEXTORAW` pieces, decimal commas from German or Spanish sessions. Every value MariaDB can't hold as it is, and every object it doesn't carry, is listed in the report. The script loads on MariaDB 10.11 and later.

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

## Export for the best result

In SQL Developer, choose Tools, Database Export, with DDL and the Insert data format, as one file. SQLcl writes the same data with `SET SQLFORMAT INSERT`. Before exporting, set a date format that keeps the time and the century:

```sql
ALTER SESSION SET NLS_DATE_FORMAT = 'SYYYY-MM-DD HH24:MI:SS';
ALTER SESSION SET NLS_TIMESTAMP_FORMAT = 'SYYYY-MM-DD HH24:MI:SS.FF9';
```

- The default format, `DD-MON-RR`, writes each date without its time and with two digits of the year; the report says so (`oracle_date_format`) and each date is read as its day, between 1950 and 2049.
- A `.dmp` file from `exp` or `expdp` is binary. Import it into an Oracle database, Oracle Database Free runs in Docker, and export it with SQL Developer; the converter says so when it gets one (`dump_oracle_binary`).
- Every test export comes from Oracle 21c and 23ai, in American, German and ISO sessions, and is compared with its source cell by cell.

## Convert

In the browser, open [Oracle to MariaDB](/oracle-to-mariadb) or the converter on the home page, drop the export and choose MariaDB. It reads the file from your disk in pieces, twice, and writes the script as it goes, so the file 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
intosql convert shop.sql --to mariadb --output shop.mariadb.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), an export converts like any other file: open a conversion with its name and `"output": {"dialect": "mariadb"}`.

## Load into MariaDB

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

The script sets the character set and a binary collation, so text compares exactly as in Oracle, then creates each table with its keys and copies the rows with foreign key and unique checks off. Identity columns become `AUTO_INCREMENT`, sequences become MariaDB sequences, and defaults that draw from a sequence keep drawing from it. NaN and Infinity become NULL (`invalid_value_null`).

### Or load it straight in

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

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

It stops before writing anything when one of the dump's tables already exists. MariaDB 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, packages, procedures, functions, triggers other than one that fills a column from a sequence, types and synonyms are written in PL/SQL. They are listed in the report by kind and name, never dropped silently, so you can recreate them in MariaDB.


## Data types

[Oracle to MariaDB data types](/docs/types/oracle-to-mariadb) 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 Oracle | In MariaDB | Why | Report line |
| --- | --- | --- | --- |
| collations that ignore case (BINARY_CI) | exact comparison (utf8mb4_bin) | Tables use utf8mb4_bin so unique keys keep apart what Oracle kept apart; columns that ignored case lose that. Accent rules differ between versions, so no _ci collation matches Oracle's on every one. | `case_insensitive_collation` |
| CREATE SEQUENCE | CREATE SEQUENCE with the same options | The export writes where a sequence has reached as its START WITH, so it continues from there. | None |
| virtual columns (GENERATED ALWAYS AS ... VIRTUAL) | generated columns, STORED when PERSISTED, VIRTUAL otherwise | MariaDB computes them from the same formula; the values the export wrote for them are left out. | None |
| virtual columns with functions IntoSQL doesn't write, and the \|\| operator | a plain column | A formula that can't be written for MariaDB leaves a plain column and a report line; the values the export wrote for virtual columns are left out, and MariaDB has none to compute. | `generated_not_translated` |
| defaults with functions IntoSQL doesn't write (SYS_GUID(), \|\|) | no default | A default that can't be written is left out and reported. | `default_not_translated` |
| CHECK constraints | translated | The same checks, tested as each row loads. | None |
| CHECK constraints with functions IntoSQL doesn't write | not created | A check that can't be written is left out and reported. | `check_skipped` |
| CHECK constraints that are DISABLE or ENABLE NOVALIDATE | not created | Oracle doesn't apply these checks to the rows already there, so the rows may break them. | `check_not_enforced` |
| function-based, bitmap and domain indexes | not created | IntoSQL writes b-tree indexes on whole columns; create the others again after loading. | `index_skipped` |
| foreign keys | foreign keys, loaded with FOREIGN_KEY_CHECKS=0 | The rows load in any order, and the keys check new rows afterwards. | None |
| foreign keys to tables the export doesn't create, and DISABLE or NOVALIDATE ones | not created | A key can't name a table that isn't there, and one Oracle doesn't check may have rows without a parent. | `foreign_key_skipped` |
| partitioned tables | one table with every partition's rows | The rows are the same; partition the table again in MariaDB if it needs it. | `partitions_dropped` |
| several schemas in one export | one database, tables named after their schema first | MariaDB's schemas are databases, which an export loaded into one database can't create; an export of one schema keeps its tables' names. | `schemas_merged` |
| 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` |
| views, synonyms, types, packages, procedures, functions and other triggers | listed in the report | Their code is written in PL/SQL, so they are listed for you to recreate. | `not_converted_objects` |
| exports without CREATE TABLE (data only) | types read from the values | A data-only export has only rows, so the types come from the values. | `no_schema` |
| expressions in INSERT values other than the ones exports write | NULL | A converter that doesn't run a database can't evaluate them; to_date, to_timestamp, TO_CLOB, HEXTORAW and the others exports write are read. | `expression_value` |
| text decoded twice (Ã±) | copied as it is | The script holds exactly these characters, so they are copied; the report points them out. | `mojibake_suspected` |
