跳到正文
IntoSQL 首页

将 SQLite 转换为 Oracle

拖入 SQLite 数据库文件(.db、.sqlite、.sqlite3)或 sqlite3 的 .dump 写出的 SQL,即可下载包含相同表、键、索引和数据的 Oracle 脚本。每列获得其值都符合的类型,Oracle 无法原样存储的每个值都会在报告中列出。

你的文件
sqlshop_2024_sqlite.sql示例文件,3.4 kB
你的数据库

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

显示

shop_2024_sqlite.sql 中的表SQLite 转储 · 由 sqlite3 写出 · utf-8

  • customers写作 CUSTOMERS8 行
    7 列
    IDNUMBER(19) not null
    NAMEVARCHAR2(4000 CHAR) not null
    EMAILVARCHAR2(750 CHAR) not null
    COUNTRYVARCHAR2(4000 CHAR) not null
    IS_ACTIVENUMBER(1) not null
    SIGNED_UPDATE
    CREATED_ATTIMESTAMP(0) not null
  • orders写作 ORDERS10 行
    9 列
    IDNUMBER(19) not null
    CUSTOMER_IDNUMBER(19) not null
    STATUSVARCHAR2(750 CHAR) not null
    TOTALNUMBER(10,2) not null
    ITEMSNUMBER(10) not null
    ORDERED_ATTIMESTAMP(0) not null
    SHIPPED_ATTIMESTAMP(0)
    NOTEVARCHAR2(4000 CHAR)
    RECEIPTBLOB

未转换

  • 视图 order_totals
  • 触发器 orders_shipped

每种数据库都用自己的语言编写这些对象。请为 Oracle 重写它们。

-- Written by IntoSQL from a SQLite 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 TABLE "CUSTOMERS" (
    "ID" NUMBER(19) GENERATED BY DEFAULT ON NULL AS IDENTITY (START WITH 9),
    "NAME" VARCHAR2(4000 CHAR) NOT NULL,
    "EMAIL" VARCHAR2(750 CHAR) NOT NULL,
    "COUNTRY" VARCHAR2(4000 CHAR) DEFAULT 'US' NOT NULL,
    "IS_ACTIVE" NUMBER(1) DEFAULT 1 NOT NULL,
    "SIGNED_UP" DATE,
    "CREATED_AT" TIMESTAMP(0) DEFAULT LOCALTIMESTAMP NOT NULL,
    CONSTRAINT "CK_CUSTOMERS_IS_ACTIVE" CHECK ("IS_ACTIVE" IN (0, 1)),
    CONSTRAINT "CHECK1" CHECK (LENGTH("COUNTRY") = 2)
);

CREATE TABLE "ORDERS" (
    "ID" NUMBER(19) GENERATED BY DEFAULT ON NULL AS IDENTITY (START WITH 11),
    "CUSTOMER_ID" NUMBER(19) NOT NULL,
    "STATUS" VARCHAR2(750 CHAR) DEFAULT 'pending' NOT NULL,
    "TOTAL" NUMBER(10,2) NOT NULL,
    "ITEMS" NUMBER(10) DEFAULT 1 NOT NULL,
    "ORDERED_AT" TIMESTAMP(0) NOT NULL,
    "SHIPPED_AT" TIMESTAMP(0),
    "NOTE" VARCHAR2(4000 CHAR),
    "RECEIPT" BLOB,
    CONSTRAINT "CHECK1_2" CHECK ("STATUS" IN ('pending', 'paid', 'shipped', 'cancelled'))
);

