索引不适用于 sqlite 表
Posted
技术标签:
【中文标题】索引不适用于 sqlite 表【英文标题】:Indices not working on sqlite table 【发布时间】:2012-03-02 12:12:17 【问题描述】:我正在搜索的列上使用索引。索引是这样创建的:
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|使用索引扫描表(~250000 行)
当我删除索引时,这个 EXPLAIN QUERY PLAN 产生的输出是:
0|0|0|扫描表(~250000 行) 0|0|0|使用 TEMP B-TREE 进行分组
在使用索引搜索表时,被扫描的行数(~250000 行)不应该更少吗???
我猜这里的问题在于 LIKE 关键字,因为我在某处读到 LIKE 关键字会使使用 if 索引无效...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|使用索引查找表零件号 model_index_partnumber (model>? 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;
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
【问题讨论】:
【参考方案1】:其他几个选项...
全文索引:
http://sqlite.org/fts3.html
描述全文搜索的最常见(和有效)方式是 “Google、Yahoo 和 Altavista 对放置在 万维网”。
SELECT count(*) FROM enrondata1 WHERE content MATCH 'linux'; /* 0.03 seconds */
SELECT count(*) FROM enrondata2 WHERE content LIKE '%linux%'; /* 22.5 seconds */
断字:
如果您要查找单词(或以开头的单词),您可以自己将文本块分解为单词并存储您自己的索引单词表。但即便如此,您也只能使用“monit%”之类的词来获得“monitor”之类的点击率
如果可能,请使用全文 - 代码会少得多。但是,如果由于某种原因这不是一个选项,那么您可以回退到自己的分词表,但这是有限的单词以避免扫描开始。 (比整个文本块开头更好)。
请注意,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?
【讨论】:
嘿,bryanmac,要使用 FTS3,我必须使用 CREATE VIRTUAL TABLE USING .... 查询而不是 CREATE TABLE ..... 查询来创建表?另外,如果我想搜索包含 '%moni%' 的记录,那么我需要使用 MATCH 'moni' 而不是 LIKE '%moni%???'【参考方案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 等全文搜索引擎的要求。这些通过在 RAM 中 100% 索引数据来工作。使用 SQLITE,您可以通过创建第二个内存数据库来执行类似的操作,将磁盘表中的所有数据读取到内存数据库中,然后使用内存数据库进行通配符搜索。您仍然可以使用诸如 LIKE "%monit%"
之类的查询进行全表扫描,但是该扫描发生在 RAM 中,因此并不那么耗时。您不需要将所有数据导入 RAM,只需将需要“%something%”搜索的部分导入,因为 SQLITE 可以进行跨数据库连接。 SQLITE 可以轻松创建内存数据库,ATTACH DATABASE
和 DETACH DATABASE
命令可以轻松地将第二个数据库连接到您的应用程序。这个问题Can iPhone sqlite apps attach to other databases?有一些IOS的示例代码
不知道为什么您不喜欢使用 B-Tree 进行解释,因为当您的数据必须从文件系统中读取时,b-tree 可能是可用的最快搜索结构。
【讨论】:
谢谢迈克尔。但问题是我的数据库中没有只有 1 个表。至少有10张桌子。虽然其他表只包含 1K 或 2K 记录,但将它们存储在 RAM 中会影响我的设备内存(iPhone 或 iPad)。如果您建议仅将这个特定表存储在 RAM 中,那么我真的不知道如何将 1 个表存储在 RAM 中,将其他 10 个表存储在内存中。如果您有任何替代解决方案,那么我会尝试。我再次编辑了这个问题。只需在链接中获得战利品即可。【参考方案3】:我有一本 mysql 书籍,其中建议使用 REVERSE()
文本(如果您的应用程序允许,请存储在列中)。然后使用LIKE(REVERSE('%something'))
搜索反向文本。
【讨论】:
嘿 Poodlehat,我不能这样做,因为我在某些查询中同时在 3 个 cols 上应用 LIKE。因此,反转文本然后应用 like 将无济于事。此外,我想在任何位置搜索包含搜索词的值。以上是关于索引不适用于 sqlite 表的主要内容,如果未能解决你的问题,请参考以下文章