跳到正文
IntoSQL 首页

将 SQLite 数据库迁移到更旧或更新的 SQLite

拖入 SQLite 数据库文件 (.db、.sqlite、.sqlite3) 或 sqlite3 的 .dump 写出的 SQL,即可下载可在 SQLite 3.31 及以上导入的脚本。每列保留声明的类型和排序规则,并去掉 STRICT 以便旧版本接受这些表,每项更改都会列在报告中。

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

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

显示

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

  • customers8 行
    7 列
    idINTEGER not null
    nameTEXT not null
    emailTEXT not null
    countryTEXT not null
    is_activeINTEGER not null
    signed_upDATE
    created_atDATETIME not null
  • orders10 行
    9 列
    idINTEGER not null
    customer_idINTEGER not null
    statusTEXT not null
    totalDECIMAL(10,2) not null
    itemsINTEGER not null
    ordered_atDATETIME not null
    shipped_atDATETIME
    noteTEXT
    receiptBLOB

未转换

  • 视图 order_totals
  • 触发器 orders_shipped

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

-- Written by IntoSQL from a SQLite dump. Load it with: sqlite3 -bail <database> < <file>
PRAGMA foreign_keys = OFF;
PRAGMA synchronous = OFF;
BEGIN;

CREATE TABLE customers (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL,
    email TEXT COLLATE NOCASE NOT NULL,
    country TEXT DEFAULT 'US' NOT NULL,
    is_active INTEGER DEFAULT 1 NOT NULL,
    signed_up DATE,
    created_at DATETIME DEFAULT (datetime('now', 'localtime')) NOT NULL,
    CONSTRAINT customers_is_active_check CHECK (is_active IN (0, 1)),
    CONSTRAINT customers_check1 CHECK (length(country) = 2)
);

CREATE TABLE orders (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    customer_id INTEGER NOT NULL,
    status TEXT DEFAULT 'pending' NOT NULL,
    total DECIMAL(10,2) NOT NULL,
    items INTEGER DEFAULT 1 NOT NULL,
    ordered_at DATETIME NOT NULL,
    shipped_at DATETIME,
    note TEXT,
    receipt BLOB,
    CONSTRAINT orders_check1 CHECK (status IN ('pending', 'paid', 'shipped', 'cancelled')),
    CONSTRAINT orders_fkey FOREIGN KEY (customer_id) REFERENCES customers (id) ON DELETE CASCADE
);

