将 SQLite 数据库迁移到更旧或更新的 SQLite
拖入 SQLite 数据库文件 (.db、.sqlite、.sqlite3) 或 sqlite3 的 .dump 写出的 SQL,即可下载可在 SQLite 3.31 及以上导入的脚本。每列保留声明的类型和排序规则,并去掉 STRICT 以便旧版本接受这些表,每项更改都会列在报告中。
已转换 18 行,无修正,无拒绝
shop_2024_sqlite.sql 中的表SQLite 转储 · 由 sqlite3 写出 · utf-8
customers8 行
7 列 id INTEGER not null name TEXT not null email TEXT not null country TEXT not null is_active INTEGER not null signed_up DATE created_at DATETIME not null orders10 行
9 列 id INTEGER not null customer_id INTEGER not null status TEXT not null total DECIMAL(10,2) not null items INTEGER not null ordered_at DATETIME not null shipped_at DATETIME note TEXT receipt BLOB
未转换
- 视图 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, BOOL | INTEGER 0 或 1,并带有 CHECK当每个值都是 0 或 1 时;同时包含其他值的列会将它们保留为数字或文本。 | |
| TEXT, VARCHAR(n), CHAR(n), CLOB | 声明的类型,按原样保留SQLite 从不强制长度;只有当每个值都符合时,才保留声明的长度。 | |
| DECIMAL(p,s), NUMERIC | 每个值都符合时为精确类型,否则为 doubleSQLite 将这些值存储为整数或 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 或 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 write | NULLunistr()、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 上导入,如有问题会在第一个错误处停止。