场景:数据库是sqlite(需要加密数据库中的记录。因此使用的是用于iOS的SQL密码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仍包含'monitor')。所以用户点击txtFieldModel。用户单击模型后,会立即触发查询:
SELECT DISTINCT model COLLATE NOCASE
FROM partnumber where description like ‘%monitor%’
此查询将返回描述包含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秒后显示(在处理查询之后),因此光标挂在那里。
在分析时,我开始知道在关键词中搜索词之前的通配符(如'%monitor%')会减慢执行速度。在这种情况下,它们之间可能有多达3个类似关键字,并且因此,执行时间肯定会增加。此外,在开头使用通配符会否定索引。
以下补充资料:
记录总数~80K
每次在表partnumber(~80K)上运行SELECT查询
我执行的一些查询的结果:
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)
提高绩效的一些替代方案:
由于不同描述的计数仅为2599,而make的计数仅为7129,因此可以将表拆分为不同的表,其中一个包含DISTINCT描述COLLATE NOCASE输出(总共2599行)和一个包含DISTINCT的表使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个不同的线程(批处理执行)中运行查询,何时结合结果以及如何知道所有线程都已完成执行。
如果我可以将%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作为我不想使用的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,但它没用。在iPhone上仍需要约8-10秒。请帮助我。
答案 0 :(得分:0)
Anshul,
我知道你说过“请不要告诉我在sqlite中启用FTS(全文搜索),因为我已经尝试过这样做,但我不知道确切的步骤”,但是FTS是你唯一能得到的方法这表现不错。毫无魔力可以使全表扫描表现良好。我建议您阅读FTS,花时间学习它,然后使用它:http://sqlite.org/fts3.html。