Performance 提高SQLite反连接性能
查看此问题底部的更新,下面提到的查询时间意外变化的原因已被确定为sqliteman怪癖的结果 我在SQLite数据库中有以下两个表(我知道这个结构似乎毫无意义,但请容忍) 源表包含大约500000行,目标表中永远不会有多个匹配记录,实际上几乎所有源行都可能有一个匹配的目标行 我正试图执行一个相当标准的反连接来查找源中的所有记录,而目标中没有相应的行,但是我发现很难创建一个执行时间可以接受的查询 我使用的查询是:Performance 提高SQLite反连接性能,performance,sqlite,join,Performance,Sqlite,Join,查看此问题底部的更新,下面提到的查询时间意外变化的原因已被确定为sqliteman怪癖的结果 我在SQLite数据库中有以下两个表(我知道这个结构似乎毫无意义,但请容忍) 源表包含大约500000行,目标表中永远不会有多个匹配记录,实际上几乎所有源行都可能有一个匹配的目标行 我正试图执行一个相当标准的反连接来查找源中的所有记录,而目标中没有相应的行,但是我发现很难创建一个执行时间可以接受的查询 我使用的查询是: SELECT source.item_id, source.time
SELECT
source.item_id,
source.time,
source.data
FROM source
LEFT JOIN target USING (item_id, time)
WHERE target.item_id IS NULL;
没有WHERE子句的左连接大约需要200毫秒才能完成,随着时间的增加,这将增加到5000毫秒
虽然我最初注意到消费应用程序中的查询速度很慢,但上面的计时是通过直接从sqliteman中执行语句获得的
这个看似简单的子句如此显著地增加了执行时间,有什么特别的原因吗?有什么方法可以重组这个查询来改进它吗
我也尝试过以下方法,但效果相同。(我认为底层查询计划是相同的)
非常感谢
更新
非常抱歉,这些明显的结果实际上是由于sqliteman的一个怪癖造成的
sqliteman似乎对返回到256的行数任意应用了一个限制,并将在滚动时更动态地加载。这将使大型数据集上的查询看起来比实际要快得多,这使得它在估计查询性能时是一个糟糕的选择
尽管如此,他们是否有任何明显的方法来提高此查询的性能,或者我只是达到了SQLite的能力极限?这是您的查询的关键(任意一个):
这是尽可能有效的:
目标
中搜索匹配行源
行可能没有排序,因此目标
搜索将在索引中的随机位置进行查找。
如果我们可以强制源
扫描按索引顺序进行,那么目标
查找也将按顺序进行,这使得这些索引页更有可能已经在缓存中
如果我们不使用索引中没有的任何列,即如果我们删除数据
列,SQLite将使用源
索引:
> EXPLAIN QUERY PLAN
SELECT source.item_id, source.time
FROM source
LEFT JOIN target USING (item_id, time)
WHERE target.item_id IS NULL;
0|0|0|SCAN TABLE source USING COVERING INDEX si
0|1|1|SEARCH TABLE target USING COVERING INDEX ti (item_id=? AND time=?)
这可能没有多大帮助。
但是,如果有帮助,并且您希望在源代码
中使用其他列,您可以通过先执行联接,然后按行的rowid
查找源代码
行来实现这一点(如果结果很少,则额外的查找应该不会有任何影响):
你到底是如何衡量执行时间的?IS NULL查询应该不会慢一些。我在问题中添加了一个注释。这些时间是使用sqliteman中的查询工具获得的。这是否实际获取了所有结果行?如果不是,则从(实际查询)中测量选择COUNT(*)。真诚的道歉,我应该使用其他查询工具验证执行时间,因为结果表明,更快的查询是sqliteman怪癖的结果。非常感谢。查询计划实际上没有改变,但是当结果较少时,您需要搜索更多的表行以获得前256个结果。非常感谢。我可以确认,虽然您描述的查询最初并没有更快,但随后的调用明显更快,这是我原始查询中未观察到的行为。
SELECT
source.item_id,
source.time,
source.data
FROM source
WHERE NOT EXISTS (
SELECT 1 FROM target
WHERE target.item_id = source.item_id
AND target.time = source.time
);
0|0|0|SCAN TABLE source
0|1|1|SEARCH TABLE target USING COVERING INDEX ti (item_id=? AND time=?)
> EXPLAIN QUERY PLAN
SELECT source.item_id, source.time
FROM source
LEFT JOIN target USING (item_id, time)
WHERE target.item_id IS NULL;
0|0|0|SCAN TABLE source USING COVERING INDEX si
0|1|1|SEARCH TABLE target USING COVERING INDEX ti (item_id=? AND time=?)
SELECT *
FROM source
WHERE rowid IN (SELECT source.rowid
FROM source
LEFT JOIN target USING (item_id, time)
WHERE target.item_id IS NULL)