跳到正文
IntoSQL 首页

将 MariaDB 转换为 Oracle

拖入由 mariadb-dump、mysqldump、phpMyAdmin 或 Adminer 生成的转储(.sql 或 .sql.gz),即可下载包含相同表、键、索引和数据的 Oracle 脚本。Oracle 无法原样存储的每个值,以及它不支持的每个对象,都会在报告中列出。

你的文件
sqlshop_2024_mariadb.sql示例文件,6.6 kB
你的数据库

已转换 18 行,4 处修正,无拒绝

显示

shop_2024_mariadb.sql 中的表MariaDB 11.8.9-MariaDB-ubu2404 转储 · 由 mariadb-dump 写出 · utf8mb4

  • customers写作 CUSTOMERS8 行2
    8 列
    IDNUMBER(10) not null
    NAMEVARCHAR2(100 CHAR) not null
    EMAILVARCHAR2(150 CHAR) not null
    COUNTRYVARCHAR2(2 CHAR) not null
    IS_ACTIVENUMBER(1) not null
    SIGNED_UPDATE
    CREATED_ATTIMESTAMP(0) WITH TIME ZONE not null
    LAST_IPVARCHAR2(39 CHAR)
  • orders写作 ORDERS10 行2
    12 列
    IDNUMBER(10) not null
    CUSTOMER_IDNUMBER(10) not null
    STATUSVARCHAR2(9 CHAR)
    TOTALNUMBER(10,2) not null
    ITEMSNUMBER(5) not null
    ORDERED_ATTIMESTAMP(0) not null
    SHIPPED_ATTIMESTAMP(0)
    NOTEVARCHAR2(255 CHAR)
    ORDER_REFVARCHAR2(36 CHAR) not null
    INVOICENUMBER(10) not null
    DETAILSCLOB
    UPDATED_ATTIMESTAMP(0) WITH TIME ZONE not null
-- Written by IntoSQL from a MySQL dump. Load it with: NLS_LANG=.AL32UTF8 sqlplus -S -L <user>/<password>@<database> @<file>
WHENEVER SQLERROR EXIT FAILURE ROLLBACK;
SET DEFINE OFF;
SET SQLBLANKLINES ON;
SET FEEDBACK OFF;
SET SERVEROUTPUT ON;
CREATE OR REPLACE FUNCTION "INTOSQL_CLOB" (p SYS.ODCIVARCHAR2LIST) RETURN CLOB IS
    r CLOB;
BEGIN
    DBMS_LOB.CREATETEMPORARY(r, TRUE);
    FOR i IN 1 .. p.COUNT LOOP
        IF p(i) IS NOT NULL THEN
            DBMS_LOB.WRITEAPPEND(r, LENGTH2(p(i)), p(i));
        END IF;
    END LOOP;
    RETURN r;
END;
/
CREATE OR REPLACE FUNCTION "INTOSQL_BLOB" (p SYS.ODCIVARCHAR2LIST) RETURN BLOB IS
    r BLOB;
    piece RAW(2000);
BEGIN
    DBMS_LOB.CREATETEMPORARY(r, TRUE);
    FOR i IN 1 .. p.COUNT LOOP
        piece := HEXTORAW(p(i));
        DBMS_LOB.WRITEAPPEND(r, UTL_RAW.LENGTH(piece), piece);
    END LOOP;
    RETURN r;
END;
/

CREATE SEQUENCE "INVOICE_NUMBERS" START WITH 2000 INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775806;
CREATE TABLE "CUSTOMERS" (
    "ID" NUMBER(10) GENERATED BY DEFAULT ON NULL AS IDENTITY (START WITH 1001),
    "NAME" VARCHAR2(100 CHAR) NOT NULL,
    "EMAIL" VARCHAR2(150 CHAR) NOT NULL,
    "COUNTRY" VARCHAR2(2 CHAR) DEFAULT 'US' NOT NULL,
    "IS_ACTIVE" NUMBER(1) DEFAULT 1 NOT NULL,
    "SIGNED_UP" DATE,
    "CREATED_AT" TIMESTAMP(0) WITH TIME ZONE DEFAULT SYSTIMESTAMP NOT NULL,
    "LAST_IP" VARCHAR2(39 CHAR),
    CONSTRAINT "CK_CUSTOMERS_ID" CHECK ("ID" >= 0),
    CONSTRAINT "CK_CUSTOMERS_IS_ACTIVE" CHECK ("IS_ACTIVE" IN (0, 1))
);

CREATE TABLE "ORDERS" (
    "ID" NUMBER(10) GENERATED BY DEFAULT ON NULL AS IDENTITY (START WITH 5001),
    "CUSTOMER_ID" NUMBER(10) NOT NULL,
    "STATUS" VARCHAR2(9 CHAR) DEFAULT 'pending',
    "TOTAL" NUMBER(10,2) NOT NULL,
    "ITEMS" NUMBER(5) DEFAULT 1 NOT NULL,
    "ORDERED_AT" TIMESTAMP(0) NOT NULL,
    "SHIPPED_AT" TIMESTAMP(0),
    "NOTE" VARCHAR2(255 CHAR),
    "ORDER_REF" VARCHAR2(36 CHAR) NOT NULL,
    "INVOICE" NUMBER(10) DEFAULT ("INVOICE_NUMBERS".NEXTVAL) NOT NULL,
    "DETAILS" CLOB,
    "UPDATED_AT" TIMESTAMP(0) WITH TIME ZONE DEFAULT SYSTIMESTAMP NOT NULL,
    CONSTRAINT "CK_ORDERS_ID" CHECK ("ID" >= 0),
    CONSTRAINT "CK_ORDERS_CUSTOMER_ID" CHECK ("CUSTOMER_ID" >= 0),
    CONSTRAINT "CK_ORDERS_STATUS" CHECK ("STATUS" IN ('pending', 'paid', 'shipped', 'cancelled')),
    CONSTRAINT "CK_ORDERS_ITEMS" CHECK ("ITEMS" >= 0)
);

