通过连接到虚拟表来加速 SQLite 查询

问题描述 投票:0回答:2

我有两个表,

t1
和一个FTS5虚拟表
vt1
,其内容为
t1

sqlite> EXPLAIN QUERY PLAN
   ...> SELECT Count(*) as num FROM t1 WHERE deleted = 0;
QUERY PLAN
--SEARCH TABLE t1 USING COVERING INDEX ix_t1_t1Id (deleted=?)
sqlite> SELECT Count(*) as num FROM t1 WHERE deleted = 0;
308498
Run Time: real 0.043 user 0.023668 sys 0.009005

如上所示,实际查询需要~43ms

sqlite> EXPLAIN QUERY PLAN
   ...> SELECT Count(*) as num FROM vt1 WHERE vt1 MATCH 'foo';
QUERY PLAN
--SCAN TABLE vt1 VIRTUAL TABLE INDEX 131073:
sqlite> SELECT Count(*) as num FROM vt1 WHERE vt1 MATCH 'foo';
80789
Run Time: real 0.047 user 0.008021 sys 0.009640

实际查询大约需要 47 毫秒。到目前为止,一切都很好。但是当我连接两个表时出现问题

sqlite> EXPLAIN QUERY PLAN
   ...> SELECT Count(*) as num
   ...> FROM t1 JOIN vt1 ON t1.t1Id = vt1.t1Id
   ...> WHERE t1.deleted = 0 AND vt1 MATCH 'foo';
QUERY PLAN
|--SCAN TABLE vt1 VIRTUAL TABLE INDEX 0:m
 --SEARCH TABLE t1 USING INDEX sqlite_autoindex_t1_1 (t1Id=?)
sqlite> SELECT Count(*) as num
   ...> FROM t1 JOIN vt1 ON t1.t1Id = vt1.t1Id
   ...> WHERE t1.deleted = 0 AND vt1 MATCH 'foo';
80789
Run Time: real 26.218 user 1.396376 sys 5.413630

答案正确,但查询时间超过26秒!当然,我希望将此查询速度提高几个数量级,但我也想了解为什么此连接会导致速度减慢。

更新:所以,在我的头多次撞到sql的墙上之后,我想出了以下内容——如上所述,我确实有两组不同的查询,我可以像这样单独执行

Q1:(从 t1 中选择 t1Id,其中...)作为 a

Q2:(从 vt1 中选择 t1Id,其中 vt1 匹配‘bar’)AS b

然后,我可以做以下事情 -

从 a WHERE a.t1Id IN b 中选择 Count(*)

当然,实际上,我不会单独执行此操作,而是一次性执行一个非常混乱的 SQL,但查询非常快,几百毫秒而不是 > 25 秒

您可能会注意到,在上面的 Q2 中,我匹配了“bar”而不是“foo”。这是因为“bar”返回的行数比“foo”少。 当 FTS 查询中有太多匹配项时,问题仍然存在,在这种情况下,FTS 查询本身很慢,例如,“foo”匹配超过 80K 行。

现在,一个有趣的比较点 - 针对 ElasticSearch 实例(即文本中任何位置带有“foo”的所有行)的相同类型的查询(从用户的角度来看)非常快,在低于百女士的顺序。我意识到将 SQLite 与 ElasticSearch 进行比较可能不公平,但仍然如此。 (或者,这是一个公平的比较吗?)

sqlite
2个回答
0
投票

加快通话速度的一种方法可能只是在连接中交换两次桌子顺序。

从 vt1 连接 t1 ON t1.t1Id = vt1.t1Id

来自 SQLite 文档:

连接中嵌套循环的默认顺序是最左边的 FROM 子句中的表构成外层循环和最右边的循环 表形成内循环

另一件事需要注意的是,连接两个表会导致嵌套循环。你的两张桌子看起来相当大。 Match 还进行全文搜索。您应该看看是否可以对一列或多列使用“LIKE”命令来获得相同的结果。

@Ge3ng 还表示,当您加入 FTS 表时,sqlite 无法再重新排列您的查询以进行优化。您还可以尝试将 MATCH 添加到 join 语句本身。

从 vt1 JOIN t1 ON (t1.t1Id = vt1.t1Id AND vt1 MATCH 'foo')


0
投票

也许您已经找到了解决方案,但我遇到了类似的问题,在阅读了很多相关内容之后,还有这个问题,我找到了一个适合我的解决方案,我想分享它。

首先,就我而言,我只有虚拟表!

使用 fts5 在两个虚拟表之间进行联接查询并不顺利。所以我所做的就是进行两个简单的查询,在第一个查询中,获取一个表的 ID,然后将它们用作第二个表中的过滤器。

示例:

桌狗:

CREATE VIRTUAL TABLE dogs USING FTS5 (id, name, dog_walker_id);

桌遛狗者:

CREATE VIRTUAL TABLE dog_walkers USING FTS5 (id, name, age);

获取遛狗者中名字以a开头的所有狗

第一个查询:

SELECT id FROM dog_walkers WHERE name MATCH 'a*';

假设您得到:1,2,3,4

第二个查询:

SELECT * FROM dogs WHERE id MATCH '1 OR 2 OR 3 OR 4';

根据我的经验,比连接查询快得多

我希望它可以帮助别人:)

© www.soinside.com 2019 - 2024. All rights reserved.