INSERT INTO customers (id, name, email, country, is_active, signed_up, created_at) VALUES
(1,'Ana Martínez','ana@example.com','MX',1,'2023-01-15','2023-01-15 10:24:00');
INSERT INTO customers (id, name, email, country, is_active, signed_up, created_at) VALUES
(2,'Zoë Müller','zoe@example.de','DE',1,'2023-02-03','2023-02-03 08:11:42');
INSERT INTO customers (id, name, email, country, is_active, signed_up, created_at) VALUES
(3,'José Peña','jose@example.es','ES',0,'2023-03-21','2023-03-21 19:05:17');
INSERT INTO customers (id, name, email, country, is_active, signed_up, created_at) VALUES
(4,'李雷','lilei@example.cn','CN',1,'2023-05-09','2023-05-09 02:40:00');
INSERT INTO customers (id, name, email, country, is_active, signed_up, created_at) VALUES
(5,'Noah O''Brien','noah@example.ie','IE',1,NULL,'2023-06-30 14:00:00');
INSERT INTO customers (id, name, email, country, is_active, signed_up, created_at) VALUES
(6,'Amélie Laurent','amelie@example.fr','FR',1,'2024-01-02','2024-01-02 09:30:00');
INSERT INTO customers (id, name, email, country, is_active, signed_up, created_at) VALUES
(7,'Ravi Patel','ravi@example.in','IN',0,'2024-02-29','2024-02-29 23:59:59');
INSERT INTO customers (id, name, email, country, is_active, signed_up, created_at) VALUES
(8,'Sofia Rossi 🌻','sofia@example.it','IT',1,'2024-04-12','2024-04-12 12:12:12');
INSERT INTO orders (id, customer_id, status, total, items, ordered_at, shipped_at, note, receipt) VALUES
(1,1,'shipped',129.9,3,'2024-03-01 10:00:00','2024-03-02 16:30:00',NULL,NULL);
INSERT INTO orders (id, customer_id, status, total, items, ordered_at, shipped_at, note, receipt) VALUES
(2,2,'paid',54,1,'2024-03-02 11:15:00',NULL,'Gift wrap 🎁',NULL);
INSERT INTO orders (id, customer_id, status, total, items, ordered_at, shipped_at, note, receipt) VALUES
(3,1,'cancelled',12.5,1,'2024-03-03 09:45:00',NULL,'Customer changed their mind',NULL);
INSERT INTO orders (id, customer_id, status, total, items, ordered_at, shipped_at, note, receipt) VALUES
(4,4,'shipped',980,12,'2024-03-05 02:10:00','2024-03-07 08:00:00','请小心轻放',X'25504446');
INSERT INTO orders (id, customer_id, status, total, items, ordered_at, shipped_at, note, receipt) VALUES
(5,5,'pending',45.75,2,'2024-03-08 17:20:00',NULL,'Leave at the door;
ring twice',NULL);
INSERT INTO orders (id, customer_id, status, total, items, ordered_at, shipped_at, note, receipt) VALUES
(6,6,'shipped',310.4,5,'2024-03-10 13:00:00','2024-03-11 09:00:00','Fragile: "glass"',NULL);
INSERT INTO orders (id, customer_id, status, total, items, ordered_at, shipped_at, note, receipt) VALUES
(7,8,'paid',76.2,2,'2024-03-11 18:45:00',NULL,NULL,NULL);
INSERT INTO orders (id, customer_id, status, total, items, ordered_at, shipped_at, note, receipt) VALUES
(8,2,'paid',19.99,1,'2024-03-12 08:00:00',NULL,'Paid at the counter',NULL);
INSERT INTO orders (id, customer_id, status, total, items, ordered_at, shipped_at, note, receipt) VALUES
(9,3,'shipped',64,2,'2024-03-14 15:30:00',NULL,'Shipping date never recorded',NULL);
INSERT INTO orders (id, customer_id, status, total, items, ordered_at, shipped_at, note, receipt) VALUES
(10,7,'paid',1499,1,'2024-03-15 21:05:00',NULL,'C:\Users\ravi\invoice.pdf',NULL);

-- Indexes, triggers and counters, after the rows so each index builds once.
CREATE UNIQUE INDEX customers_email ON customers (email);
CREATE INDEX orders_idx_orders_customer ON orders (customer_id);
CREATE INDEX orders_idx_orders_status_date ON orders (status, ordered_at);
UPDATE sqlite_sequence SET seq = max(seq, 8) WHERE name = 'customers';
INSERT INTO sqlite_sequence (name, seq) SELECT 'customers', 8 WHERE NOT EXISTS (SELECT 1 FROM sqlite_sequence WHERE name = 'customers');
UPDATE sqlite_sequence SET seq = max(seq, 10) WHERE name = 'orders';
INSERT INTO sqlite_sequence (name, seq) SELECT 'orders', 10 WHERE NOT EXISTS (SELECT 1 FROM sqlite_sequence WHERE name = 'orders');
COMMIT;

-- Rows whose foreign key finds no parent, if any: the load doesn't stop for them.
PRAGMA foreign_key_check;

修正

没有需要修正的内容。

需要检查

  • 2 个视图、触发器、例程或事件未转换;报告逐一列出了它们。 请在 SQLite 中手动重新创建它们。

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

其他示例:

文件与脚本之间的变化

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

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

    列中每个值都符合的类型

    声明的类型相同,因此值按存储时的样子导入。

  • BOOLEAN, BOOL

    INTEGER 0 或 1,并带有 CHECK

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

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

    声明的类型,按原样保留

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

  • DECIMAL(p,s), NUMERIC

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

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

  • BLOB, and columns holding bytes

    BLOB

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

  • DATE, DATETIME, TIMESTAMP holding ISO 8601 text

    DATE、DATETIME,以 ISO 8601 文本存储

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

  • JSON holding JSON text

    TEXT

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

  • Infinity in REAL columns

    9e999,即 SQLite 自己的无穷大

    SQLite 将无穷大写为溢出的字面量,导入后会恢复为无穷大。

  • INTEGER PRIMARY KEY, AUTOINCREMENT

    INTEGER PRIMARY KEY,保留 AUTOINCREMENT

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

  • COLLATE NOCASE

    COLLATE NOCASE

    相同的排序规则。

  • generated columns

    生成列,与源数据一样为 STORED 或 VIRTUAL

    SQLite 以相同方式计算它们。

  • 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)

    相同的外键,可用 PRAGMA foreign_key_check 列出

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

  • 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

