跳到正文
IntoSQL 首页

将 PostgreSQL 转换为 SQLite

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

你的文件
sqlshop_2024_postgres.sql示例文件,6.2 kB
你的数据库

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

显示

shop_2024_postgres.sql 中的表PostgreSQL 18.6 转储 · 由 pg_dump 写出 · utf8mb4

  • customers8 行2
    8 列
    idINTEGER not null
    nameTEXT not null
    emailTEXT not null
    countryVARCHAR(2) not null
    is_activeINTEGER not null
    tagsTEXT not null
    signed_upDATE
    created_atTIMESTAMP not null
  • orders10 行1
    10 列
    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
    detailsTEXT
    deliveryTEXT

未转换

  • 视图 paid_orders

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

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

CREATE TABLE customers ( -- People who bought at least once
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL,
    email TEXT NOT NULL,
    country VARCHAR(2) DEFAULT 'US' NOT NULL,
    is_active INTEGER DEFAULT 1 NOT NULL,
    tags TEXT NOT NULL,
    signed_up DATE,
    created_at TIMESTAMP DEFAULT (strftime('%Y-%m-%d %H:%M:%f', 'now')) NOT NULL,
    CONSTRAINT customers_is_active_check CHECK (is_active IN (0, 1))
);

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,
    details TEXT,
    delivery TEXT,
    CONSTRAINT orders_status_check CHECK (status IN ('pending', 'paid', 'shipped', 'cancelled')),
    CONSTRAINT orders_orders_total_check CHECK (total >= (0)),
    CONSTRAINT orders_customer_id_fkey FOREIGN KEY (customer_id) REFERENCES customers (id)
);

INSERT INTO customers (id, name, email, country, is_active, tags, signed_up, created_at) VALUES
(1,'Ana Martínez','ana@example.com','MX',1,'["vip"]','2023-01-15','2023-01-15 10:24:00'),
(2,'Zoë Müller','zoe@example.de','DE',1,'[]','2023-02-03','2023-02-03 08:11:42'),
(3,'José Peña','jose@example.es','ES',0,'["wholesale","late payer"]','2023-03-21','2023-03-21 19:05:17'),
(4,'李雷','lilei@example.cn','CN',1,'[]','2023-05-09','2023-05-09 02:40:00'),
(5,'Noah O''Brien','noah@example.ie','IE',1,'[]',NULL,'2023-06-30 14:00:00'),
(6,'Amélie Laurent','amelie@example.fr','FR',1,'["vip"]','2024-01-02','2024-01-02 09:30:00'),
(7,'Ravi Patel','ravi@example.in','IN',0,'[]','2024-02-29','2024-02-29 23:59:59'),
(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, details, delivery) VALUES
(1,1,'shipped',129.90,3,'2024-03-01 10:00:00','2024-03-02 16:30:00',NULL,'{"gift": false}','1 day 06:30:00'),
(2,2,'paid',54.00,1,'2024-03-02 11:15:00',NULL,'Gift wrap 🎁','{"gift": true, "wrap": "red"}',NULL),
(3,1,'cancelled',12.50,1,'2024-03-03 09:45:00',NULL,'Customer changed their mind',NULL,NULL),
(4,4,'shipped',980.00,12,'2024-03-05 02:10:00','2024-03-07 08:00:00','请小心轻放','{"fragile": true}','2 days 05:50:00'),
(5,5,'pending',45.75,2,'2024-03-08 17:20:00',NULL,'Leave at the door; ring twice',NULL,NULL),
(6,6,'shipped',310.40,5,'2024-03-10 13:00:00','2024-03-11 09:00:00','Fragile: "glass"','{"fragile": true}','20:00:00'),
(7,8,'paid',76.20,2,'2024-03-11 18:45:00',NULL,NULL,NULL,NULL),
(8,2,'shipped',19.99,1,'2024-03-12 08:00:00',NULL,'Shipped date set to infinity by an old import',NULL,NULL),
(9,3,'shipped',64.00,2,'2024-03-14 15:30:00','2024-03-15 10:00:00','Two parcels','{"parcels": [1, 2]}','18:30:00'),
(10,7,'paid',1499.00,1,'2024-03-15 21:05:00',NULL,'C:\Users\ravi\invoice.pdf',NULL,NULL);

