【问题标题】:SQLite table taking time to fetch the records in LIKE querySQLite 表需要时间来获取 LIKE 查询中的记录
【发布时间】:2012-03-24 15:55:13
【问题描述】:

场景:数据库是sqlite(需要对数据库中的记录进行加密。因此使用了iOS的SQL cipher API)

数据库中有一个名为partnumber的表,其架构如下:

CREATE TABLE partnumber (
  objid varchar PRIMARY KEY,
  description varchar,
  make varchar,
  model varcha,
  partnumber varchar,
  SSOKey varchar,
  PMOKey varchar
)

此表包含大约 80K 条记录。

UI 视图中有 3 个文本字段,用户可以在其中输入搜索词,并在用户输入字母后立即进行搜索。

3 个文本字段是:txtFieldDescription、txtFieldMake 和 txtFieldModel。

假设,第一个用户在 txtFieldDescription 中输入搜索词“monitor”。因此,每个字母都会执行的查询是:

1.

    SELECT DISTINCT description COLLATE NOCASE
    FROM partnumber where description like ‘%m%’

2.

    SELECT DISTINCT description COLLATE NOCASE
    FROM partnumber where description like ‘%mo%’

3.

    SELECT DISTINCT description COLLATE NOCASE
    FROM partnumber where description like ‘%mon%’

4.

    SELECT DISTINCT description COLLATE NOCASE
    FROM partnumber where description like ‘%moni%’

5.

    SELECT DISTINCT description COLLATE NOCASE
    FROM partnumber where description like ‘%monit%’

6.

    SELECT DISTINCT description COLLATE NOCASE
    FROM partnumber where description like ‘%monito%’

7.

    SELECT DISTINCT description COLLATE NOCASE
    FROM partnumber where description like ‘%monitor%’

到目前为止一切顺利。现在假设如果用户想要搜索模型(txtFieldDescription 仍然包含“监视器”)。所以用户点击txtFieldModel。一旦用户点击模型,就会触发如下查询:

SELECT DISTINCT model COLLATE NOCASE
FROM partnumber where description like ‘%monitor%’

此查询将返回描述包含监视器(在任何位置)的记录的所有模型。

现在,如果用户想要搜索所有包含单词“sony”的模型(描述字段仍然包含监视器),那么每个字母都会执行的查询是:

1.

    SELECT DISTINCT model COLLATE NOCASE
    FROM partnumber WHERE model like ‘%s%’ AND description like ‘%monitor%’

2.

    SELECT DISTINCT model COLLATE NOCASE
    FROM partnumber WHERE model like ‘%so%’ AND description like ‘%monitor%’

3.

    SELECT DISTINCT model COLLATE NOCASE
    FROM partnumber WHERE model like ‘%son%’ AND description like ‘%monitor%’

4.

    SELECT DISTINCT model COLLATE NOCASE
    FROM partnumber WHERE model like ‘%sony%’ AND description like ‘%monitor%’

现在,如果用户点击 txtFieldMake 并输入搜索词“1980”,那么被触发的查询是:

1.

    SELECT DISTINCT make COLLATE NOCASE
    FROM partnumber WHERE make like ‘%1%’
      AND model like ‘%sony%’ AND description like ‘%monitor%’

2.

    SELECT DISTINCT make COLLATE NOCASE
    FROM partnumber WHERE make like ‘%19%’
      AND model like ‘%sony%’ AND description like ‘%monitor%’

3.

    SELECT DISTINCT make COLLATE NOCASE
    FROM partnumber WHERE make like ‘%198%’
      AND model like ‘%sony%’ AND description like ‘%monitor%’

4.

    SELECT DISTINCT make COLLATE NOCASE
    FROM partnumber WHERE make like ‘%1980%’
      AND model like ‘%sony%’ AND description like ‘%monitor%’

这里,从 txtFieldDescription 到 txtFieldModel 或 txtFieldModel 到 txtFieldMake 转换的时间延迟太大,在 txtFieldModel 和 txtFieldMake 中,输入的字母在 5 或 6 秒后显示(在处理查询之后),因此光标挂在那里。

在分析中,我发现在 like 关键字(如“%monitor%”)中搜索词之前的通配符会减慢执行速度。在这种情况下,可能有多达 3 个类似的关键字,它们之间带有 AND,因此,执行时间肯定会增加。此外,在 like 开头使用通配符会否定索引。

一些附加信息:

  1. 总记录数~80K

  2. SELECT 查询每次在表部件号 (~80K) 上运行

  3. 我执行的一些查询的结果:

    Sqlite> SELECT count(DISTINCT description COLLATE NOCASE) from partnumber;
        Result is: 2599
    
    Sqlite> SELECT count(DISTINCT make COLLATE NOCASE) from partnumber;
        Result is: 7129
    
    Sqlite> SELECT count(DISTINCT model COLLATE NOCASE) from partnumber;
        Result is: 64644
    
    Sqlite> SELECT count(objid) from partnumber;
        Result is: 82135
    
  4. 索引创建如下:

    CREATE INDEX index_description
    ON partnumber (description collate nocase)
    
    CREATE INDEX index_make
    ON partnumber (make collate nocase)
    
    CREATE INDEX index_model
    ON partnumber (model collate nocase)
    

