Skip to content

DM 与 Oracle、MySQL 兼容性对比

你正在开发一个系统,数据库要从 Oracle 迁移到国产数据库。

最担心的是什么?

代码改动多不多?存储过程还能用吗?现有 SQL 语法要改多少?

这是所有迁移决策者最关心的问题。今天我们就来扒一扒达梦数据库的兼容性。

兼容性全景图

兼容性维度与 Oracle与 MySQL
SQL 语法★★★★★ 高度兼容★★★ 部分兼容
PL/SQL 语法★★★★★ 高度兼容不支持
系统视图★★★★☆ 核心兼容★★ 较少兼容
数据类型★★★★★ 完全兼容★★★ 部分兼容
索引类型★★★★☆ 核心兼容★★★ 部分兼容
高可用方案★★★★☆ 类似★★ 完全不同

SQL 语法兼容性

常见 DDL 语句

sql
-- Oracle 风格
CREATE TABLE T_EMPLOYEE (
    ID NUMBER(18) PRIMARY KEY,
    NAME VARCHAR2(100) NOT NULL,
    SALARY NUMBER(15,2),
    HIRE_DATE DATE DEFAULT SYSDATE
);

-- 达梦完全兼容
CREATE TABLE T_EMPLOYEE (
    ID NUMBER(18) PRIMARY KEY,
    NAME VARCHAR2(100) NOT NULL,
    SALARY NUMBER(15,2),
    HIRE_DATE DATE DEFAULT SYSDATE
);

-- MySQL 需要调整:VARCHAR2 → VARCHAR
CREATE TABLE T_EMPLOYEE (
    ID BIGINT PRIMARY KEY,
    NAME VARCHAR(100) NOT NULL,
    SALARY DECIMAL(15,2),
    HIRE_DATE DATE DEFAULT CURRENT_TIMESTAMP
);

序列(Sequence)

达梦和 Oracle 的序列语法几乎一致:

sql
-- 创建序列(Oracle 和达梦通用)
CREATE SEQUENCE SEQ_EMPLOYEE
    START WITH 1
    INCREMENT BY 1
    MINVALUE 1
    MAXVALUE 999999999999999999
    CACHE 20;

-- 使用序列
INSERT INTO T_EMPLOYEE (ID, NAME) VALUES (SEQ_EMPLOYEE.NEXTVAL, '张三');

-- 查询序列当前值
SELECT SEQ_EMPLOYEE.CURRVAL FROM DUAL;

同义词(Synonym)

sql
-- 创建公共同义词
CREATE PUBLIC SYNONYM T_EMP FOR SYSDBA.T_EMPLOYEE;

-- 创建私有同义词
CREATE SYNONYM MY_EMP FOR T_EMPLOYEE;

PL/SQL 兼容性

这是达梦最核心的优势——存储过程、函数、包可以几乎不改代码直接迁移

存储过程

sql
-- Oracle 存储过程
CREATE OR REPLACE PROCEDURE PROC_GET_EMPLOYEE(
    p_id IN NUMBER,
    p_name OUT VARCHAR2,
    p_salary OUT NUMBER
) AS
BEGIN
    SELECT NAME, SALARY INTO p_name, p_salary
    FROM T_EMPLOYEE
    WHERE ID = p_id;
END PROC_GET_EMPLOYEE;
/

-- 达梦完全兼容
CREATE OR REPLACE PROCEDURE PROC_GET_EMPLOYEE(
    p_id IN NUMBER,
    p_name OUT VARCHAR2,
    p_salary OUT NUMBER
) AS
BEGIN
    SELECT NAME, SALARY INTO p_name, p_salary
    FROM T_EMPLOYEE
    WHERE ID = p_id;
END;

函数

sql
-- 创建函数(Oracle 和达梦通用)
CREATE OR REPLACE FUNCTION FUNC_GET_BONUS(
    p_salary NUMBER
) RETURN NUMBER AS
BEGIN
    RETURN p_salary * 0.15;
END;
/

-- Java 调用
CallableStatement cs = conn.prepareCall("{? = CALL FUNC_GET_BONUS(?)}");
cs.registerOutParameter(1, Types.DECIMAL);
cs.setBigDecimal(2, salary);
cs.execute();

包(Package)

sql
-- 创建包规范
CREATE OR REPLACE PACKAGE PKG_EMPLOYEE AS
    PROCEDURE ADD_EMPLOYEE(p_name VARCHAR2, p_salary NUMBER);
    FUNCTION GET_TOTAL_SALARY RETURN NUMBER;
END PKG_EMPLOYEE;
/

-- 创建包体
CREATE OR REPLACE PACKAGE BODY PKG_EMPLOYEE AS
    PROCEDURE ADD_EMPLOYEE(p_name VARCHAR2, p_salary NUMBER) IS
    BEGIN
        INSERT INTO T_EMPLOYEE (ID, NAME, SALARY)
        VALUES (SEQ_EMPLOYEE.NEXTVAL, p_name, p_salary);
    END;
    
    FUNCTION GET_TOTAL_SALARY RETURN NUMBER IS
        v_total NUMBER;
    BEGIN
        SELECT SUM(SALARY) INTO v_total FROM T_EMPLOYEE;
        RETURN NVL(v_total, 0);
    END;
END PKG_EMPLOYEE;
/

触发器

