Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/sqlite/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sqlite 如何获得按另一个表中的字段排序的更快的FTS4查询结果?_Sqlite_Fts3_Fts4 - Fatal编程技术网

Sqlite 如何获得按另一个表中的字段排序的更快的FTS4查询结果?

Sqlite 如何获得按另一个表中的字段排序的更快的FTS4查询结果?,sqlite,fts3,fts4,Sqlite,Fts3,Fts4,背景 我正在使用SQLite的内置引擎,对存储在SQLite中的大量电子邮件进行全文搜索。我得到了一些相当差的查询性能,尽管不是我所期望的那样。让我们看一下。 代表性模式 我将给出一些有关代码的简化示例,并在适用的情况下提供完整代码的链接 我们有一个MessageTable,用于存储有关电子邮件的数据(完整版本分布在多个文件中,并且): 可搜索文本添加到名为MessageSearchTable(完整版本)的FTS4表中: 搜索表中的id充当消息表的外键 我将把它作为一个练习留给读者在这些表中插入

背景

我正在使用SQLite的内置引擎,对存储在SQLite中的大量电子邮件进行全文搜索。我得到了一些相当差的查询性能,尽管不是我所期望的那样。让我们看一下。

代表性模式

我将给出一些有关代码的简化示例,并在适用的情况下提供完整代码的链接

我们有一个
MessageTable
,用于存储有关电子邮件的数据(完整版本分布在多个文件中,并且):

可搜索文本添加到名为
MessageSearchTable
(完整版本)的FTS4表中:

搜索表中的
id
充当消息表的外键

我将把它作为一个练习留给读者在这些表中插入数据(我当然不能给出我的私人电子邮件)。我在每个表中都有不到26k条记录

问题查询

当我们检索搜索结果时,我们需要将它们按
internaldate\u time\t
降序排列,这样我们就可以只提取最近的几个结果。下面是一个示例搜索查询(完整版本):

在我的机器上,通过以下方式测量,电子邮件的运行时间约为150毫秒:

time sqlite3 test.db <<<"..." > /dev/null
如果我对的解释是正确的,那么使用
MessageTableInternalDateTimeTIndex
看起来查询完全跳过了。查询的完整版本还包含以下行:

Sort Operations:                     1
Fullscan Steps:                      25824
听起来像是在桌子上的某个地方走动,但我们暂时忽略这一点

我的发现

让我们来优化一下。我可以将查询重新安排为一个子选择,并强制SQLite将我们的索引与扩展一起使用:

SELECT id
FROM MessageTable
INDEXED BY MessageTableInternalDateTimeTIndex
WHERE id IN (
    SELECT id
    FROM MessageSearchTable
    WHERE MessageSearchTable MATCH 'a'
)
ORDER BY internaldate_time_t DESC
LIMIT 10 OFFSET 0
瞧,运行时间已经下降到大约100毫秒(查询的完整版本为300毫秒,运行时间减少了50%),并且没有报告任何排序操作。请注意,通过像这样重新组织查询,但不强制使用索引的索引索引,仍然有一个排序操作(尽管我们仍然非常奇怪地减少了几毫秒),因此,除非强制执行,否则SQLite似乎确实忽略了我们的索引

我还尝试了一些其他方法,看看它们是否能带来不同,但它们没有:

  • 按照说明明确编制索引
    DESC
    ,有无
  • 在索引中显式添加
    id
    列,包括和不包括
    internaldate\u time\t
    ordered
    DESC
    ,包括和不包括
    index BY
  • 可能还有几件事我现在记不得了
问题

这里的100毫秒似乎仍然非常慢,因为它似乎应该是一个简单的FTS查找和索引顺序

  • 这是怎么回事?为什么它会忽略明显的索引,除非你强迫它这么做
  • 将虚拟表和常规表中的数据组合在一起是否遇到了一些限制
  • 为什么它仍然相对较慢,我还可以做些什么来让FTS匹配按另一个表中的字段排序

谢谢

索引用于根据索引列的值查找表行。 一旦找到一个表行,索引就不再有用了,因为用任何其他标准在索引中查找表行是没有效率的