BEGIN
INSERT INTO "CUSTOMERS" (
"ID",
"NAME",
"EMAIL",
"COUNTRY",
"IS_ACTIVE",
"SIGNED_UP",
"CREATED_AT",
"LAST_IP"
) VALUES (
1,
'Ana Martínez',
'ana@example.com',
'MX',
1,
DATE '2023-01-15',
TIMESTAMP '2023-01-15 10:24:00 +00:00',
'::ffff:203.0.113.7'
);
INSERT INTO "CUSTOMERS" (
"ID",
"NAME",
"EMAIL",
"COUNTRY",
"IS_ACTIVE",
"SIGNED_UP",
"CREATED_AT",
"LAST_IP"
) VALUES (
2,
'Zoë Müller',
'zoe@example.de',
'DE',
1,
DATE '2023-02-03',
TIMESTAMP '2023-02-03 08:11:42 +00:00',
'2001:db8::1'
);
INSERT INTO "CUSTOMERS" (
"ID",
"NAME",
"EMAIL",
"COUNTRY",
"IS_ACTIVE",
"SIGNED_UP",
"CREATED_AT",
"LAST_IP"
) VALUES (
3,
'José Peña',
'jose@example.es',
'ES',
0,
DATE '2023-03-21',
TIMESTAMP '2023-03-21 19:05:17 +00:00',
NULL
);
INSERT INTO "CUSTOMERS" (
"ID",
"NAME",
"EMAIL",
"COUNTRY",
"IS_ACTIVE",
"SIGNED_UP",
"CREATED_AT",
"LAST_IP"
) VALUES (
4,
'李雷',
'lilei@example.cn',
'CN',
1,
DATE '2023-05-09',
TIMESTAMP '2023-05-09 02:40:00 +00:00',
'::ffff:198.51.100.23'
);
INSERT INTO "CUSTOMERS" (
"ID",
"NAME",
"EMAIL",
"COUNTRY",
"IS_ACTIVE",
"SIGNED_UP",
"CREATED_AT",
"LAST_IP"
) VALUES (
5,
'Noah O''Brien',
'noah@example.ie',
'IE',
1,
NULL,
TIMESTAMP '2023-06-30 14:00:00 +00:00',
NULL
);
INSERT INTO "CUSTOMERS" (
"ID",
"NAME",
"EMAIL",
"COUNTRY",
"IS_ACTIVE",
"SIGNED_UP",
"CREATED_AT",
"LAST_IP"
) VALUES (
6,
'Amélie Laurent',
'amelie@example.fr',
'FR',
1,
DATE '2024-01-02',
TIMESTAMP '2024-01-02 09:30:00 +00:00',
'2001:db8:85a3::8a2e:370:7334'
);
INSERT INTO "CUSTOMERS" (
"ID",
"NAME",
"EMAIL",
"COUNTRY",
"IS_ACTIVE",
"SIGNED_UP",
"CREATED_AT",
"LAST_IP"
) VALUES (
7,
'Ravi Patel',
'ravi@example.in',
'IN',
0,
DATE '2024-02-29',
TIMESTAMP '2024-02-29 23:59:59 +00:00',
'::ffff:192.0.2.200'
);
INSERT INTO "CUSTOMERS" (
"ID",
"NAME",
"EMAIL",
"COUNTRY",
"IS_ACTIVE",
"SIGNED_UP",
"CREATED_AT",
"LAST_IP"
) VALUES (
8,
'Sofia Rossi 🌻',
'sofia@example.it',
'IT',
1,
DATE '2024-04-12',
TIMESTAMP '2024-04-12 12:12:12 +00:00',
NULL
);
COMMIT;
END;
/
BEGIN
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE",
"ORDER_REF",
"INVOICE",
"DETAILS",
"UPDATED_AT"
) VALUES (
1,
1,
'shipped',
129.90,
3,
TIMESTAMP '2024-03-01 10:00:00',
TIMESTAMP '2024-03-02 16:30:00',
NULL,
'0b7a4c2e-3f1d-4e8a-9c55-1a2b3c4d5e6f',
1000,
'{"gift": false}',
TIMESTAMP '2024-03-02 16:30:00 +00:00'
);
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE",
"ORDER_REF",
"INVOICE",
"DETAILS",
"UPDATED_AT"
) VALUES (
2,
2,
'paid',
54.00,
1,
TIMESTAMP '2024-03-02 11:15:00',
NULL,
'Gift wrap 🎁',
'1c8b5d3f-4a2e-4f9b-8d66-2b3c4d5e6f70',
1001,
'{"gift": true, "wrap": "red"}',
TIMESTAMP '2024-03-02 11:20:00 +00:00'
);
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE",
"ORDER_REF",
"INVOICE",
"DETAILS",
"UPDATED_AT"
) VALUES (
3,
1,
'cancelled',
12.50,
1,
TIMESTAMP '2024-03-03 09:45:00',
NULL,
'Customer changed their mind',
'2d9c6e4a-5b3f-4a0c-9e77-3c4d5e6f7081',
1002,
NULL,
TIMESTAMP '2024-03-03 10:00:00 +00:00'
);
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE",
"ORDER_REF",
"INVOICE",
"DETAILS",
"UPDATED_AT"
) VALUES (
4,
4,
'shipped',
980.00,
12,
TIMESTAMP '2024-03-05 02:10:00',
TIMESTAMP '2024-03-07 08:00:00',
'请小心轻放',
'3e0d7f5b-6c4a-4b1d-8f88-4d5e6f708192',
1003,
'{"fragile": true}',
TIMESTAMP '2024-03-07 08:00:00 +00:00'
);
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE",
"ORDER_REF",
"INVOICE",
"DETAILS",
"UPDATED_AT"
) VALUES (
5,
5,
'pending',
45.75,
2,
TIMESTAMP '2024-03-08 17:20:00',
NULL,
'Leave at the door; ring twice',
'4f1e806c-7d5b-4c2e-9a99-5e6f708192a3',
1004,
NULL,
TIMESTAMP '2024-03-08 17:20:00 +00:00'
);
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE",
"ORDER_REF",
"INVOICE",
"DETAILS",
"UPDATED_AT"
) VALUES (
6,
6,
'shipped',
310.40,
5,
TIMESTAMP '2024-03-10 13:00:00',
TIMESTAMP '2024-03-11 09:00:00',
'Fragile: "glass"',
'50f2917d-8e6c-4d3f-8baa-6f708192a3b4',
1005,
'{"fragile": true}',
TIMESTAMP '2024-03-11 09:00:00 +00:00'
);
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE",
"ORDER_REF",
"INVOICE",
"DETAILS",
"UPDATED_AT"
) VALUES (
7,
8,
'paid',
76.20,
2,
TIMESTAMP '2024-03-11 18:45:00',
NULL,
NULL,
'6103a28e-9f7d-4e40-9cbb-708192a3b4c5',
1006,
NULL,
TIMESTAMP '2024-03-11 18:46:00 +00:00'
);
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE",
"ORDER_REF",
"INVOICE",
"DETAILS",
"UPDATED_AT"
) VALUES (
8,
2,
NULL,
19.99,
1,
TIMESTAMP '2024-03-12 08:00:00',
NULL,
'Status lost in an old import',
'7214b39f-a08e-4f51-8dcc-8192a3b4c5d6',
1007,
NULL,
TIMESTAMP '2024-03-12 08:00:00 +00:00'
);
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE",
"ORDER_REF",
"INVOICE",
"DETAILS",
"UPDATED_AT"
) VALUES (
9,
3,
'shipped',
64.00,
2,
TIMESTAMP '2024-03-14 15:30:00',
NULL,
'Shipping date never recorded',
'8325c4a0-b19f-4062-9edd-92a3b4c5d6e7',
1008,
'{"parcels": [1, 2]}',
TIMESTAMP '2024-03-15 10:00:00 +00:00'
);
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE",
"ORDER_REF",
"INVOICE",
"DETAILS",
"UPDATED_AT"
) VALUES (
10,
7,
'paid',
1499.00,
1,
TIMESTAMP '2024-03-15 21:05:00',
NULL,
'C:\Users\ravi\invoice.pdf',
'9436d5b1-c2a0-4173-8fee-a3b4c5d6e7f8',
1009,
NULL,
TIMESTAMP '2024-03-15 21:05:00 +00:00'
);
COMMIT;
END;
/