sql
-- 创建触发器(Oracle 和达梦通用)
CREATE OR REPLACE TRIGGER TRG_EMPLOYEE_SALARY
    BEFORE INSERT OR UPDATE OF SALARY ON T_EMPLOYEE
    FOR EACH ROW
BEGIN
    IF :NEW.SALARY < 3000 THEN
        RAISE_APPLICATION_ERROR(-20001, '工资不能低于 3000');
    END IF;
END;
/

数据类型对比

Oracle达梦 DMMySQL说明
NUMBERNUMBERDECIMAL数值类型
VARCHAR2VARCHARVARCHAR变长字符串
CHARCHARCHAR定长字符串
DATEDATETIME/DATEDATETIME/DATE日期时间
CLOBCLOB/TEXTTEXT大文本
BLOBBLOBBLOB二进制大对象
TIMESTAMPTIMESTAMPTIMESTAMP时间戳
sql
-- 数据类型对比示例
CREATE TABLE T_TEST_TYPES (
    -- Oracle/NUMBER
    col_number    NUMBER(18,2),
    -- 达梦兼容 Oracle
    col_varchar2   VARCHAR2(100),
    col_date       DATE,
    col_timestamp  TIMESTAMP(6),
    col_clob       CLOB,
    col_blob       BLOB
);

系统视图对比

核心数据字典

Oracle达梦 DMMySQL用途
DBA_TABLESDBA_TABLESinformation_schema.TABLES表信息
DBA_INDEXESDBA_INDEXESinformation_schema.STATISTICS索引信息
DBA_TAB_COLUMNSDBA_TAB_COLUMNSinformation_schema.COLUMNS列信息
V$SESSIONV$SESSIONPROCESSLIST会话信息
V$SQLV$SQL不可直接查看SQL 执行信息
sql
-- 查看表结构(三个数据库通用语法)
-- Oracle 和达梦
SELECT COLUMN_NAME, DATA_TYPE, DATA_LENGTH, NULLABLE
FROM USER_TAB_COLUMNS
WHERE TABLE_NAME = 'T_EMPLOYEE'
ORDER BY COLUMN_ID;

-- MySQL
SELECT COLUMN_NAME, DATA_TYPE, CHARACTER_MAXIMUM_LENGTH, IS_NULLABLE
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = 'test' AND TABLE_NAME = 't_employee'
ORDER BY ORDINAL_POSITION;

重要差异提醒

达梦与 Oracle 的差异点

  1. 分页语法差异
sql
-- Oracle 12c 之前
SELECT * FROM T_EMPLOYEE WHERE ROWNUM <= 10;

-- Oracle 12c+
SELECT * FROM T_EMPLOYEE OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY;

-- 达梦支持两种方式
-- 方式1:ROWNUM
SELECT * FROM T_EMPLOYEE WHERE ROWNUM <= 10;

-- 方式2:LIMIT(MySQL 兼容)
SELECT * FROM T_EMPLOYEE LIMIT 10 OFFSET 0;
  1. 递归 CTE 语法
sql
-- Oracle 和达梦都支持
WITH RECURSIVE CTE AS (
    SELECT 1 AS LVL, 'ROOT' AS NAME, 0 AS PARENT_ID
    UNION ALL
    SELECT LVL + 1, A.NAME, A.PARENT_ID
    FROM CTE, T_ORG A
    WHERE A.PARENT_ID = CTE.ID
)
SELECT * FROM CTE;

达梦与 MySQL 的差异点

  1. 自增主键
sql
-- MySQL
CREATE TABLE T_USER (
    ID INT AUTO_INCREMENT PRIMARY KEY,
    NAME VARCHAR(50)
);

-- 达梦(兼容 Oracle)
CREATE TABLE T_USER (
    ID NUMBER(18) IDENTITY(1,1) PRIMARY KEY,
    NAME VARCHAR(50)
);

-- 或者使用序列(更推荐)
CREATE TABLE T_USER (
    ID NUMBER(18) PRIMARY KEY,
    NAME VARCHAR(50)
);
  1. 日期函数
sql
-- MySQL
SELECT NOW(), DATE_FORMAT(NOW(), '%Y-%m-%d');

-- 达梦(兼容 Oracle)
SELECT SYSDATE, TO_CHAR(SYSDATE, 'YYYY-MM-DD');

迁移建议

从 Oracle 迁移到达梦

步骤操作说明
1语法兼容性检查使用达梦迁移工具扫描
2数据类型转换VARCHAR2 → VARCHAR
3存储过程迁移大部分无需修改
4索引重建语法兼容,检查索引类型
5测试验证功能测试 + 性能测试

从 MySQL 迁移到达梦

步骤操作说明
1自增主键转换改用序列或 IDENTITY
2SQL 方言改造LIMIT → ROWNUM 或分页语句
3存储过程重构需要改写为达梦 PL/SQL
4引擎差异注意InnoDB → 达梦堆表

面试追问方向

  • 如果 Oracle 存储过程中使用了动态 SQL(EXECUTE IMMEDIATE),迁移到达梦要注意什么?
  • 达梦的 PL/SQL 和 Oracle 的 PL/SQL 在性能调优方面有什么差异?
  • 如何评估迁移的兼容性程度?

这些问题在实际迁移项目中经常会遇到,值得深入了解。

基于 VitePress 构建