SQLite 中的整数主键与 rowid

Jay*_*Tee 3 sqlite rowid

我正在尝试将一些空间数据 (OSM) 导入 SQLite 数据库。SQLite 参考声明 INTEGER PRIMARY KEY 成为 rowid 的别名(如果未指定 WITHOUT ROWID)。可以肯定的是,我以两种不同的方式创建了我的主表:

CREATE TABLE points (tags BLOB NOT NULL,
                     lon INTEGER NOT NULL,
                     lat INTEGER NOT NULL)
Run Code Online (Sandbox Code Playgroud)

对比

CREATE TABLE points (id INTEGER PRIMARY KEY,
                     tags BLOB NOT NULL,
                     lon INTEGER NOT NULL,
                     lat INTEGER NOT NULL)
Run Code Online (Sandbox Code Playgroud)

我期望得到相同的结果,但是在运行应用程序两次后,我的两个数据库文件的大小明显不同:具有显式主键的版本需要大约 100 MB 以上的磁盘空间(1.5 GB 与 1.4 GB)。除了一个使用“id”,另一个使用“rowid”作为点 ID 的目标列之外,我的插入语句是相同的。

有没有人知道这种巨大的尺寸差异来自哪里?提前致谢。

Mik*_*keT 5

似乎每行一个字节(我认为)的 rowid 有一个别名是有开销的,我相信这是由以下原因解释的:-

当 SQL 表包含一个 INTEGER PRIMARY KEY 列(它是 rowid 的别名)时,该列将作为 NULL 值出现在记录中。在引用 INTEGER PRIMARY KEY 列时,SQLite 将始终使用表 b-tree 键而不是 NULL 值。 数据库文件格式 - 2.3。SQL 表的表示

根据以下测试,每行 1 个字节似乎非常接近:-

使用两个不同的表创建了两个数据库,使用以下 SQL 加载了 1,000,000 百万行:-

为了第一 :-

DROP TABLE IF EXISTS points;
CREATE TABLE IF NOT EXISTS points (tags BLOB NOT NULL, lon INTEGER NOT NULL, lat INTEGER NOT NULL);
WITH RECURSIVE counter(tags,lon,lat) AS (SELECT x'00000000', 0,0 UNION ALL SELECT tags, random() AS lon, random() AS lat FROM counter LIMIT 1000000)
INSERT INTO points (tags,lon,lat) SELECT * FROM counter;
SELECT * FROM points;
VACUUM
Run Code Online (Sandbox Code Playgroud)

对于第二个(使用 rowid 的别名):-

DROP TABLE IF EXISTS points;
CREATE TABLE IF NOT EXISTS points (id INTEGER PRIMARY KEY, tags BLOB NOT NULL, lon INTEGER NOT NULL, lat INTEGER NOT NULL);
WITH RECURSIVE counter(tags,lon,lat) AS (SELECT x'00000000', 0,0 UNION ALL SELECT tags, random() AS lon, random() AS lat FROM counter LIMIT 1000000)
INSERT INTO points (tags,lon,lat) SELECT * FROM counter;
SELECT * FROM points;
VACUUM
Run Code Online (Sandbox Code Playgroud)

生成的文件大小分别为 29484Kb 和 30600Kb。

那是 30600 - 29484 = 1,116 的差异,将其乘以 1024 = 1142784(与 1,000,000 行、页面和可用空间相差不远,这可能是造成差异的原因)。

  • 请注意 VACUUM 命令没有任何区别(因为它们是新表,所以没有期望它们会。)