我的问题是我的查询太慢了。
我有一个相当大的sqlite数据库。表格是:
CREATE TABLE results (
timestamp TEXT,
name TEXT,
result float,
)
(我知道作为TEXT的时间戳不是最佳的,但是出于这个问题的目的,请忽略它。我有时间的时候必须修正它)
“名称”是一个类别。该计算包含必须在每个时间戳上为所有“名称”进行的计算结果。因此,插入是在相等的时间戳下完成的,但查询将以相同的名称进行(即,我想给一个名称,获取其时间序列),例如:
SELECT timestamp,result WHERE name='some_name';
现在,我现在的工作方式是没有索引,计算所有结果,然后以名称CREATE INDEX index_name ON results (name)
创建索引。原因是插入时不需要索引,但是拥有索引会使索引查询变得非常快。
但是不是。数据库相当大。它有大约一百万个时间戳,对于每个时间戳,我都有大约1000个名称。
尽管我不确定,但我怀疑它变慢的原因是尽管我已经为名称建立索引,但它们仍然分散在整个物理磁盘上。像这样:
timestamp1,name1,result
timestamp1,name2,result
timestamp1,name3,result
...
timestamp1,name999,result
timestamp1,name1000,result
timestamp2,name1,result
timestamp2,name2,result
etc...
我敢肯定,使用NAME ='some_name'进行查询的速度要比行的物理排序方式慢:
timestamp1,name1,result
timestamp2,name1,result
timestamp3,name1,result
...
timestamp499997,name1000,result
timestamp499998,name1000,result
timestamp499999,name1000,result
timestamp500000,namee1000,result
etc...
那么,如何告诉SQLite我希望磁盘中的行的顺序不是它们写入的顺序?
更新:我进一步确信,使用这种索引进行选择的缓慢性完全来自于非连续磁盘访问。 SELECT * FROM results WHERE name=<something_that_doesnt_exist>
立即返回零结果。这表明它没有找到速度较慢的名称,而是从磁盘读取它们。
答案 0 :(得分:0)
普通的sqlite表具有一个64位整数(称为rowid和其他一些别名)作为主键。这确定了行在B *-树中存储的顺序(将所有实际数据放入叶节点页中)。您可以使用WITHOUT ROWID表进行更改,但这需要一个显式主键,该主键用于将行放置在B树中。因此,如果每一行的(name, timestamp)
列都具有唯一值,则可能会将具有相同名称的所有行留在较小的一组页面上,而不是散布在各处。
如果您大部分时间都在搜索特定名称,则希望复合PK处于该顺序,例如:
CREATE TABLE results (
timestamp TEXT
, name TEXT
, result REAL
, PRIMARY KEY (name, timestamp)
) WITHOUT ROWID
(当然也不必担心名称上的第二个索引。)折衷是随着需要在B树中拆分页面的机会增加,插入速度可能会变慢。
一些值得研究的实用指令:
由于您没有INTEGER PRIMARY KEY
,因此请删除大量行后再考虑使用VACUUM。