关于 oracle-to-mariadb
oracle-to-mariadb 是为计划或执行 Oracle 数据库迁移到 MariaDB 的团队编写的参考 Skill。它介绍 Oracle 兼容模式、数据类型映射、需手动改写的内容和迁移工具,并说明版本相关注意事项。
主要功能
- 01说明 Oracle 兼容模式
- 02列出 Oracle 到 MariaDB 的类型映射
- 03指出需要手动改写的语法
- 04汇总迁移工具和版本注意事项
适用场景
- 01迁移前评估 Oracle 架构兼容性
- 02规划不支持 SQL 结构的改写
- 03检查数据类型与事务行为差异
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ mariadb/ skills --skill oracle-to-mariadb安装后在 Claude Code 中输入/oracle-to-mariadb确认已加载。
详细介绍
Oracle 到 MariaDB 迁移指南
最后更新:2026-06-04
为什么选择 MariaDB 进行 Oracle 迁移
MariaDB 是唯一原生兼容 PL/SQL 的开源数据库。MySQL 没有与 Oracle 兼容模式相当的功能。PostgreSQL 则需要彻底重写 PL/SQL。MariaDB 的 sql_mode=ORACLE 可让约 80% 的 Oracle 代码无需修改即可运行,包括存储过程、程序包、游标、异常处理和 Oracle 风格函数。
相较于其他开源替代方案,MariaDB 还有以下优势:
- Connect 存储引擎——迁移期间通过 ODBC 联合查询实时 Oracle 数据,无需完全切换
- 系统版本控制表——可用于时态数据,与 Oracle Flashback Archive 相匹配;这是开源数据库中独有的功能
- ColumnStore——列式分析引擎,可与 Oracle 的 In-Memory ColumnStore 相媲美
- 成本:组织通常可将 Oracle 许可成本降低 70–90%
**要求:**使用 MariaDB Community Server 10.3+ 才能启用 Oracle 兼容模式。使用 10.6+ 才能支持
ROWNUM、TO_CHAR()、ADD_MONTHS()、SYS_GUID()。当前 LTS 版本为 11.8(2025 年 5 月)。
第一步:启用 Oracle 兼容模式
SET sql_mode = 'ORACLE';
-- or permanently in configuration:
-- sql_mode = ORACLE
如果不启用此模式,PL/SQL 语法、Oracle 数据类型同义词和 Oracle 风格函数都无法使用。这是最重要的一步,也是最常被遗漏的一步。
LLM 容易出错的地方
| 你可能看到的内容 | 正确做法 |
|---|---|
| PL/SQL 因语法错误而无法运行 | 先设置 sql_mode=ORACLE——否则无法识别 PL/SQL 结构 |
将 Oracle DATE 映射为 MariaDB DATE | Oracle DATE 同时存储日期和时间——应映射为 DATETIME,而非 DATE |
| 认为 PL/SQL 兼容率为 100% | 约 80% 的代码可直接运行;SYNONYM、INSERT ALL 和 CONNECT BY 通常需要重写——12.1+ 的 Oracle 模式支持 (+) 连接(见下一行) |
在架构或代码中使用 SYNONYM | MariaDB 没有等效功能——应替换为视图或直接引用对象 |
(+) 外连接表示法 | MariaDB 12.1 起的 Oracle 模式支持此表示法(MDEV-13817)——旧版本应重写为 LEFT JOIN / RIGHT JOIN |
START WITH ... CONNECT BY | 不支持——应使用 WITH RECURSIVE 重写为递归 CTE |
TIMESTAMP WITH TIME ZONE | 迁移时会丢失时区信息——将变为 DATETIME;应在应用程序中处理时区 |
数据类型映射
sql_mode=ORACLE 会自动处理以下类型:
| Oracle | MariaDB | 说明 |
|---|---|---|
VARCHAR2(n) | VARCHAR(n) | 自动 |
NUMBER(p,s) | DECIMAL(p,s) | 自动 |
NUMBER | DOUBLE | 自动 |
DATE | DATETIME | **自动,但请注意:**Oracle DATE 包含时间;MariaDB DATE 不包含时间 |
CLOB | LONGTEXT | 自动 |
BLOB | LONGBLOB | 自动 |
RAW(n) | VARBINARY(n) | 自动 |
CHAR(n > 255) | VARCHAR(n) | 手动 |
TIMESTAMP WITH TIME ZONE | DATETIME | 手动——时区信息会丢失 |
BFILE | LONGBLOB | 手动——文件路径的存储方式不同 |
ROWID | CHAR(10) | 手动 |
sql_mode=ORACLE 的覆盖范围
PL/SQL 语法(10.3+)
- 包:
CREATE PACKAGE、CREATE PACKAGE BODY— 从 11.4 起,也可在sql_mode=ORACLE之外使用(MDEV-10075),因此可在混合模式代码库中使用包 - 游标:显式、隐式、带参数、
%ISOPEN、%ROWCOUNT、%FOUND、%NOTFOUND - 用于预处理语句的游标(12.3+)
- 游标变量:
TYPE ... IS REF CURSOR(13.0+)— 将游标作为过程参数传递并作为返回值返回 - 预定义的弱类型
SYS_REFCURSOR(12.0+)— 内置游标类型,无需TYPE声明;系统变量max_open_cursors限制可同时打开的游标数量 - 变量类型:
:=赋值、%TYPE、%ROWTYPE - 将
ROW数据类型用作存储函数的返回值(11.7+)— 函数返回结构化行,类似 Oracle 行类型 - 在例程参数和函数
RETURN子句中使用RECORD类型(13.0+) - 关联数组:
DECLARE TYPE ... TABLE OF ... INDEX BY(12.1+) - 存储例程参数可设默认值(11.8+)— 像 Oracle 的
DEFAULT子句一样,使用较少的参数调用过程 - 控制流:
FOR i IN 1..10 LOOP、GOTO、EXIT WHEN、ELSIF、CONTINUE - 异常处理:
EXCEPTION WHEN TOO_MANY_ROWS / NO_DATA_FOUND / DUP_VAL_ON_INDEX - 匿名块:
BEGIN ... END - 动态 SQL:
EXECUTE IMMEDIATE ... USING - 触发器变量:
:NEW、:OLD
Oracle 兼容函数
| 函数 | 起始支持版本 |
|---|---|
DECODE() | 10.3 |
CHR()、位置为 0 的 SUBSTR() | 10.3 |
ADD_MONTHS() | 10.6 |
TO_CHAR() | 10.6(12.0 添加了用于抑制填充的 FM 格式) |
SYS_GUID() | 10.6 |
ROWNUM | 10.6 |
TO_NUMBER() | 12.2.1 |
TO_DATE() | 12.3(原生支持;旧版本请使用 STR_TO_DATE()) |
TRUNC() | 12.2 |
NULL 处理
Oracle 将空字符串视为 NULL。sql_mode=ORACLE 不会自动启用此行为 — 必须另行添加 EMPTY_STRING_IS_NULL:
SET sql_mode = 'ORACLE,EMPTY_STRING_IS_NULL';
如果未启用 EMPTY_STRING_IS_NULL,即使在 Oracle 模式下,MariaDB 中的 '' 也不是 NULL。
其他自动行为
- 将
||用作字符串拼接运算符(忽略 NULL) MINUS作为EXCEPT的同义词(10.6+)- 预处理语句中的命名占位符(
:1、:2) SELECT UNIQUE作为SELECT DISTINCT的同义词- 支持
DUAL表
需要手动改写的内容
以下 Oracle 功能没有直接对应项,需要修改代码:
SYNONYM— 替换为视图(CREATE VIEW)或直接更新对象引用INSERT ALL/INSERT FIRST— 改写为多条INSERT语句或应用逻辑(+)外连接语法 — 从 MariaDB 12.1 起,在 Oracle 模式下原生支持。旧版本请改写为LEFT JOIN/RIGHT JOIN:-- Oracle: SELECT * FROM a, b WHERE a.id = b.id(+); -- MariaDB: SELECT * FROM a LEFT JOIN b ON a.id = b.id;START WITH ... CONNECT BY— 改写为递归 CTE:-- MariaDB equivalent: WITH RECURSIVE tree AS ( SELECT id, parent_id, name FROM categories WHERE parent_id IS NULL UNION ALL SELECT c.id, c.parent_id, c.name FROM categories c JOIN tree t ON c.parent_id = t.id ) SELECT * FROM tree;TIMESTAMP WITH TIME ZONE— 将时区偏移量存储在单独的列中,或在应用中处理- 对象类型和继承 — 没有对应实现;请重新设计为规范化表
迁移工具
- MariaDB Migration Assessment Tool — 分析 Oracle DDL 导出并评估兼容性,助你在开始迁移前了解准备情况
- Connect Storage Engine — 创建可通过 ODBC 读取实时 Oracle 数据的 MariaDB 表;适用于无需一次性完全切换的分阶段迁移
- DBeaver — 在 Oracle 和 MariaDB 之间进行架构映射、数据比较和传输
- MaxScale 查询改写 — 对无法在应用中更改的情况,实时拦截并改写不受支持的 Oracle 语法
关键注意事项
- 自动提交(提交模型相反):Oracle 没有
autocommit变量,默认使用手动提交——INSERT/UPDATE/DELETE操作在显式运行COMMIT之前不会持久化。MariaDB 则相反:默认情况下,autocommit为 ON,因此每条语句都会立即提交。要保留 Oracle 的批量操作后再提交的行为,请将工作单元包装在START TRANSACTION ... COMMIT中,或设置autocommit=0。请参阅 Oracle 的事务概念指南。 SYSDATE:在 MariaDB Oracle 模式下,SYSDATE可用,但返回DATETIME。请验证仅比较日期的操作。DUAL:在 MariaDB 中可用,但行为并不完全相同——避免使用schema.DUAL这类带架构限定的引用。- 序列:MariaDB 10.3+ 支持使用与 Oracle 兼容的语法(
MINVALUE、MAXVALUE、INCREMENT BY)运行CREATE SEQUENCE。 - 存在活动会话时执行
DROP USER(12.1+):行为与 Oracle 兼容——在 Oracle 模式下,如果用户存在活动会话,DROP USER会失败;在其他模式下,则会发出警告。 - 约 20% 需要重写:没有任何工具能够实现 100% 的 Oracle 到 MariaDB 转换。请预留时间,手动检查复杂的 PL/SQL、对象类型和不受支持的语法。
来源
- sql_mode=ORACLE — MariaDB 文档
- Oracle 到 MariaDB 的迁移 — mariadb.com
- 借助 sql_mode 和 DBeaver 简化 Oracle 到 MariaDB 的迁移 — MariaDB 博客
- 迁移评估工具 — MariaDB 博客
- Oracle 迁移场景下的 MariaDB 与 PostgreSQL 对比 — mariadb.com
- Oracle 到 MariaDB 的迁移深入解析 — Severalnines
- 使用 Connect SE 将数据从 Oracle 迁移到 MariaDB — mariadb.org
对于此处未涵盖的主题,请参阅 MariaDB 官方文档:mariadb.com/docs。