1

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

数据库中有一个名为 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 中输入搜索词“监视器”。因此,每个字母都会执行的查询是:

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 make 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;

而宾果游戏,搜索时间前所未有。但现在的问题是,当我像这样执行命令 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 秒。请帮帮我。

4

1 回答 1

0

安舒尔,

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

于 2012-03-07T20:41:48.537 回答