3

现在一个名为 log 的表中有大约 200 万条记录。查询性能变得不可接受,但我不想在当前阶段将表拆分到不同的分区。因此,我尝试添加一些索引以提高查询性能。

CREATE TABLE log
           (
                id Integer primary key autoincrement,
                app_id text,
                __key__id INTEGER,

                secret text,
                trace_code text,
                url text,

                action text,

                facebook_id text,
                ip text,

                tw_time timestamp,
                time timestamp,

                tag text,
                to_url text,

                from_url text,
                referer text,

                weight integer,
                Unique(app_id, __key__id)
            );
CREATE INDEX key1 on log (action, url, tag);

但是,看起来 sqlite 只是忽略了我的索引,而是扫描了整个表。我错过了什么吗?

sqlite> explain query plan select count(*) from log where action like 'content_%
';
0|0|0|SCAN TABLE log (~1182357 rows)


sqlite> explain query plan select count(*) from log where action like 'content_%
' group by url, tag;
0|0|0|SCAN TABLE log (~1182357 rows)
0|0|0|USE TEMP B-TREE FOR GROUP BY

编辑1

@MaxSem 谢谢,当我将查询更改为:

sqlite> explain query plan select count(*) from log indexed by key1 where action
 in ('content_click','content_mouseover', 'content_display');
0|0|0|SEARCH TABLE log USING COVERING INDEX key1 (action=?) (~886770 rows)
0|0|0|EXECUTE LIST SUBQUERY 1

但是,我无法解释 Sqlite 无法处理原始查询的原因。

编辑2

我应该改变我的问题。有没有办法加快sqlite中的这种查询?

4

2 回答 2

5

我相信 SQLite 不能对带有 LIKE 谓词的查询使用索引列,即使以LIKE 查询开头也是如此。但是你可以用不等式来模拟这种 LIKE 谓词:

sqlite> create table t (action text, url text, tag text);
sqlite> 插入 t 值 ('click', 'foo', 'bar');
sqlite> 插入 t 值('clack'、'foo'、'bar');
sqlite> 插入 t 值 ('clock', 'foo', 'bar');
sqlite> 插入 t 值 ('cluck', 'foo', 'bar');
sqlite> 插入 t 值 ('cleck', 'foo', 'bar');
sqlite> 插入 t 值 ('clyck', 'foo', 'bar');
sqlite> 在 t (action, url, tag) 上创建索引 t_index;

以LIKE开头,你会得到一个完整的扫描:

sqlite> 解释查询计划 select count(*) from t where action like 'cl%';
0|0|0|SCAN TABLE t(~500000 行)

但是对于不等式,使用索引:

sqlite> 解释查询计划 select count(*) from t where action >= 'cl' and action <'cm';
0|0|0|搜索表 t 使用覆盖索引 t_index (action>? AND action<?) (~62500 行)

这种技术需要注意的是,在选择下限(此处为“cm”)时必须小心,以便字典顺序为您提供您所期望的,并且该技术不能轻松地对所有 LIKE 谓词进行建模。

当您使用带有标记的 IN 谓词来精确匹配时,当然会使用索引,因为您又回到了使用相等性:

sqlite> 解释查询计划 select count(*) from t where action in ('click', 'clack');
0|0|0|搜索表 t 使用覆盖索引 t_index (action=?) (~20 行)
0|0|0|执行列表子查询 1

如果您仍想使用 LIKE,您可以将您的表与您想要从子查询中预选的操作值列表连接起来:

sqlite> explain query plan select count(*) from t join (select distinct action from t where action like 'cl%') a where t.action = a.action;
1|0|0|SCAN TABLE t 使用覆盖索引 t_index(~500000 行)
0|0|1|SCAN SUBQUERY 1 AS a (~500000 行)
0|1|0|SEARCH TABLE t 使用 COVERING INDEX t_index (action=?) (~10 rows)

如果您愿意,这基本上为您提供了一个动态IN 谓词,而不是明确列出所有可能的值。

当然在这里,我正在从您的大表中提取与 LIKE 查询匹配的操作列表,因此这本身就是一个完整的扫描,否定了所有的好处,但是如果您将您的架构标准化为有一个单独的(小得多)操作带有一个小整数代理键的表,您将其用作日志表中的操作列,那么您将避免在日志表中多次存储重复字符串(由小整数替换),并且您可以轻松地加入他们的查询。

于 2012-07-31T08:58:53.227 回答
2

你需要:

PRAGMA case_sensitive_like=ON;

SQLite 的默认设置是使用不区分大小写的 LIKE 运算符。通常,不区分大小写和索引不能很好地相处。数据库可以使用不区分大小写的索引,但额外的复杂性和搜索是不值得的。

如果您打开 case_sensitive_like,那么 SQLite 应该为您的查询使用索引。

于 2014-05-28T06:07:48.990 回答