提高性能的一些替代方案:

  1. 由于 distinct description 的计数只有 2599,make 的计数只有 7129,所以可以将表拆分为不同的表,一个包含 DISTINCT description COLLATE NOCASE 输出(共 2599 行),一个包含 DISTINCT制作 COLLATE NOCASE(总共 7129 行)。就模型而言,为其创建不同的表将无济于事,因为行数 ~64644 几乎等于总记录 ~82135。 但是这种方法的问题是我不知道如何在这些表中进行搜索,每个表中必须有哪些列以及必须创建多少表。如果用户输入一些描述然后输入模型,然后再次输入新的描述会怎样。

  2. 由于此选择查询的结果显示在 UITableView 中,并且用户一次最多只能看到 5 行。因此,我们可以将返回的行数限制为 500,当用户滚动时,可以获取下一个 500,依此类推,直到最后搜索的记录。

但这里的问题是虽然我只需要 500 条记录,但我必须搜索整个表(SCAN ~80K 记录)。所以,我需要一个查询,首先只搜索表的前 10% 并从中返回前 500 行,然后搜索接下来的 500 到前 10% 的记录,然后是下一个 10%,然后是下一个 10% 到 80000 条记录正在搜索(需要搜索 10-10% 的记录块)。

  1. 如果可以将80K记录的表拆分为4张20K记录的表,然后对所有4张表同时进行搜索(在不同的后台线程中)以获取结果集。但是这里我不知道如何在 4 个不同的线程中运行查询(某种批处理执行),何时合并结果以及如何知道所有线程都已完成执行。

  2. 如果我可以将 like %monitor%' 替换为另一个返回相同结果但执行速度更快的函数,并且该函数的使用不影响索引的使用,(即不通过使用索引),那么执行可能会变得更快。如果有人可以在 sqlite 中建议我这样的功能,那么我可以继续使用这种方法。

如果您可以帮助我实现这些替代方案中的任何一种,或者您可以向我推荐任何其他解决方案,那么我将能够提高查询的执行速度。并且请不要告诉我在 sqlite 中启用 FTS(全文搜索),因为我已经尝试过这样做,但我不知道确切的步骤。非常感谢您如此耐心地阅读这个问题......

编辑:

大家好,我取得了一些成功。我将选择查询修改为如下所示:

select distinct description collate nocase as description from partnumber where rowid BETWEEN 1 AND (select max(rowid) from partnumber) AND description like '%a%' order by description;

Bingo,搜索时间前所未有。但是现在的问题是当我像这样执行命令 EXPLAIN QUERY PLAN 时,它显示我使用 B-Tree 来区分我不想使用的。

explain query plan select distinct description collate nocase as description from partnumber where rowid BETWEEN 1 AND (select max(rowid) from partnumber) AND description like '%a%' order by description;

输出:

0|0|0|SEARCH TABLE partnumber USING INTEGER PRIMARY KEY (rowid>? AND rowid<?) (~15625 rows)
0|0|0|EXECUTE SCALAR SUBQUERY 1
1|0|0|SEARCH TABLE partnumber USING INTEGER PRIMARY KEY (~1 rows)
0|0|0|USE TEMP B-TREE FOR DISTINCT

编辑:

对不起各位。上述方法(使用 rowid 进行搜索)在设备上花费的时间比原来的要多。我尝试过删除 distinct 和 order by 关键字,但没有用。在 iPhone 上仍然需要约 8-10 秒。请帮帮我。

【问题讨论】:

    标签: ios sqlite query-optimization sqlcipher


    【解决方案1】:

    安舒尔,

    我知道您说过“请不要告诉我在 sqlite 中启用 FTS(全文搜索),因为我已经尝试过这样做,但我不知道确切的步骤”,但是 FTS 是您将要获得的唯一方法这表现得很好。没有什么魔法可以使全表扫描执行得很好。我建议阅读 FTS,花时间学习它,然后使用它:http://sqlite.org/fts3.html

    【讨论】:

    • ,我喜欢在我的 iPhone 应用程序中使用 SQLChiper 来保护 SQLite3.0 数据库。我已经按照 SQLCHiper 中的 iOS 实现库的描述进行了所有设置。但在编译过程中,它要求特定的库文件进行加密/解密。在获取该库文件时,我们需要购买 SQLChiper 的许可证吗?如果是,我们能得到关于那个的详细信息吗?
    猜你喜欢
    • 2018-07-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-07
    • 2016-09-25
    相关资源
    最近更新 更多