文件中脚本中报告记录
declared types (any text: VARCHAR(255), DATETIME, BOOLEAN, JSON or none)列中每个值都符合的类型声明的类型相同,因此值按存储时的样子导入。
BOOLEAN, BOOLINTEGER 0 或 1,并带有 CHECK当每个值都是 0 或 1 时;同时包含其他值的列会将它们保留为数字或文本。
TEXT, VARCHAR(n), CHAR(n), CLOB声明的类型,按原样保留SQLite 从不强制长度;只有当每个值都符合时,才保留声明的长度。
DECIMAL(p,s), NUMERIC每个值都符合时为精确类型,否则为 doubleSQLite 将这些值存储为整数或 double;只有当值符合时,才保留声明的精度。
BLOB, and columns holding bytesBLOB字节保持为字节;同一列中的文本和数字按其字节保留。
DATE, DATETIME, TIMESTAMP holding ISO 8601 textDATE、DATETIME,以 ISO 8601 文本存储当每个值都是日期或日期加时间时;纪元时间数字和其他文本保持原值不变。
JSON holding JSON textTEXT当每个值都是 JSON 对象或数组时。
Infinity in REAL columns9e999,即 SQLite 自己的无穷大SQLite 将无穷大写为溢出的字面量,导入后会恢复为无穷大。
INTEGER PRIMARY KEY, AUTOINCREMENTINTEGER PRIMARY KEY,保留 AUTOINCREMENT只有声明恰好为 INTEGER PRIMARY KEY 的列才是 rowid;每一行都保留其 id,计数器从曾经分配过的最大 id 之后开始。
COLLATE NOCASECOLLATE NOCASE相同的排序规则。
generated columns生成列,与源数据一样为 STORED 或 VIRTUALSQLite 以相同方式计算它们。
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)相同的外键,可用 PRAGMA foreign_key_check 列出SQLite 只在被要求时才检查外键,因此孤立行很常见:它们会被导入,外键保持未检查状态并给出警告,而不是中止导入。
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

将脚本导入 SQLite

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

sqlite3 -bail mydb.sqlite < sqlite.sqlite.sql

外键在每个表中声明,行在关闭外键检查的情况下导入。随后脚本会列出所有指向不存在的行的行,而不是中途失败。

对于较大的转储,请导入到本地磁盘上的新文件中。脚本在关闭同步的单个事务中导入全部内容,因此以磁盘速度运行,中途停止的导入也不会留下不完整的表。

命令行可将文件直接导入 SQLite 数据库,无需生成脚本,而且免费。

如何转储 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 自己的语言编写的对象,会按名称列在报告中,绝不会被悄悄丢弃,你可以先在要导入的 SQLite 版本上逐一检查再重建。

关于在 SQLite 版本之间迁移的问题

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

我的转储会被上传吗?

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

转储可以多大?

转换器从不把整个转储放进内存:它按范围读取文件两次,边读边写出脚本。几 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 不强制类型时,列类型如何确定?

每列保留其声明的类型,COLLATE NOCASE 和 RTRIM 也是如此。STRICT 会被去掉,因此脚本可以在 3.31 起的每个版本上导入。

INTEGER PRIMARY KEY 和 AUTOINCREMENT 会保留吗?

会。INTEGER PRIMARY KEY 和 AUTOINCREMENT 保持不变,sqlite_sequence 从转储时的位置继续。

视图和触发器怎么处理?

它们会按名称在报告中列出,但不会转换,以便你在导入表之后重新创建它们。

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

SQLite 3.31 及更高版本。每个测试文件和转储都能在 3.31 和 3.54 上导入,如有问题会在第一个错误处停止。