-- Indexes, triggers and counters, after the rows so each index builds once.
CREATE UNIQUE INDEX customers_customers_email_key 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, 1000) WHERE name = 'customers';
INSERT INTO sqlite_sequence (name, seq) SELECT 'customers', 1000 WHERE NOT EXISTS (SELECT 1 FROM sqlite_sequence WHERE name = 'customers');
UPDATE sqlite_sequence SET seq = max(seq, 5000) WHERE name = 'orders';
INSERT INTO sqlite_sequence (name, seq) SELECT 'orders', 5000 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;

修正

  1. customers 的 "created_at" 包含带时区的时间点;SQLite 没有能保留时区的类型,因此以 UTC 存储为 TIMESTAMP。
  2. customers 的 "tags" 是 text[],SQLite 没有该类型,因此为 TEXT,值保持不变。
  3. orders 的 "delivery" 是 interval,SQLite 没有该类型,因此为 TEXT,值保持不变。

需要检查

  • 1 个视图、触发器、例程或事件未转换;报告逐一列出了它们。 请在 SQLite 中手动重新创建它们。
  • customers 中 "tags" 的默认值无法为 SQLite 写出(a value its type can't hold),因此该列没有默认值。 如果新行需要,请手动添加默认值。
  • orders 上的索引 idx_orders_details 使用了无法为 SQLite 写出的表达式(a gin index),因此未创建。 如果查询需要,请手动创建该索引。
  • orders 的 "shipped_at" 中有 1 个值是 SQLite 无法存储的日期,例如 infinity,因此设为 NULL。 SQLite 存储 1 到 9999 年,不支持无穷值;如果这些日期很重要,请在源数据中替换它们。

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

其他示例:

从 PostgreSQL 到 SQLite 的变化

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

  • boolean

    INTEGER 0 或 1,并带有 CHECK

    SQLite 没有布尔类型;它自己的 true 和 false 就是 1 和 0。

  • varchar(n), char(n), text

    VARCHAR(n) 和 TEXT,不强制长度

    SQLite 保留声明的类型供工具读取,但不限制长度。

  • numeric(p,s) past 15 digits and numeric without a precision

    TEXT

    SQLite 的数字大约保留 15 位有效数字。更宽的数字保存为文本,使每一位都保持精确。

    报告记录: decimal_as_text

  • money

    NUMERIC

    SQLite 没有 money 类型。金额保存为数字,不带服务器区域设置添加的货币符号。

    报告记录: type_replaced

  • interval

    TEXT,存放 PostgreSQL 的文本

    SQLite 没有 interval 类型;文本保留每个部分。

    报告记录: type_replaced

  • arrays (integer[], text[]...)

    存放 JSON 数组的 TEXT

    SQLite 没有数组。JSON 数组保留每个元素和 NULL,SQLite 的 JSON 函数可以读取它。

    报告记录: type_replaced

  • ranges, geometric types, tsvector, xml, hstore, PostGIS, composite and extension types

    TEXT,存放 PostgreSQL 的文本

    SQLite 没有对应的类型,因此每个值保留 PostgreSQL 写出的文本。

    报告记录: type_replaced

  • json, jsonb

    TEXT

    SQLite 的 JSON 函数从文本读取 JSON。

  • timestamp

    DATETIME,存为 ISO 8601 文本

    SQLite 将日期存储为其日期函数能读取的文本。

  • timestamp with time zone

    TIMESTAMP,存为 UTC 的 ISO 8601 文本

    SQLite 没有能保留偏移量的类型,因此每个时间点以 UTC 写入:无论转储写入的偏移量是多少,都是同一时刻。

    报告记录: moments_as_utc

  • time

    TIME,存为文本

    SQLite 以文本保存时间,包括 24:00:00。

  • infinity, -infinity and BC dates

    NULL

    SQLite 的日期函数读取 0 到 9999 年,不支持无穷值。这些值变为 NULL,报告会统计其数量。

    报告记录: date_out_of_range

  • NaN and Infinity in numeric, real and double precision

    NULL

    SQLite 将 NaN 存储为 NULL,其无穷值也无法在所有工具中保留,因此这些值变为 NULL,报告会统计其数量。

    报告记录: invalid_value_null

  • NOT NULL columns with values that become NULL

    去除 NOT NULL

    值必须变为 NULL 的列无法保留 NOT NULL,否则导入会中止。

    报告记录: not_null_relaxed

  • enum types

    TEXT,并带有限定其标签的 CHECK

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

  • enum columns holding values outside their labels

    不加检查约束的 TEXT

    标签之外的值会使检查失败,因此不创建检查约束。

    报告记录: enum_check_skipped

  • uuid, inet, cidr, macaddr

    TEXT

    SQLite 没有这些类型;文本保留每个值。

  • bytea

    BLOB

    字节仍然是字节。

  • bit(n), bit varying

    不超过 63 位时为 INTEGER,值为各位组成的数;更长则为 TEXT 数字

    位串在能放入有符号整数时,存储为其各位组成的数字。

  • oid, xid, cid

    INTEGER,并带有不能为负数的 CHECK

    SQLite 的 64 位整数可容纳每个值。

  • citext and nondeterministic collations

    精确比较

    除非另有指定,SQLite 会精确比较文本,而其 NOCASE 只对 ASCII 字母忽略大小写。

    报告记录: case_insensitive_collation

  • serial, identity

    INTEGER PRIMARY KEY AUTOINCREMENT,从最大的 id 之后继续

    SQLite 为构成完整主键的整数列计数,AUTOINCREMENT 像序列一样防止 id 被重复使用。

  • serial and identity columns that aren't the whole primary key

    不带计数器的 INTEGER

    SQLite 只为构成完整主键的列计数,因此该列保留其值,新行需要自行提供值。

    报告记录: counter_dropped

  • sequences not owned by a column

    不创建

    SQLite 没有序列。

    报告记录: sequence_not_converted

  • generated columns

    GENERATED ALWAYS AS,与源数据一样为 STORED 或 VIRTUAL

    SQLite 3.31 及更高版本以相同的两种方式计算生成列。

  • generated columns with functions SQLite lacks

    带有这些值的普通列

    无法为 SQLite 写出的公式会留下一个带有转储中值的普通列和一条报告记录。

    报告记录: generated_not_translated

  • defaults with functions SQLite lacks, and defaults from a sequence

    无默认值

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

    报告记录: default_not_translated

  • CHECK constraints

    已转换,内联在 CREATE TABLE 中

    SQLite 只接受 CREATE TABLE 中的检查约束。

  • CHECK constraints with functions SQLite lacks

    不创建

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

    报告记录: check_skipped

  • CHECK ... NOT VALID

    不创建

    PostgreSQL 从未检查之前已有的行,而 SQLite 会在导入时检查它们。

    报告记录: check_not_valid

  • gin, gist, brin and hash indexes, partial and expression indexes

    不创建

    IntoSQL 为 SQLite 在列上写出 b-tree 索引;其他索引请在导入后重新创建。

    报告记录: index_skipped

  • foreign keys

    在 CREATE TABLE 中,不检查即导入,然后由 PRAGMA foreign_key_check 列出

    SQLite 只接受 CREATE TABLE 中的外键,且只在 PRAGMA foreign_keys 开启时检查。行在不检查的情况下导入,脚本最后列出所有没有父行的行。

  • foreign keys to tables the dump doesn't create

    不创建

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

    报告记录: foreign_key_skipped

  • partitioned tables

    单个表,包含每个分区的行

    行保持不变。

    报告记录: partitions_dropped

  • schemas

    单个文件,每个表名前加上其 schema 名

    一个 SQLite 文件就是一个数据库,因此用 schema 名区分同名的表;public 中的表保留原名。

    报告记录: schemas_merged

  • views, functions, procedures, triggers, rules, policies, extensions and types

    在报告中列出

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

    报告记录: not_converted_objects

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

    根据值推断类型

    只含数据的转储只有行,因此类型来自值。

    报告记录: no_schema

  • expressions in INSERT values

    NULL

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

    报告记录: expression_value

  • types IntoSQL doesn't know

    TEXT

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

    报告记录: unknown_type

  • text that isn't valid in its encoding

    错误的字节变为替换字符

    SQLite 文本为 UTF-8。

    报告记录: invalid_utf8_replaced

  • text decoded twice (ñ)

    按原样复制

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

    报告记录: mojibake_suspected

在 PostgreSQL 中在 SQLite 中报告记录
booleanINTEGER 0 或 1,并带有 CHECKSQLite 没有布尔类型;它自己的 true 和 false 就是 1 和 0。
varchar(n), char(n), textVARCHAR(n) 和 TEXT,不强制长度SQLite 保留声明的类型供工具读取,但不限制长度。
numeric(p,s) past 15 digits and numeric without a precisionTEXTSQLite 的数字大约保留 15 位有效数字。更宽的数字保存为文本,使每一位都保持精确。decimal_as_text
moneyNUMERICSQLite 没有 money 类型。金额保存为数字,不带服务器区域设置添加的货币符号。type_replaced
intervalTEXT,存放 PostgreSQL 的文本SQLite 没有 interval 类型;文本保留每个部分。type_replaced
arrays (integer[], text[]...)存放 JSON 数组的 TEXTSQLite 没有数组。JSON 数组保留每个元素和 NULL,SQLite 的 JSON 函数可以读取它。type_replaced
ranges, geometric types, tsvector, xml, hstore, PostGIS, composite and extension typesTEXT,存放 PostgreSQL 的文本SQLite 没有对应的类型,因此每个值保留 PostgreSQL 写出的文本。type_replaced
json, jsonbTEXTSQLite 的 JSON 函数从文本读取 JSON。
timestampDATETIME,存为 ISO 8601 文本SQLite 将日期存储为其日期函数能读取的文本。
timestamp with time zoneTIMESTAMP,存为 UTC 的 ISO 8601 文本SQLite 没有能保留偏移量的类型,因此每个时间点以 UTC 写入:无论转储写入的偏移量是多少,都是同一时刻。moments_as_utc
timeTIME,存为文本SQLite 以文本保存时间,包括 24:00:00。
infinity, -infinity and BC datesNULLSQLite 的日期函数读取 0 到 9999 年,不支持无穷值。这些值变为 NULL,报告会统计其数量。date_out_of_range
NaN and Infinity in numeric, real and double precisionNULLSQLite 将 NaN 存储为 NULL,其无穷值也无法在所有工具中保留,因此这些值变为 NULL,报告会统计其数量。invalid_value_null
NOT NULL columns with values that become NULL去除 NOT NULL值必须变为 NULL 的列无法保留 NOT NULL,否则导入会中止。not_null_relaxed
enum typesTEXT,并带有限定其标签的 CHECK带检查约束的文本保留相同的值。
enum columns holding values outside their labels不加检查约束的 TEXT标签之外的值会使检查失败,因此不创建检查约束。enum_check_skipped
uuid, inet, cidr, macaddrTEXTSQLite 没有这些类型;文本保留每个值。
byteaBLOB字节仍然是字节。
bit(n), bit varying不超过 63 位时为 INTEGER,值为各位组成的数;更长则为 TEXT 数字位串在能放入有符号整数时,存储为其各位组成的数字。
oid, xid, cidINTEGER,并带有不能为负数的 CHECKSQLite 的 64 位整数可容纳每个值。
citext and nondeterministic collations精确比较除非另有指定,SQLite 会精确比较文本,而其 NOCASE 只对 ASCII 字母忽略大小写。case_insensitive_collation
serial, identityINTEGER PRIMARY KEY AUTOINCREMENT,从最大的 id 之后继续SQLite 为构成完整主键的整数列计数,AUTOINCREMENT 像序列一样防止 id 被重复使用。
serial and identity columns that aren't the whole primary key不带计数器的 INTEGERSQLite 只为构成完整主键的列计数,因此该列保留其值,新行需要自行提供值。counter_dropped
sequences not owned by a column不创建SQLite 没有序列。sequence_not_converted
generated columnsGENERATED ALWAYS AS,与源数据一样为 STORED 或 VIRTUALSQLite 3.31 及更高版本以相同的两种方式计算生成列。
generated columns with functions SQLite lacks带有这些值的普通列无法为 SQLite 写出的公式会留下一个带有转储中值的普通列和一条报告记录。generated_not_translated
defaults with functions SQLite lacks, and defaults from a sequence无默认值无法为 SQLite 写出的默认值会被省略,并在报告中列出。default_not_translated
CHECK constraints已转换,内联在 CREATE TABLE 中SQLite 只接受 CREATE TABLE 中的检查约束。
CHECK constraints with functions SQLite lacks不创建无法为 SQLite 写出的检查约束会被省略,并在报告中列出。check_skipped
CHECK ... NOT VALID不创建PostgreSQL 从未检查之前已有的行,而 SQLite 会在导入时检查它们。check_not_valid
gin, gist, brin and hash indexes, partial and expression indexes不创建IntoSQL 为 SQLite 在列上写出 b-tree 索引;其他索引请在导入后重新创建。index_skipped
foreign keys在 CREATE TABLE 中,不检查即导入,然后由 PRAGMA foreign_key_check 列出SQLite 只接受 CREATE TABLE 中的外键,且只在 PRAGMA foreign_keys 开启时检查。行在不检查的情况下导入,脚本最后列出所有没有父行的行。
foreign keys to tables the dump doesn't create不创建外键无法引用不存在的表。foreign_key_skipped
partitioned tables单个表,包含每个分区的行行保持不变。partitions_dropped
schemas单个文件,每个表名前加上其 schema 名一个 SQLite 文件就是一个数据库,因此用 schema 名区分同名的表;public 中的表保留原名。schemas_merged
views, functions, procedures, triggers, rules, policies, extensions and types在报告中列出它们的代码用 PostgreSQL 的语言编写,因此会列出来,由你重新创建。not_converted_objects
dumps without CREATE TABLE (--data-only)根据值推断类型只含数据的转储只有行,因此类型来自值。no_schema
expressions in INSERT valuesNULL不运行数据库的转换器无法对它们求值。expression_value
types IntoSQL doesn't knowTEXT文本可以容纳任何值;请在导入后更改类型。unknown_type
text that isn't valid in its encoding错误的字节变为替换字符SQLite 文本为 UTF-8。invalid_utf8_replaced
text decoded twice (ñ)按原样复制转储中存储的正是这些字符,因此按原样复制;报告会指出它们。mojibake_suspected

将脚本导入 SQLite

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

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

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

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

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

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

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

  • --format=plain 是 pg_dump 的默认格式,写出 SQL。custom 或 directory 归档(-Fc、-Fd)可用 pg_restore -f dump.sql 转为 SQL,无需数据库服务器。
  • --no-owner --no-privileges 会省略角色和授权,转换器本来也会跳过它们,因此转储更小。
  • pg_dumpall 将每个数据库转储到一个文件中;每个数据库的表名前会加上该数据库名,因此不会冲突。

哪些内容不会转换

视图、物化视图、函数、存储过程、触发器、规则、行安全策略、扩展和类型都是用 PostgreSQL 自己的语言编写的。它们会按名称在报告中列出,绝不会被悄悄丢弃,以便你为 SQLite 重写。

关于 PostgreSQL 转 SQLite 的问题

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

我的转储会被上传吗?

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

转储可以多大?

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

它能读取 pg_dump -Fc 归档吗?

不能直接读取:custom 归档是二进制格式。pg_restore -f dump.sql dump.backup 会将其转为转换器读取的纯 SQL,无需连接服务器。转换器收到归档时会给出提示。

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

由 PostgreSQL 10 及更高版本的 pg_dump 和 pg_dumpall 写出的转储,行可以是 COPY 或 INSERT。每个测试转储都来自 PostgreSQL 13 和 18,并与其源数据逐个单元格比较。

serial 和标识列的值会保留吗?

构成表的完整主键的 serial 或标识列变为 INTEGER PRIMARY KEY AUTOINCREMENT,每行保留其 id,下一个 id 从最大的 id 之后继续。其他列保留其值,不带计数器,报告会说明这一点。

数组、enum 和其他 PostgreSQL 类型会怎样处理?

数组变为 JSON 数组,布尔值变为 0 和 1,enum 变为带有标签检查约束的文本,带时区的时间戳变为 UTC 文本。宽 numeric 以文本保持精确。range、几何类型和复合类型保留 PostgreSQL 的文本。

函数、触发器和视图怎么处理?

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

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

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