-- Keys, indexes, foreign keys, counters and triggers, after the rows.
ALTER TABLE "CUSTOMERS" ADD CONSTRAINT "PK_CUSTOMERS" PRIMARY KEY ("ID");
ALTER TABLE "CUSTOMERS" ADD CONSTRAINT "UQ_CUSTOMERS_UQ_EMAIL" UNIQUE ("EMAIL");
ALTER TABLE "ORDERS" ADD CONSTRAINT "PK_ORDERS" PRIMARY KEY ("ID");
CREATE INDEX "IX_ORDERS_IDX_CUSTOMER" ON "ORDERS" ("CUSTOMER_ID");
CREATE INDEX "IX_ORDERS_IDX_STATUS_DATE" ON "ORDERS" ("STATUS", "ORDERED_AT");
ALTER TABLE "ORDERS" ADD CONSTRAINT "FK_ORDERS_CUSTOMER" FOREIGN KEY ("CUSTOMER_ID") REFERENCES "CUSTOMERS" ("ID") ENABLE NOVALIDATE;
BEGIN
    EXECUTE IMMEDIATE 'ALTER TABLE "ORDERS" ENABLE VALIDATE CONSTRAINT "FK_ORDERS_CUSTOMER"';
EXCEPTION
    WHEN OTHERS THEN
        DBMS_OUTPUT.PUT_LINE('Foreign key ' || '"FK_ORDERS_CUSTOMER"' || ' has rows without a parent: it stays unvalidated, and new rows are checked.');
END;
/
DECLARE
    next_id NUMBER;
BEGIN
    SELECT NVL(MAX("ID"), 0) + 1 INTO next_id FROM "CUSTOMERS";
    IF next_id < 1001 THEN
        next_id := 1001;
    END IF;
    EXECUTE IMMEDIATE 'ALTER TABLE "CUSTOMERS" MODIFY ("ID" GENERATED BY DEFAULT ON NULL AS IDENTITY (START WITH ' || next_id || '))';
END;
/
COMMENT ON TABLE "CUSTOMERS" IS 'People who bought at least once';
DECLARE
    next_id NUMBER;
BEGIN
    SELECT NVL(MAX("ID"), 0) + 1 INTO next_id FROM "ORDERS";
    IF next_id < 5001 THEN
        next_id := 5001;
    END IF;
    EXECUTE IMMEDIATE 'ALTER TABLE "ORDERS" MODIFY ("ID" GENERATED BY DEFAULT ON NULL AS IDENTITY (START WITH ' || next_id || '))';
END;
/
CREATE OR REPLACE TRIGGER "TR_ORDERS_UPDATED_AT" BEFORE UPDATE ON "ORDERS" FOR EACH ROW
BEGIN
    IF NOT UPDATING('UPDATED_AT') THEN
        :NEW."UPDATED_AT" := SYSTIMESTAMP;
    END IF;
END;
/
DROP FUNCTION "INTOSQL_CLOB";
DROP FUNCTION "INTOSQL_BLOB";
COMMIT;
EXIT;

