场景:数据库是 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 开头使用通配符会否定索引。
一些附加信息:
总记录数~80K
SELECT 查询每次在表部件号上运行(~80K)
我执行的一些查询的结果:
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
索引创建如下:
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)
提高性能的一些替代方案:
由于distinct description的计数只有2599,make的计数只有7129,所以可以将表拆分成不同的表,一个包含DISTINCT description COLLATE NOCASE输出(共2599行),一个包含DISTINCT make COLLATE NOCASE(共7129 行)。就模型而言,为其创建不同的表将无济于事,因为行数 ~64644 几乎等于总记录 ~82135。但是这种方法的问题是我不知道如何在这些表中进行搜索,每个表中必须有哪些列以及必须创建多少表。如果用户输入一些描述然后输入模型然后再次输入新的描述会怎样。
由于此选择查询的结果显示在 UITableView 中,并且用户一次最多只能看到 5 行。因此,我们可以将返回的行数限制为 500,当用户滚动时,可以获取下一个 500,依此类推,直到最后搜索的记录。
但这里的问题是虽然我只需要 500 条记录,但我必须搜索整个表(SCAN ~80K 记录)。所以,我需要一个查询,它首先只搜索表的前 10% 并从中返回前 500 行,然后搜索接下来的 500 到前 10% 的记录,然后是下一个 10%,然后是下一个 10% 到 80000 条记录正在搜索(需要搜索 10-10% 的记录块)。
如果可以将80K记录的表拆分为4张20K记录的表,然后在所有4张表上同时(在不同的后台线程中)执行搜索以获得结果集。但是在这里我不知道如何在 4 个不同的线程中运行查询(某种批处理执行),何时组合结果以及如何知道所有线程都已完成执行。
如果我可以用另一个返回相同结果但执行速度更快的函数替换 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 秒。请帮帮我。