BEGIN
INSERT INTO "CUSTOMERS" (
"ID",
"NAME",
"EMAIL",
"COUNTRY",
"IS_ACTIVE",
"SIGNED_UP",
"CREATED_AT"
) VALUES (
1,
'Ana Martínez',
'ana@example.com',
'MX',
1,
DATE '2023-01-15',
TIMESTAMP '2023-01-15 10:24:00'
);
COMMIT;
END;
/
BEGIN
INSERT INTO "CUSTOMERS" (
"ID",
"NAME",
"EMAIL",
"COUNTRY",
"IS_ACTIVE",
"SIGNED_UP",
"CREATED_AT"
) VALUES (
2,
'Zoë Müller',
'zoe@example.de',
'DE',
1,
DATE '2023-02-03',
TIMESTAMP '2023-02-03 08:11:42'
);
COMMIT;
END;
/
BEGIN
INSERT INTO "CUSTOMERS" (
"ID",
"NAME",
"EMAIL",
"COUNTRY",
"IS_ACTIVE",
"SIGNED_UP",
"CREATED_AT"
) VALUES (
3,
'José Peña',
'jose@example.es',
'ES',
0,
DATE '2023-03-21',
TIMESTAMP '2023-03-21 19:05:17'
);
COMMIT;
END;
/
BEGIN
INSERT INTO "CUSTOMERS" (
"ID",
"NAME",
"EMAIL",
"COUNTRY",
"IS_ACTIVE",
"SIGNED_UP",
"CREATED_AT"
) VALUES (
4,
'李雷',
'lilei@example.cn',
'CN',
1,
DATE '2023-05-09',
TIMESTAMP '2023-05-09 02:40:00'
);
COMMIT;
END;
/
BEGIN
INSERT INTO "CUSTOMERS" (
"ID",
"NAME",
"EMAIL",
"COUNTRY",
"IS_ACTIVE",
"SIGNED_UP",
"CREATED_AT"
) VALUES (
5,
'Noah O''Brien',
'noah@example.ie',
'IE',
1,
NULL,
TIMESTAMP '2023-06-30 14:00:00'
);
COMMIT;
END;
/
BEGIN
INSERT INTO "CUSTOMERS" (
"ID",
"NAME",
"EMAIL",
"COUNTRY",
"IS_ACTIVE",
"SIGNED_UP",
"CREATED_AT"
) VALUES (
6,
'Amélie Laurent',
'amelie@example.fr',
'FR',
1,
DATE '2024-01-02',
TIMESTAMP '2024-01-02 09:30:00'
);
COMMIT;
END;
/
BEGIN
INSERT INTO "CUSTOMERS" (
"ID",
"NAME",
"EMAIL",
"COUNTRY",
"IS_ACTIVE",
"SIGNED_UP",
"CREATED_AT"
) VALUES (
7,
'Ravi Patel',
'ravi@example.in',
'IN',
0,
DATE '2024-02-29',
TIMESTAMP '2024-02-29 23:59:59'
);
COMMIT;
END;
/
BEGIN
INSERT INTO "CUSTOMERS" (
"ID",
"NAME",
"EMAIL",
"COUNTRY",
"IS_ACTIVE",
"SIGNED_UP",
"CREATED_AT"
) VALUES (
8,
'Sofia Rossi 🌻',
'sofia@example.it',
'IT',
1,
DATE '2024-04-12',
TIMESTAMP '2024-04-12 12:12:12'
);
COMMIT;
END;
/
BEGIN
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE",
"RECEIPT"
) VALUES (
1,
1,
'shipped',
129.9,
3,
TIMESTAMP '2024-03-01 10:00:00',
TIMESTAMP '2024-03-02 16:30:00',
NULL,
NULL
);
COMMIT;
END;
/
BEGIN
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE",
"RECEIPT"
) VALUES (
2,
2,
'paid',
54,
1,
TIMESTAMP '2024-03-02 11:15:00',
NULL,
'Gift wrap 🎁',
NULL
);
COMMIT;
END;
/
BEGIN
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE",
"RECEIPT"
) VALUES (
3,
1,
'cancelled',
12.5,
1,
TIMESTAMP '2024-03-03 09:45:00',
NULL,
'Customer changed their mind',
NULL
);
COMMIT;
END;
/
BEGIN
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE",
"RECEIPT"
) VALUES (
4,
4,
'shipped',
980,
12,
TIMESTAMP '2024-03-05 02:10:00',
TIMESTAMP '2024-03-07 08:00:00',
'请小心轻放',
HEXTORAW('25504446')
);
COMMIT;
END;
/
BEGIN
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE",
"RECEIPT"
) VALUES (
5,
5,
'pending',
45.75,
2,
TIMESTAMP '2024-03-08 17:20:00',
NULL,
'Leave at the door;'
|| CHR(10)
|| 'ring twice',
NULL
);
COMMIT;
END;
/
BEGIN
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE",
"RECEIPT"
) VALUES (
6,
6,
'shipped',
310.4,
5,
TIMESTAMP '2024-03-10 13:00:00',
TIMESTAMP '2024-03-11 09:00:00',
'Fragile: "glass"',
NULL
);
COMMIT;
END;
/
BEGIN
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE",
"RECEIPT"
) VALUES (
7,
8,
'paid',
76.2,
2,
TIMESTAMP '2024-03-11 18:45:00',
NULL,
NULL,
NULL
);
COMMIT;
END;
/
BEGIN
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE",
"RECEIPT"
) VALUES (
8,
2,
'paid',
19.99,
1,
TIMESTAMP '2024-03-12 08:00:00',
NULL,
'Paid at the counter',
NULL
);
COMMIT;
END;
/
BEGIN
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE",
"RECEIPT"
) VALUES (
9,
3,
'shipped',
64,
2,
TIMESTAMP '2024-03-14 15:30:00',
NULL,
'Shipping date never recorded',
NULL
);
COMMIT;
END;
/
BEGIN
INSERT INTO "ORDERS" (
"ID",
"CUSTOMER_ID",
"STATUS",
"TOTAL",
"ITEMS",
"ORDERED_AT",
"SHIPPED_AT",
"NOTE",
"RECEIPT"
) VALUES (
10,
7,
'paid',
1499,
1,
TIMESTAMP '2024-03-15 21:05:00',
NULL,
'C:\Users\ravi\invoice.pdf',
NULL
);
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_EMAIL" UNIQUE ("EMAIL");
ALTER TABLE "ORDERS" ADD CONSTRAINT "PK_ORDERS" PRIMARY KEY ("ID");
CREATE INDEX "IX_ORDERS_IDX_ORDERS_CUSTOMER" ON "ORDERS" ("CUSTOMER_ID");
CREATE INDEX "IX_ORDERS_IDX_ORDERS_STATUS_DATE" ON "ORDERS" ("STATUS", "ORDERED_AT");
ALTER TABLE "ORDERS" ADD CONSTRAINT "FK_ORDERS" FOREIGN KEY ("CUSTOMER_ID") REFERENCES "CUSTOMERS" ("ID") ON DELETE CASCADE ENABLE NOVALIDATE;
BEGIN
    EXECUTE IMMEDIATE 'ALTER TABLE "ORDERS" ENABLE VALIDATE CONSTRAINT "FK_ORDERS"';