这意味着不可能对查询中访问的每个表使用多个索引

另请参阅文档:


您的第一个查询具有以下输出:

发生了什么事

  • FTS索引用于查找所有匹配的
    MessageSearchTable
  • 对于在1中找到的每一行,
    MessageTable
    主键索引用于查找匹配的行
  • 在2中找到所有行。使用临时表进行排序
  • 返回前10行
  • 您的第二个查询具有以下解释查询计划输出:

    0 0 0 SCAN TABLE MessageTable USING COVERING INDEX MessageTableInternalDateTimeTIndex (~100000 rows)
    0 0 0 EXECUTE LIST SUBQUERY 1
    1 0 0 SCAN TABLE MessageSearchTable VIRTUAL TABLE INDEX 4: (~0 rows)
    
    0 0 0 SEARCH TABLE MessageTable USING INTEGER PRIMARY KEY (rowid=?) (~25 rows)
    0 0 0 EXECUTE LIST SUBQUERY 1
    1 0 0 SCAN TABLE MessageSearchTable VIRTUAL TABLE INDEX 4: (~0 rows)
    0 0 0 USE TEMP B-TREE FOR ORDER BY
    
    发生了什么事

  • FTS索引用于查找所有匹配的
    MessageSearchTable
  • SQLite按照索引顺序遍历
    MessageTableInternalDateTimeTIndex
    中的所有条目,并在
    id
    值是步骤1中找到的值之一时返回一行。 SQLite在第十行之后停止
  • 在此查询中,可以使用索引进行(隐含的)排序,但这只是因为没有其他索引用于查找此表中的行。 以这种方式使用索引意味着SQLite必须遍历所有条目,而不是查找与其他条件匹配的几行

    当您从第二个查询中省略
    index BY
    子句时,您将获得以下解释查询计划输出:

    0 0 0 SCAN TABLE MessageTable USING COVERING INDEX MessageTableInternalDateTimeTIndex (~100000 rows)
    0 0 0 EXECUTE LIST SUBQUERY 1
    1 0 0 SCAN TABLE MessageSearchTable VIRTUAL TABLE INDEX 4: (~0 rows)
    
    0 0 0 SEARCH TABLE MessageTable USING INTEGER PRIMARY KEY (rowid=?) (~25 rows)
    0 0 0 EXECUTE LIST SUBQUERY 1
    1 0 0 SCAN TABLE MessageSearchTable VIRTUAL TABLE INDEX 4: (~0 rows)
    0 0 0 USE TEMP B-TREE FOR ORDER BY
    
    这与第一个查询基本相同,只是连接和子查询的处理略有不同


    使用您的表结构,不可能真正加快速度。
    您正在执行三项操作:

  • MessageSearchTable
    中查找行
  • MessageTable
    中查找相应的行
  • MessageTable
    值对行进行排序
  • 就索引而言,步骤2和步骤3相互冲突。 数据库必须选择是为步骤2使用索引(在这种情况下,必须显式地进行排序)还是为步骤3使用索引(在这种情况下,它必须遍历所有
    MessageTable
    条目)


    您可以尝试从FTS搜索中返回较少的记录,方法是将消息时间作为FTS表的一部分,并只搜索最近几天(如果没有得到足够的结果,则增加或减少时间)。

    “不可能为每个ta使用多个索引。”
    0 0 0 SCAN TABLE MessageTable USING COVERING INDEX MessageTableInternalDateTimeTIndex (~100000 rows)
    0 0 0 EXECUTE LIST SUBQUERY 1
    1 0 0 SCAN TABLE MessageSearchTable VIRTUAL TABLE INDEX 4: (~0 rows)
    
    0 0 0 SEARCH TABLE MessageTable USING INTEGER PRIMARY KEY (rowid=?) (~25 rows)
    0 0 0 EXECUTE LIST SUBQUERY 1
    1 0 0 SCAN TABLE MessageSearchTable VIRTUAL TABLE INDEX 4: (~0 rows)
    0 0 0 USE TEMP B-TREE FOR ORDER BY