我有两个表,
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 进行比较可能不公平,但仍然如此。 (或者,这是一个公平的比较吗?)
加快通话速度的一种方法可能只是在连接中交换两次桌子顺序。
从 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')
也许您已经找到了解决方案,但我遇到了类似的问题,在阅读了很多相关内容之后,还有这个问题,我找到了一个适合我的解决方案,我想分享它。
首先,就我而言,我只有虚拟表!
使用 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';
根据我的经验,比连接查询快得多
我希望它可以帮助别人:)