EXCEPTION
    WHEN OTHERS THEN
        DBMS_OUTPUT.PUT_LINE('Foreign key ' || '"FK_ORDERS"' || ' 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 < 9 THEN
        next_id := 9;
    END IF;
    EXECUTE IMMEDIATE 'ALTER TABLE "CUSTOMERS" MODIFY ("ID" GENERATED BY DEFAULT ON NULL AS IDENTITY (START WITH ' || next_id || '))';
END;
/
DECLARE
    next_id NUMBER;
BEGIN
    SELECT NVL(MAX("ID"), 0) + 1 INTO next_id FROM "ORDERS";
    IF next_id < 11 THEN
        next_id := 11;
    END IF;
    EXECUTE IMMEDIATE 'ALTER TABLE "ORDERS" MODIFY ("ID" GENERATED BY DEFAULT ON NULL AS IDENTITY (START WITH ' || next_id || '))';
END;
/
DROP FUNCTION "INTOSQL_CLOB";
DROP FUNCTION "INTOSQL_BLOB";
COMMIT;
EXIT;

修正

没有需要修正的内容。

需要检查

  • 源数据中有 1 个文本列比较时不区分大小写;Oracle 精确比较,因此唯一键和搜索的行为可能不同。 在需要的列上使用不区分大小写的排序规则或列类型。
  • 2 个视图、触发器、例程或事件未转换;报告逐一列出了它们。 请在 Oracle 中手动重新创建它们。

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

其他示例:

从 SQLite 到 Oracle 的变化

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

  • declared types (any text: VARCHAR(255), DATETIME, BOOLEAN, JSON or none)

    列中每个值都符合的类型

    SQLite 允许任何列存放任何值,因此声明的类型只是提示:每列获得其所有值都符合的类型,包含混合值的列保留其文本。

  • BOOLEAN, BOOL

    NUMBER(1),并带有限定 0 和 1 的 CHECK

    当每个值都是 0 或 1 时;同时包含其他值的列会将它们保留为数字或文本。

  • TEXT, VARCHAR(n), CHAR(n), CLOB

    每个值都符合时为 VARCHAR2(n CHAR),否则为 CLOB

    SQLite 从不强制长度;只有当每个值都符合时,才保留声明的长度。

  • TEXT and VARCHAR(n) longer than 4000 bytes

    每个值都不超过 4000 字节(键中为 750 个字符)时为 VARCHAR2(4000 CHAR),否则为 CLOB

    VARCHAR2 默认可容纳 4000 字节,而 Oracle 无法为 CLOB 建立索引,因此值符合时文本保持 VARCHAR2,不符合时为 CLOB。

    报告记录: text_as_lob

  • DECIMAL(p,s), NUMERIC

    每个值都符合时为精确类型,否则为 double

    SQLite 将这些值存储为整数或 double;只有当值符合时,才保留声明的精度。

  • BLOB, and columns holding bytes

    BLOB

    字节保持为字节;同一列中的文本和数字按其字节保留。

  • DATE, DATETIME, TIMESTAMP holding ISO 8601 text

    根据值的内容为 DATE 或 TIMESTAMP(p)

    当每个值都是日期或日期加时间时;纪元时间数字和其他文本保持原值不变。

  • JSON holding JSON text

    CLOB

    当每个值都是 JSON 对象或数组时。

  • Infinity in REAL columns

    保留为 BINARY_DOUBLE_INFINITY

    Oracle 的 double 可以保存无穷大。

  • INTEGER PRIMARY KEY, AUTOINCREMENT

    GENERATED BY DEFAULT ON NULL AS IDENTITY,从最大的 id 或 sqlite_sequence 的值之后继续

    只有声明恰好为 INTEGER PRIMARY KEY 的列才是 rowid;每一行都保留其 id,计数器从曾经分配过的最大 id 之后开始。

  • COLLATE NOCASE

    精确比较

    除非另有指定,Oracle 会精确比较文本;NOCASE 列不再忽略大小写。

    报告记录: case_insensitive_collation

  • generated columns

    虚拟列

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

    报告记录: stored_as_virtual

  • generated columns with functions IntoSQL doesn't write, and the || operator

    普通列

    无法写出的公式会留下一个普通列和一条报告记录;转储中没有生成列的值。

    报告记录: generated_not_translated

  • defaults with functions IntoSQL doesn't write (strftime, randomblob)

    无默认值

    无法写出的默认值会被省略,并在报告中列出;CURRENT_TIMESTAMP 和 datetime('now') 会被写出。

    报告记录: default_not_translated

  • CHECK constraints

    已转换

    检查约束相同,在导入每一行时检查。

  • CHECK constraints with functions IntoSQL doesn't write

    不创建

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

    报告记录: check_skipped

  • partial and expression indexes

    不创建

    IntoSQL 在整列上写出索引;其他索引请在导入后重新创建。

    报告记录: index_skipped

  • foreign keys (unenforced unless PRAGMA foreign_keys=ON)

    在数据之后以 ENABLE NOVALIDATE 添加,然后验证

    SQLite 只在被要求时才检查外键,因此孤立行很常见:它们会被导入,外键保持未检查状态并给出警告,而不是中止导入。

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

    去除

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

    报告记录: foreign_key_action_unsupported

  • empty strings and empty blobs

    NULL

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

    报告记录: empty_string_null

  • foreign keys to tables the dump doesn't create

    不创建

    外键无法引用不存在的表。

    报告记录: foreign_key_skipped

  • primary keys holding NULL, and NOT NULL columns with values that become NULL

    去除 NOT NULL

    SQLite 允许 rowid 以外的主键包含 NULL;该列在目标数据库中不能为 NOT NULL,否则导入会中止。

    报告记录: not_null_relaxed

  • views, triggers and virtual tables (FTS, R*Tree)

    在报告中列出

    它们的代码用 SQLite 自己的语言编写,虚拟表需要 SQLite 的模块;它们会被列出,由你重新创建。它们的内部表不会被复制。

    报告记录: not_converted_objects

  • dumps without CREATE TABLE (--data-only)

    根据值推断类型

    只含数据的转储只有行,因此类型来自值,列按编号命名。

    报告记录: no_schema

  • expressions in INSERT values other than the text functions dumps write

    NULL

    unistr()、replace()、char() 和 || 会按 shell 写出的形式求值;其他表达式在没有数据库时无法求值。

    报告记录: expression_value

  • NUL characters in text

    保留

    sqlite3 shell 和 Python 在转储时会在 NUL 字符处截断文本,因此转储中只有它之前的内容。

  • text that isn't valid UTF-8

    错误的字节变为替换字符

    转储会将这类文本写为原始字节;脚本是 UTF-8。

    报告记录: invalid_utf8_replaced

  • text decoded twice (ñ)

    按原样复制

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

    报告记录: mojibake_suspected

在 SQLite 中在 Oracle 中报告记录
declared types (any text: VARCHAR(255), DATETIME, BOOLEAN, JSON or none)列中每个值都符合的类型SQLite 允许任何列存放任何值,因此声明的类型只是提示:每列获得其所有值都符合的类型,包含混合值的列保留其文本。
BOOLEAN, BOOLNUMBER(1),并带有限定 0 和 1 的 CHECK当每个值都是 0 或 1 时;同时包含其他值的列会将它们保留为数字或文本。
TEXT, VARCHAR(n), CHAR(n), CLOB每个值都符合时为 VARCHAR2(n CHAR),否则为 CLOBSQLite 从不强制长度;只有当每个值都符合时,才保留声明的长度。
TEXT and VARCHAR(n) longer than 4000 bytes每个值都不超过 4000 字节(键中为 750 个字符)时为 VARCHAR2(4000 CHAR),否则为 CLOBVARCHAR2 默认可容纳 4000 字节,而 Oracle 无法为 CLOB 建立索引,因此值符合时文本保持 VARCHAR2,不符合时为 CLOB。text_as_lob
DECIMAL(p,s), NUMERIC每个值都符合时为精确类型,否则为 doubleSQLite 将这些值存储为整数或 double;只有当值符合时,才保留声明的精度。
BLOB, and columns holding bytesBLOB字节保持为字节;同一列中的文本和数字按其字节保留。
DATE, DATETIME, TIMESTAMP holding ISO 8601 text根据值的内容为 DATE 或 TIMESTAMP(p)当每个值都是日期或日期加时间时;纪元时间数字和其他文本保持原值不变。
JSON holding JSON textCLOB当每个值都是 JSON 对象或数组时。
Infinity in REAL columns保留为 BINARY_DOUBLE_INFINITYOracle 的 double 可以保存无穷大。
INTEGER PRIMARY KEY, AUTOINCREMENTGENERATED BY DEFAULT ON NULL AS IDENTITY,从最大的 id 或 sqlite_sequence 的值之后继续只有声明恰好为 INTEGER PRIMARY KEY 的列才是 rowid;每一行都保留其 id,计数器从曾经分配过的最大 id 之后开始。
COLLATE NOCASE精确比较除非另有指定,Oracle 会精确比较文本;NOCASE 列不再忽略大小写。case_insensitive_collation
generated columns虚拟列Oracle 只在读取时计算生成列;STORED 列变为值相同的虚拟列。stored_as_virtual
generated columns with functions IntoSQL doesn't write, and the || operator普通列无法写出的公式会留下一个普通列和一条报告记录;转储中没有生成列的值。generated_not_translated
defaults with functions IntoSQL doesn't write (strftime, randomblob)无默认值无法写出的默认值会被省略,并在报告中列出;CURRENT_TIMESTAMP 和 datetime('now') 会被写出。default_not_translated
CHECK constraints已转换检查约束相同,在导入每一行时检查。
CHECK constraints with functions IntoSQL doesn't write不创建无法写出的检查约束会被省略,并在报告中列出。check_skipped
partial and expression indexes不创建IntoSQL 在整列上写出索引;其他索引请在导入后重新创建。index_skipped
foreign keys (unenforced unless PRAGMA foreign_keys=ON)在数据之后以 ENABLE NOVALIDATE 添加,然后验证SQLite 只在被要求时才检查外键,因此孤立行很常见:它们会被导入,外键保持未检查状态并给出警告,而不是中止导入。
ON UPDATE CASCADE, SET NULL or SET DEFAULT; ON DELETE SET DEFAULT去除Oracle 的外键只在删除时执行操作,可用 CASCADE 或 SET NULL。foreign_key_action_unsupported
empty strings and empty blobsNULLOracle 将空字符串存储为 NULL,因此原为 ' 的值读回时为 NULL。报告会按列统计它们。empty_string_null
foreign keys to tables the dump doesn't create不创建外键无法引用不存在的表。foreign_key_skipped
primary keys holding NULL, and NOT NULL columns with values that become NULL去除 NOT NULLSQLite 允许 rowid 以外的主键包含 NULL;该列在目标数据库中不能为 NOT NULL,否则导入会中止。not_null_relaxed
views, triggers and virtual tables (FTS, R*Tree)在报告中列出它们的代码用 SQLite 自己的语言编写,虚拟表需要 SQLite 的模块;它们会被列出,由你重新创建。它们的内部表不会被复制。not_converted_objects
dumps without CREATE TABLE (--data-only)根据值推断类型只含数据的转储只有行,因此类型来自值,列按编号命名。no_schema
expressions in INSERT values other than the text functions dumps writeNULLunistr()、replace()、char() 和 || 会按 shell 写出的形式求值;其他表达式在没有数据库时无法求值。expression_value
NUL characters in text保留sqlite3 shell 和 Python 在转储时会在 NUL 字符处截断文本,因此转储中只有它之前的内容。
text that isn't valid UTF-8错误的字节变为替换字符转储会将这类文本写为原始字节;脚本是 UTF-8。invalid_utf8_replaced
text decoded twice (ñ)按原样复制转储中存储的正是这些字符,因此按原样复制;报告会指出它们。mojibake_suspected

将脚本导入 Oracle

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

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

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

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

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

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

  • 数据库文件本身会原样转换,在浏览器中逐页读取。请先关闭正在写入它的应用,以免有更改遗留在其 -wal 文件中。
  • sqlite3 app.db .dump > app.sql 会将同一个数据库写为 SQL,它同样可以转换,格式为 .sql 或 .sql.gz。
  • DB Browser for SQLite 通过 File、Export、Database to SQL file 也能写出该 SQL,Python 的 iterdump() 也一样。

哪些内容不会转换

视图和触发器是用 SQLite 自己的语言编写的。它们会按名称在报告中列出,绝不会被悄悄丢弃,以便你为 Oracle 重写。FTS5 和 R*Tree 等虚拟表也会列出,其影子表则不包含在内。

关于 SQLite 转 Oracle 的问题

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

我的转储会被上传吗?

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

转储可以多大?

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

可以直接转换 .db 文件吗?

可以。拖入 .db、.sqlite 或 .sqlite3 文件:它在浏览器中读取,绝不会上传。含 NUL 字符的文本和 double 值的每一位数字都会按存储原样保留,而 .dump 可能会将其截断。

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

SQLite 3.0 及更高版本的 UTF-8 或 UTF-16 数据库文件,以及由 sqlite3 shell 的 .dump 或 Python 的 iterdump() 写出的转储。每个测试文件和转储都来自 sqlite3 3.31 到 3.54,包括 STRICT 和 WITHOUT ROWID 表,并与其源数据逐个单元格比较。

SQLite 不强制类型时,列类型如何确定?

声明的类型只是提示,由值决定:当每个值都符合时,列会变为 NUMBER、BINARY_DOUBLE、DATE、TIMESTAMP、BLOB、VARCHAR2 或 CLOB。空字符串会按 Oracle 的存储方式变为 NULL,报告会统计其数量。

INTEGER PRIMARY KEY 和 AUTOINCREMENT 会保留吗?

会。INTEGER PRIMARY KEY 变为标识列,每一行都保留其 id,下一个 id 从最大的 id 之后继续;使用 AUTOINCREMENT 时则从 sqlite_sequence 的值之后继续。

视图和触发器怎么处理?

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

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

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