SQLite中的INTEGER PRIMARY KEY vs rowid

时间:2018-06-04 17:53:53

标签: 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)

VS

CREATE TABLE points (id INTEGER PRIMARY KEY,
                     tags BLOB NOT NULL,
                     lon INTEGER NOT NULL,
                     lat INTEGER NOT NULL)

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

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

1 个答案:

答案 0 :(得分:3)

看起来每行有一个字节的rowid(我认为)的别名的开销,我相信这可以解释为: -

  

当SQL表包含INTEGER PRIMARY KEY列时(其中   别名rowid)然后该列在记录中显示为NULL   值。 SQLite将始终使用表b-tree键而不是表   引用INTEGER PRIMARY KEY列时的NULL值。    Database File Format - 2.3. Representation Of SQL Tables

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

使用以下SQL创建了两个不同表的两个数据库,加载了1,000,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

对于Second(带有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

结果文件大小分别为29484Kb和30600Kb。

这是30600 - 29484 = 1,116的差异,乘以1024 = 1142784(距离1,000,000行不远,页面和自由空间可能会造成差异)。

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