索引不在sqlite表上工作

时间:2012-03-02 12:12:18

标签: ios sqlite indexing

我在我正在搜索的列上使用索引。索引创建如下:

CREATE INDEX index1 on <TABLE>(<col1> COLLATE NOCASE ASC)
CREATE INDEX index2 on <TABLE>(<col2> COLLATE NOCASE ASC)
CREATE INDEX index3 on <TABLE>(<col3> COLLATE NOCASE ASC)

现在,搜索记录的选择查询是这样的:

select <col1> from <TABLE> where <col1> like '%monit%' AND <col2> like '%84%'   GROUP BY <col1> limit 0,501;

当我在我的sqlite数据库上运行EXPLAIN QUERY PLAN时,如下所示:

EXPLAIN QUERY PLAN select <col1> from <TABLE> where <col1> like '%monit%' AND <col2> like '%84%'   GROUP BY <col1> limit 0,501;

它将输出返回为:

0 | 0 | 0 |使用INDEX扫描表(~250000行)

当我删除索引时,此EXPLAIN QUERY PLAN产生的输出是:

0 | 0 | 0 |扫描表(~250000行) 0 | 0 | 0 |使用分组的TEMP B-BREE

当搜索表时使用索引时,扫描的行数(~250000行)是不是应该更小?

我想这里的问题是使用LIKE关键字,因为我已经读过某个地方LIKE关键字使索引无效... Here is the link

编辑:对于处理使用LIKE的查询的索引,LIKE的右侧必须是不以通配符开头的字符串文字。因此,在上面的查询中,我尝试在开头使用搜索参数,例如没有'%':

EXPLAIN QUERY PLAN select <col1> from <TABLE> where <col1> like 'monit%' AND <col2> like '84%'   GROUP BY <col1> limit 0,501;

我得到的输出是:

0 | 0 | 0 | SEARCH TABLE partnumber USING INDEX model_index_partnumber(model&gt;?AND model

所以,你看。被搜索的行数(而不是扫描数)是(~15625行)。 但现在的问题是我开始无法取消%外卡。任何人都建议我另一种方法来实现同样的目标......

编辑: 我尝试过使用终端的FTS3但是当我输入这个查询时:

CREATE VIRTUAL TABLE <tbl> USING FTS3 (<col_list>);

它的投掷错误如下: 错误:没有这样的模块:FTS3

有人请帮我从终端和XCode启用FTS3(需要我必须为这两个任务执行的步骤)。

我正在使用sqlcipher并且已经从终端执行此操作:

CFLAGS="-DSQLITE_ENABLE_FTS3=1" ./configure 

编辑:

请访问我发布的问题sqlite table taking time to fetch the records in LIKE query

编辑:

嘿所有,我取得了一些成功。我修改了我的选择查询,如下所示:

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

3 个答案:

答案 0 :(得分:3)

其他几个选项......

全文索引:

http://sqlite.org/fts3.html

  

描述全文搜索的最常见(也是最有效)方式是   “谷歌,雅虎和阿尔塔维斯塔所做的文件都放在了   万维网“。

SELECT count(*) FROM enrondata1 WHERE content MATCH 'linux';  /* 0.03 seconds */
SELECT count(*) FROM enrondata2 WHERE content LIKE '%linux%'; /* 22.5 seconds */ 

断言:

如果您正在寻找单词(或以单词开头的单词),您可以自己将文本blob分解为单词并存储您自己的索引单词表。但即便如此,你也只能像'monit%'这样的词来获得像“监视器”这样的热门歌曲

如果可能,请使用全文 - 代码将少得多。但是,如果由于某种原因这不是一个选项,那么你可以回到你自己的单词破解表,但有限的单词开头是为了避免扫描。 (优于整个文本块开头)。

请注意iOS附带的sqlite没有启用全文。你可以解决这个问题。有关于它的说明,它在以下地方使用:

http://longweekendmobile.com/2010/06/16/sqlite-full-text-search-for-iphone-ipadyour-own-sqlite-for-iphone-and-ipad/

有关创建和查询全文表的完整文档,请访问:http://sqlite.org/fts3.html

要使FTS3也能从终端工作,请参阅:

编译命令行界面@ http://www.sqlite.org/howtocompile.html

sqlite3 using fts3 create table in my mac terminal and how to use it in iphone xcode project?

答案 1 :(得分:2)

这很简单。您告诉SQLITE检查表中的每条记录。在不使用索引的情况下执行此操作会更快,因为使用索引会涉及额外的IO。当您想要检查表中记录的子集时使用索引,其中通过不必检查表中的每个记录来回收使用索引的额外IO。

当您说 LIKE “%something”时,表示all records with anything at all at the beginning of the field, followed by something。唯一的方法是检查每一条记录。请注意,如果仅使用 LIKE “something%”,仍应使用索引,因为在这种情况下,SQLITE可以使用索引查找以“something”开头的记录子集。在过去,当数据库不那么聪明时,我们曾经这样写它来强制使用索引。 SELECT * WHERE col1 >= "something" AND col1 < "somethinh",请注意在第二种情况下故意拼写错误。

如果可以,最好避免在LIKE条件开始时使用%。在某些情况下,您可以更改架构,以便将数据存储在两列而不是一列中。然后在两列中的第二列上使用 LIKE “something%”搜索。当然,这取决于您的数据结构正确。

但即使分成两列也是不可能的,也许可以用另一种方式划分和征服数据。例如,您可以将搜索字段拆分为单词,并将另一个搜索表中单个列中的每个单词编入索引。这样“寻找某种东西或其他”成为记录列表,其中“某些东西”与搜索表中的记录完全匹配。不需要像。然后,您将获得记录ID以检索原始记录。这是SOLR在内部执行的操作之一,因此如果您必须坚持使用SQLITE并且无法以任何方式利用SOLR或LUCENE,那么您可以随时了解它们如何构建反向索引并在SQLITE数据库中自己执行相同的操作。

请记住, LIKE “%something%”必须检查每条记录,但如果您可以先选择数据的子集,然后应用 LIKE 搜索,会运行得更快。填充缓存将产生与DISTINCT实验相同的效果。也许您需要做的就是扩大缓存以获得可接受的搜索时间。第一次搜索仍然会很慢,但人们通常会对你重试时会消失的问题感到宽容。

当您使用任意通配符时,您将非常接近像SOLR这样的全文搜索引擎要求。这些工作通过将数据100%索引到RAM中。使用SQLITE,您可以通过创建第二个内存数据库,将磁盘表中的所有数据读入内存数据库,然后使用内存数据库进行通配符搜索来执行类似操作。您仍然可以使用LIKE "%monit%"之类的查询进行全表扫描,但是该扫描发生在RAM中,而不是耗费时间。您不需要将所有数据导入RAM,只需要搜索需要“%something%”的部分,因为SQLITE可以进行跨数据库连接。 SQLITE可以轻松创建内存数据库,ATTACH DATABASEDETACH DATABASE命令可以轻松地将第二个数据库连接到您的应用程序。在此问题Can iPhone sqlite apps attach to other databases?

中有一些IOS示例代码

不确定为什么你不喜欢使用B-Trees的EXPLAIN,因为当你的数据必须从文件系统中读取时,b-tree可能是最快的搜索结构。

答案 2 :(得分:1)

我有一本MySQL书,建议REVERSE()文本(如果您的应用程序允许,则存储在一列中)。然后使用LIKE(REVERSE('%something'))搜索反向文本。