修正

  1. customers 的 "signed_up" 现在允许 NULL:它的部分值无法存储,已设为 NULL。
  2. customers 中 "signed_up" 的默认值 0000‑00‑00 不是 Oracle 接受的日期,因此该列没有默认值。
  3. orders 的 "status" 现在允许 NULL:它的部分值无法存储,已设为 NULL。
  4. orders 的 "updated_at" 在每次更新时变为当前时间;在 Oracle 中由触发器完成。

需要检查

  • customers 的 "signed_up" 中有 1 个值是零日期或不可能的日期,例如 0000‑00‑00,Oracle 无法存储,因此设为 NULL。 如果这些日期有意义,请在转储前于源数据中替换为真实日期。
  • orders 的 "shipped_at" 中有 1 个值是零日期或不可能的日期,例如 0000‑00‑00 00:00:00,Oracle 无法存储,因此设为 NULL。 如果这些日期有意义,请在转储前于源数据中替换为真实日期。
  • orders 的 "status" 中有 1 个空值变为 NULL;Oracle 将空字符串存储为 NULL。 在查询将此列与 ' 比较的地方,请改用 IS NULL。

文件在这台电脑上转换,永远不会上传。

其他示例:

从 MariaDB 到 Oracle 的变化

每一行是转换器应用的一条规则,以及该规则更改你的数据时在报告中写入的记录。

  • TINYINT(1), BOOL

    所有值都是 0 或 1 时为带 0 和 1 CHECK 的 NUMBER(1),否则为 NUMBER(3)

    MySQL 的 BOOL 是 TINYINT(1),可以存储 -128 到 127 之间的任何数字。23ai 之前的 Oracle 没有布尔列,因此只含 0 和 1 的列为带检查约束的 NUMBER(1);包含 2 或 -1 的列保留其数字。

    报告记录: bool_kept_numeric

  • UNSIGNED integers

    足以容纳该范围的 NUMBER,并带有不能为负数的 CHECK

    Oracle 的 NUMBER 能容纳 MySQL 各种宽度的任何整数;检查约束像 MySQL 一样阻止负值。

  • ZEROFILL and display widths

    去除

    补零只是数字的显示方式,不是数字的一部分。数字会保留;如果需要补零,请在读取时格式化。

    报告记录: zerofill_dropped

  • FLOAT(M,D)

    BINARY_FLOAT 或 BINARY_DOUBLE,不带小数位数

    Oracle 的二进制浮点数没有固定的小数位数。已存储的值按原样复制。

    报告记录: float_scale_dropped

  • DECIMAL(p,s)

    不超过 38 位时为 NUMBER(p,s);超过则为 VARCHAR2

    Oracle 的 NUMBER 可容纳 38 位数字。更宽的小数保存为文本,使每一位都保持精确。

    报告记录: decimal_as_text

  • BIT(1), BIT(n)

    NUMBER(1),不超过 63 位时为 NUMBER(19),BIT(64) 为 NUMBER(20)

    位字段存储为其各位组成的数字。

  • zero, partial or impossible dates (0000-00-00, 2024-02-30)

    NULL

    Oracle 只存储真实的日历日期。当 SQL 模式允许时,MySQL 会保留零日期和不可能的日期;它们会变为 NULL,报告会统计其数量。

    报告记录: invalid_date_null

  • NOT NULL columns with values that become NULL

    去除 NOT NULL

    值必须变为 NULL(包括空字符串)的列无法保留 NOT NULL,否则导入会中止。

    报告记录: not_null_relaxed

  • empty strings and empty binary values

    NULL

    Oracle 将空字符串存储为 NULL,因此原为 ' 的值读回时为 NULL。报告会按列统计它们。

    报告记录: empty_string_null

  • DEFAULT '0000-00-00'

    无默认值

    Oracle 无法存储的默认值会被省略,而不是替换为编造的日期。

    报告记录: zero_date_default

  • TIME

    INTERVAL DAY TO SECOND,可容纳时长

    Oracle 没有表示一天中时刻的类型,而 MySQL 的 TIME 是从 -838:59:59 到 838:59:59 的时长;interval 能容纳每个值。

  • YEAR

    NUMBER(4),并带有限定范围的 CHECK

    Oracle 没有年份类型;检查约束只允许 MySQL 允许的那些值。

  • CHAR(n), VARCHAR(n)

    VARCHAR2(n CHAR),按字符计数

    长度像 MySQL 一样按字符计数,而 MySQL 返回的 CHAR 值不带末尾空格,VARCHAR2 也按此保留。

  • VARCHAR(n) and TEXT longer than 4000 bytes

    CLOB

    VARCHAR2 默认容纳 4000 字节,因此更长的文本为 CLOB;所有值都能容纳的列保持为 VARCHAR2。

    报告记录: text_as_lob

  • DATETIME

    TIMESTAMP

    DATETIME 是不带时区的挂钟读数,与 TIMESTAMP 相同。DATE 变为 Oracle 的 DATE,它也包含一天中的时刻,保持为午夜。

  • TIMESTAMP

    使用转储声明的时区的 TIMESTAMP WITH TIME ZONE,未声明时为 UTC

    TIMESTAMP 是一个时间点。转储会声明写入时使用的时区(mysqldump 写入 UTC);没有声明时,值按 UTC 读取,报告会说明这一点。

    报告记录: timestamp_zone_assumed

  • ENUM

    VARCHAR2,并带有限定其值的 CHECK

    带检查约束的文本保留相同的值。

  • ENUM holding '' (MySQL's error value)

    NULL

    在宽松的 SQL 模式下,无效值写入 ENUM 时 MySQL 会存储空字符串。Oracle 将其存储为 NULL,检查约束允许它,报告会将其与其他空值一起统计。

  • ENUM holding values outside its list

    不加 CHECK

    列表中没有的值会使检查失败,因此不创建检查约束。

    报告记录: enum_check_skipped

  • SET

    VARCHAR2,按 MySQL 的写法('a,b')

    Oracle 没有集合类型;文本就是 MySQL 返回的文本。

  • JSON

    CLOB

    Oracle 的 JSON 函数在每个版本上都能从文本读取 JSON,而 CLOB 能保存任意大小的值。

  • NUL bytes in text

    保留

    VARCHAR2 和 CLOB 可以包含 NUL 字符,因此值按原样复制。

  • text that isn't valid in its character set

    错误的字节变为替换字符

    文本必须先按字符读取,才能写为 VARCHAR2。

    报告记录: invalid_utf8_replaced

  • text decoded twice (ñ)

    按原样复制

    转储中存储的正是这些字符,因此按原样复制;报告会指出它们。

    报告记录: mojibake_suspected

  • AUTO_INCREMENT

    GENERATED BY DEFAULT ON NULL AS IDENTITY,从已复制的 id 之后重新开始

    复制过来的 id 按原样导入,计数器从最大的 id 或转储中的 AUTO_INCREMENT 之后继续,取两者中较大者。

  • MyISAM AUTO_INCREMENT on the second column of a key

    整个表一个计数器

    MyISAM 按键的第一列分组计数;Oracle 每个表只有一个计数器。

    报告记录: myisam_group_autoincrement

  • ON UPDATE CURRENT_TIMESTAMP

    一个 BEFORE UPDATE 触发器

    当更新语句没有设置该列时,触发器将其设为当前时间。

    报告记录: on_update_trigger

  • generated columns

    虚拟列

    Oracle 只在读取时计算生成列;STORED 列变为值相同的虚拟列。

    报告记录: stored_as_virtual

  • generated columns with functions Oracle lacks

    普通列

    无法为 Oracle 写出的公式会留下一个普通列和一条报告记录。

    报告记录: generated_not_translated

  • INVISIBLE columns

    INVISIBLE 列

    Oracle 像 MySQL 一样在 SELECT * 中隐藏不可见列。

  • CHECK constraints

    已转换,内联在 CREATE TABLE 中

    内联的检查约束在每行导入时检验。

  • CHECK constraints with functions Oracle lacks

    不创建

    无法为 Oracle 写出的检查约束会被省略,并在报告中列出。

    报告记录: check_skipped

  • CHECK ... NOT ENFORCED

    不创建

    MySQL 保留这些检查约束但不执行,因此行可能违反它们。

    报告记录: check_not_enforced

  • defaults with functions Oracle lacks

    无默认值

    无法为 Oracle 写出的默认值会被省略,并在报告中列出。

    报告记录: default_not_translated

  • keys and indexes on a prefix, c(20)

    基于该列 SUBSTR 的索引

    基于列的相同开头部分的函数索引能同样区分各行,还能为 Oracle 无法整列索引的 CLOB 列建立索引。

    报告记录: prefix_unique

  • FULLTEXT indexes

    不创建

    Oracle Text 使用自己的索引类型和查询实现全文搜索。

    报告记录: fulltext_not_created

  • SPATIAL indexes

    不创建

    几何数据以 WKB 字节存储,空间索引无法读取。

    报告记录: spatial_index_not_created

  • functional indexes, and indexes on whole CLOB or BLOB columns

    不创建

    Oracle 无法为整个大对象建立索引,而且有些 MySQL 函数在 Oracle 中没有对应的函数。

    报告记录: index_skipped

  • foreign keys

    在数据之后以 ENABLE NOVALIDATE 添加,然后验证,有孤立行时给出警告

    转储以 FOREIGN_KEY_CHECKS=0 导入,而 MyISAM 从不检查,因此没有父行的行很常见。外键立即检查新行,最后再验证;有孤立行时外键保持未验证状态并给出警告,而不是中止导入。

  • ON UPDATE CASCADE, SET NULL or SET DEFAULT; ON DELETE SET DEFAULT

    去除

    Oracle 的外键只在删除时执行操作,可用 CASCADE 或 SET NULL。

    报告记录: foreign_key_action_unsupported

  • foreign keys to columns without a unique key

    不创建

    Oracle 要求被引用的列上有主键或唯一约束;InnoDB 不要求。

    报告记录: foreign_key_skipped

  • table and column names

    普通名称为大写,否则按原样书写

    Oracle 以大写存储不加引号书写的名称,因此普通名称变为大写,查询时可以省略引号;其他名称保留大小写,需要加引号。

    报告记录: identifier_renamed

  • names over 128 bytes

    缩短

    Oracle 12.2 及更高版本的名称最多 128 字节。

    报告记录: name_shortened

  • GEOMETRY, POINT, POLYGON...

    存放 WKB 的 BLOB

    WKB 是每个空间库都能读取的标准格式;SDO_GEOMETRY 需要 Oracle Spatial 的元数据。

    报告记录: geometry_as_blob

  • INET4, INET6, UUID (MariaDB)

    VARCHAR2(39), VARCHAR2(36)

    Oracle 没有这两种类型;文本就是 MariaDB 返回的文本。

  • BINARY, VARBINARY, BLOB

    不超过 2000 字节时为 RAW,否则为 BLOB

    字节仍然是字节。

  • PARTITION BY

    单个表

    行保持不变;如有需要,请在 Oracle 中重新对该表分区。

    报告记录: partitions_dropped

  • WITH SYSTEM VERSIONING (MariaDB)

    只含当前行的表

    转储只包含当前的行。

    报告记录: system_versioning_dropped

  • MyISAM, MEMORY and other engines

    普通表

    Oracle 只有一种表引擎。

    报告记录: engine_dropped

  • views, triggers, procedures, functions and events

    在报告中列出

    它们的代码用 MySQL 的语言编写,因此会列出来,由你重新创建。

    报告记录: not_converted_objects

  • dumps without CREATE TABLE

    根据值推断类型

    使用 --no-create-info 生成的转储只有行,因此类型来自值。

    报告记录: no_schema

  • geometries in a dump without CREATE TABLE

    存放 WKB 的 BLOB

    MySQL 的几何格式根据值识别,并存储为标准 WKB。

    报告记录: geometry_inferred

  • _ci collations

    精确比较

    除非另有指定,Oracle 会精确比较文本。

    报告记录: case_insensitive_collation

  • SET NAMES utf8 from utf8mb4 tables

    已转换,并给出警告

    MySQL 5.x 的 mysqldump 默认写出 3 字节 utf8,在任何转换器看到文件之前就已把每个 emoji 替换为问号。

    报告记录: dump_lost_four_byte_chars

  • dumps without SET NAMES whose text isn't UTF-8

    按 Latin-1 (Windows-1252) 读取

    MySQL 的 latin1 就是 Windows-1252,这是此类转储常用的编码。

    报告记录: charset_guessed

  • sjis, cp932, gbk, big5 dumps

    按 UTF-8 读取,并给出警告

    这些编码暂时无法解码。

    报告记录: unsupported_charset

  • UNHEX('..'), CONVERT(b'..', UNSIGNED), CONVERT(".." USING x), ST_GeomFromText('..') in rows

    求值

    有些转储工具把值写成这些函数调用;它们按其产生的值读取,地理 WKT 会转为经度在前。

  • other expressions in rows

    NULL

    不运行数据库的转换器无法对它们求值。

    报告记录: expression_value

  • DEFAULT in VALUES

    该列的默认值

    字面量默认值会复制到该行中。

    报告记录: default_keyword_filled

  • DEFAULT in VALUES without a literal default

    NULL

    没有可复制的值。

    报告记录: default_keyword_null

  • REPLACE and INSERT IGNORE

    普通行

    转储中每个键只有一行,因此行是相同的。

    报告记录: replace_as_insert

  • types IntoSQL doesn't know

    CLOB

    文本可以容纳任何值;请在导入后更改类型。

    报告记录: unknown_type

  • values that don't fit their column

    NULL

    损坏或手写的转储可能包含列无法容纳的值;这些值变为 NULL,以便其余数据能够导入。

    报告记录: invalid_value_null

  • unreadable geometries

    NULL

    不是几何数据的字节无法作为几何数据存储。

    报告记录: bad_geometry

  • dumps of several databases

    单个 schema,每个表名前加上其数据库名

    脚本导入到运行它的用户的 schema 中,因此来自不同数据库的表名前会加上各自的数据库名。

    报告记录: databases_merged

  • CREATE SEQUENCE (MariaDB)

    CREATE SEQUENCE,从它已达到的值开始

    Oracle 序列的工作方式相同。

  • VECTOR(n) (MariaDB 11.7+)

    CLOB,浮点数存为 JSON 数组

    只有 Oracle 23ai 有向量类型;JSON 数组在每个版本上都保留每个浮点数。

    报告记录: type_replaced

在 MariaDB 中在 Oracle 中报告记录
TINYINT(1), BOOL所有值都是 0 或 1 时为带 0 和 1 CHECK 的 NUMBER(1),否则为 NUMBER(3)MySQL 的 BOOL 是 TINYINT(1),可以存储 -128 到 127 之间的任何数字。23ai 之前的 Oracle 没有布尔列,因此只含 0 和 1 的列为带检查约束的 NUMBER(1);包含 2 或 -1 的列保留其数字。bool_kept_numeric
UNSIGNED integers足以容纳该范围的 NUMBER,并带有不能为负数的 CHECKOracle 的 NUMBER 能容纳 MySQL 各种宽度的任何整数;检查约束像 MySQL 一样阻止负值。
ZEROFILL and display widths去除补零只是数字的显示方式,不是数字的一部分。数字会保留;如果需要补零,请在读取时格式化。zerofill_dropped
FLOAT(M,D)BINARY_FLOAT 或 BINARY_DOUBLE,不带小数位数Oracle 的二进制浮点数没有固定的小数位数。已存储的值按原样复制。float_scale_dropped
DECIMAL(p,s)不超过 38 位时为 NUMBER(p,s);超过则为 VARCHAR2Oracle 的 NUMBER 可容纳 38 位数字。更宽的小数保存为文本,使每一位都保持精确。decimal_as_text
BIT(1), BIT(n)NUMBER(1),不超过 63 位时为 NUMBER(19),BIT(64) 为 NUMBER(20)位字段存储为其各位组成的数字。
zero, partial or impossible dates (0000-00-00, 2024-02-30)NULLOracle 只存储真实的日历日期。当 SQL 模式允许时,MySQL 会保留零日期和不可能的日期;它们会变为 NULL,报告会统计其数量。invalid_date_null
NOT NULL columns with values that become NULL去除 NOT NULL值必须变为 NULL(包括空字符串)的列无法保留 NOT NULL,否则导入会中止。not_null_relaxed
empty strings and empty binary valuesNULLOracle 将空字符串存储为 NULL,因此原为 ' 的值读回时为 NULL。报告会按列统计它们。empty_string_null
DEFAULT '0000-00-00'无默认值Oracle 无法存储的默认值会被省略,而不是替换为编造的日期。zero_date_default
TIMEINTERVAL DAY TO SECOND,可容纳时长Oracle 没有表示一天中时刻的类型,而 MySQL 的 TIME 是从 -838:59:59 到 838:59:59 的时长;interval 能容纳每个值。
YEARNUMBER(4),并带有限定范围的 CHECKOracle 没有年份类型;检查约束只允许 MySQL 允许的那些值。
CHAR(n), VARCHAR(n)VARCHAR2(n CHAR),按字符计数长度像 MySQL 一样按字符计数,而 MySQL 返回的 CHAR 值不带末尾空格,VARCHAR2 也按此保留。
VARCHAR(n) and TEXT longer than 4000 bytesCLOBVARCHAR2 默认容纳 4000 字节,因此更长的文本为 CLOB;所有值都能容纳的列保持为 VARCHAR2。text_as_lob
DATETIMETIMESTAMPDATETIME 是不带时区的挂钟读数,与 TIMESTAMP 相同。DATE 变为 Oracle 的 DATE,它也包含一天中的时刻,保持为午夜。
TIMESTAMP使用转储声明的时区的 TIMESTAMP WITH TIME ZONE,未声明时为 UTCTIMESTAMP 是一个时间点。转储会声明写入时使用的时区(mysqldump 写入 UTC);没有声明时,值按 UTC 读取,报告会说明这一点。timestamp_zone_assumed
ENUMVARCHAR2,并带有限定其值的 CHECK带检查约束的文本保留相同的值。
ENUM holding '' (MySQL's error value)NULL在宽松的 SQL 模式下,无效值写入 ENUM 时 MySQL 会存储空字符串。Oracle 将其存储为 NULL,检查约束允许它,报告会将其与其他空值一起统计。
ENUM holding values outside its list不加 CHECK列表中没有的值会使检查失败,因此不创建检查约束。enum_check_skipped
SETVARCHAR2,按 MySQL 的写法('a,b')Oracle 没有集合类型;文本就是 MySQL 返回的文本。
JSONCLOBOracle 的 JSON 函数在每个版本上都能从文本读取 JSON,而 CLOB 能保存任意大小的值。
NUL bytes in text保留VARCHAR2 和 CLOB 可以包含 NUL 字符,因此值按原样复制。
text that isn't valid in its character set错误的字节变为替换字符文本必须先按字符读取,才能写为 VARCHAR2。invalid_utf8_replaced
text decoded twice (ñ)按原样复制转储中存储的正是这些字符,因此按原样复制;报告会指出它们。mojibake_suspected
AUTO_INCREMENTGENERATED BY DEFAULT ON NULL AS IDENTITY,从已复制的 id 之后重新开始复制过来的 id 按原样导入,计数器从最大的 id 或转储中的 AUTO_INCREMENT 之后继续,取两者中较大者。
MyISAM AUTO_INCREMENT on the second column of a key整个表一个计数器MyISAM 按键的第一列分组计数;Oracle 每个表只有一个计数器。myisam_group_autoincrement
ON UPDATE CURRENT_TIMESTAMP一个 BEFORE UPDATE 触发器当更新语句没有设置该列时,触发器将其设为当前时间。on_update_trigger
generated columns虚拟列Oracle 只在读取时计算生成列;STORED 列变为值相同的虚拟列。stored_as_virtual
generated columns with functions Oracle lacks普通列无法为 Oracle 写出的公式会留下一个普通列和一条报告记录。generated_not_translated
INVISIBLE columnsINVISIBLE 列Oracle 像 MySQL 一样在 SELECT * 中隐藏不可见列。
CHECK constraints已转换,内联在 CREATE TABLE 中内联的检查约束在每行导入时检验。
CHECK constraints with functions Oracle lacks不创建无法为 Oracle 写出的检查约束会被省略,并在报告中列出。check_skipped
CHECK ... NOT ENFORCED不创建MySQL 保留这些检查约束但不执行,因此行可能违反它们。check_not_enforced
defaults with functions Oracle lacks无默认值无法为 Oracle 写出的默认值会被省略,并在报告中列出。default_not_translated
keys and indexes on a prefix, c(20)基于该列 SUBSTR 的索引基于列的相同开头部分的函数索引能同样区分各行,还能为 Oracle 无法整列索引的 CLOB 列建立索引。prefix_unique
FULLTEXT indexes不创建Oracle Text 使用自己的索引类型和查询实现全文搜索。fulltext_not_created
SPATIAL indexes不创建几何数据以 WKB 字节存储,空间索引无法读取。spatial_index_not_created
functional indexes, and indexes on whole CLOB or BLOB columns不创建Oracle 无法为整个大对象建立索引,而且有些 MySQL 函数在 Oracle 中没有对应的函数。index_skipped
foreign keys在数据之后以 ENABLE NOVALIDATE 添加,然后验证,有孤立行时给出警告转储以 FOREIGN_KEY_CHECKS=0 导入,而 MyISAM 从不检查,因此没有父行的行很常见。外键立即检查新行,最后再验证;有孤立行时外键保持未验证状态并给出警告,而不是中止导入。
ON UPDATE CASCADE, SET NULL or SET DEFAULT; ON DELETE SET DEFAULT去除Oracle 的外键只在删除时执行操作,可用 CASCADE 或 SET NULL。foreign_key_action_unsupported
foreign keys to columns without a unique key不创建Oracle 要求被引用的列上有主键或唯一约束;InnoDB 不要求。foreign_key_skipped
table and column names普通名称为大写,否则按原样书写Oracle 以大写存储不加引号书写的名称,因此普通名称变为大写,查询时可以省略引号;其他名称保留大小写,需要加引号。identifier_renamed
names over 128 bytes缩短Oracle 12.2 及更高版本的名称最多 128 字节。name_shortened
GEOMETRY, POINT, POLYGON...存放 WKB 的 BLOBWKB 是每个空间库都能读取的标准格式;SDO_GEOMETRY 需要 Oracle Spatial 的元数据。geometry_as_blob
INET4, INET6, UUID (MariaDB)VARCHAR2(39), VARCHAR2(36)Oracle 没有这两种类型;文本就是 MariaDB 返回的文本。
BINARY, VARBINARY, BLOB不超过 2000 字节时为 RAW,否则为 BLOB字节仍然是字节。
PARTITION BY单个表行保持不变;如有需要,请在 Oracle 中重新对该表分区。partitions_dropped
WITH SYSTEM VERSIONING (MariaDB)只含当前行的表转储只包含当前的行。system_versioning_dropped
MyISAM, MEMORY and other engines普通表Oracle 只有一种表引擎。engine_dropped
views, triggers, procedures, functions and events在报告中列出它们的代码用 MySQL 的语言编写,因此会列出来,由你重新创建。not_converted_objects
dumps without CREATE TABLE根据值推断类型使用 --no-create-info 生成的转储只有行,因此类型来自值。no_schema
geometries in a dump without CREATE TABLE存放 WKB 的 BLOBMySQL 的几何格式根据值识别,并存储为标准 WKB。geometry_inferred
_ci collations精确比较除非另有指定,Oracle 会精确比较文本。case_insensitive_collation
SET NAMES utf8 from utf8mb4 tables已转换,并给出警告MySQL 5.x 的 mysqldump 默认写出 3 字节 utf8,在任何转换器看到文件之前就已把每个 emoji 替换为问号。dump_lost_four_byte_chars
dumps without SET NAMES whose text isn't UTF-8按 Latin-1 (Windows-1252) 读取MySQL 的 latin1 就是 Windows-1252,这是此类转储常用的编码。charset_guessed
sjis, cp932, gbk, big5 dumps按 UTF-8 读取,并给出警告这些编码暂时无法解码。unsupported_charset
UNHEX('..'), CONVERT(b'..', UNSIGNED), CONVERT(".." USING x), ST_GeomFromText('..') in rows求值有些转储工具把值写成这些函数调用;它们按其产生的值读取,地理 WKT 会转为经度在前。
other expressions in rowsNULL不运行数据库的转换器无法对它们求值。expression_value
DEFAULT in VALUES该列的默认值字面量默认值会复制到该行中。default_keyword_filled
DEFAULT in VALUES without a literal defaultNULL没有可复制的值。default_keyword_null
REPLACE and INSERT IGNORE普通行转储中每个键只有一行,因此行是相同的。replace_as_insert
types IntoSQL doesn't knowCLOB文本可以容纳任何值;请在导入后更改类型。unknown_type
values that don't fit their columnNULL损坏或手写的转储可能包含列无法容纳的值;这些值变为 NULL,以便其余数据能够导入。invalid_value_null
unreadable geometriesNULL不是几何数据的字节无法作为几何数据存储。bad_geometry
dumps of several databases单个 schema,每个表名前加上其数据库名脚本导入到运行它的用户的 schema 中,因此来自不同数据库的表名前会加上各自的数据库名。databases_merged
CREATE SEQUENCE (MariaDB)CREATE SEQUENCE,从它已达到的值开始Oracle 序列的工作方式相同。
VECTOR(n) (MariaDB 11.7+)CLOB,浮点数存为 JSON 数组只有 Oracle 23ai 有向量类型;JSON 数组在每个版本上都保留每个浮点数。type_replaced

将脚本导入 Oracle

脚本先创建表、复制行,再添加键、索引和外键,因此导入速度快,并会在遇到第一个错误时停止。

NLS_LANG=.AL32UTF8 sqlplus -S -L user@//localhost/mydb @mariadb.oracle.sql

外键以 ENABLE NOVALIDATE 添加,因此新行会立即检查,之后再验证。如果某个旧行指向不存在的行,脚本会说明是哪个约束,并让它保持未验证状态,而不是中途失败。

对于较大的转储,请在数据库所在的机器上运行 SQL*Plus。脚本每 100 行提交一次。运行脚本的用户需要 CREATE TABLE、CREATE SEQUENCE、CREATE TRIGGER 和 CREATE PROCEDURE 权限,以及其表空间上的配额。

如何转储 MariaDB 以获得最佳结果

转换器读取转储中的内容。以下选项能让转储保留更多数据:

  • --default-character-set=utf8mb4 会保留表情符号和其他 4 字节字符,而 utf8mb3 转储在转换器读取之前就会丢失它们。
  • --hex-blob 将二进制列和 VECTOR 值写为十六进制,在系统之间复制时不会损坏。
  • --routines --triggers --events 会将这些对象加入转储,以便报告列出它们。

哪些内容不会转换

视图、触发器、存储过程、函数和事件都是用 MariaDB 自己的语言编写的。它们会按名称在报告中列出,绝不会被悄悄丢弃,以便你为 Oracle 重写。

关于 MariaDB 转 Oracle 的问题

这里没有你要的答案?请通过联系页面给我们留言,会有专人回复。 联系页面

我的转储会被上传吗?

不会。转换器在你的浏览器中运行,分块从磁盘读取转储,因此转储不会离开你的电脑。桌面应用和命令行也在你的电脑上转换。

转储可以多大?

转换器从不把整个转储放进内存:它按范围读取文件两次,边读边写出脚本。几 GB 的转储可以在浏览器、桌面应用和命令行中转换。

转储可以来自哪些 MariaDB 版本?

由 MariaDB 10.3 到 12 的 mariadb-dump 和 mysqldump 写出的转储,每个表一条 INSERT 或每行一条均可。每个测试转储都来自 MariaDB 10.11、11.4 和 11.8,并与其源数据逐个单元格比较。

UUID、INET6、JSON 和 VECTOR 列会保留吗?

UUID 和 INET 列变为 VARCHAR2,JSON 变为 CLOB,VECTOR 变为由其浮点数组成的 JSON 数组。零日期变为 NULL,空字符串也是如此,因为 Oracle 就是这样存储它们的。

AUTO_INCREMENT 值和序列会保留吗?

会。每个 AUTO_INCREMENT 列变为标识列,从最大的 id 之后继续。序列变为 Oracle 序列,从某个序列取值的默认值继续从该序列取值。

存储过程和触发器怎么处理?

它们会按名称在报告中列出,但不会转换,因为每种数据库都用自己的语言编写它们。

脚本可以导入哪些 Oracle 版本?

Oracle 19c 及更高版本。每个测试转储都能在 21c 和 23ai 上导入,如有问题会在第一个错误处停止。