sqlite中sql语句的最佳索引配置?

时间:2013-11-25 13:34:16

标签: sql sqlite indexing

我有以下复合sql语句用于查找,我试图理解这是要创建的最佳索引(索引?),以及哪些我应该忽略,因为它们不需要或者是否适得其反有多个。

SELECT items.id, items.standard_part_number, 
items.standard_price, items.quantity, 
part_numbers.value, items.metadata, 
items.image_file_name, items.updated_at
FROM items LEFT OUTER JOIN part_numbers ON items.id=part_numbers.item_id 
AND part_numbers.account_id='#{account_id}'
WHERE items.standard_part_number LIKE '#{part_number}%' 
UNION ALL 
SELECT items.id, items.standard_part_number, 
items.standard_price, items.quantity, 
part_numbers.value, items.metadata, 
items.image_file_name, items.updated_at
FROM items LEFT OUTER JOIN part_numbers ON items.id=part_numbers.item_id 
AND part_numbers.account_id='#{account_id}'
WHERE part_numbers.value LIKE '#{part_number}%' 
ORDER BY items.standard_part_number
LIMIT '#{limit}' OFFSET '#{offset}'

我有以下索引,其中一些可能没有必要,或者我可能缺少一个索引?...或者更糟糕的是,有太多人可能会遇到最佳性能配置?

for items:
CREATE INDEX index_items_standard_part_number ON items (standard_part_number);

for part_numbers:
CREATE INDEX index_part_numbers_item_id ON part_numbers (item_id);
CREATE INDEX index_part_numbers_item_id_and_account_id on part_numbers (item_id,account_id);
CREATE INDEX index_part_numbers_item_id_and_account_id_and_value ON part_numbers (item_id,account_id,value);
CREATE INDEX index_part_numbers_item_id_and_value on part_numbers (item_id,value);
CREATE INDEX index_part_numbers_value on part_numbers (value);

更新: 上面列出的表的架构

CREATE TABLE accounts (id INTEGER PRIMARY KEY,name TEXT,code TEXT UNIQUE,created_at INTEGER,updated_at INTEGER,company_id INTEGER,standard BOOLEAN,price_list_id INTEGER);
CREATE TABLE items (id INTEGER PRIMARY KEY,standard_part_number TEXT UNIQUE,standard_price INTEGER,part_number TEXT,price INTEGER,quantity INTEGER,unit_of_measure TEXT,metadata TEXT,image_file_name TEXT,created_at INTEGER,updated_at INTEGER,company_id INTEGER);
CREATE TABLE part_numbers (id INTEGER PRIMARY KEY,value TEXT,item_id INTEGER,account_id INTEGER,created_at INTEGER,updated_at INTEGER,company_id INTEGER,standard BOOLEAN);

1 个答案:

答案 0 :(得分:0)

外连接约束连接顺序,因此除非必要,否则不应使用它们。 在第二个子查询中,WHERE part_numbers.value LIKE ...子句无论如何都会过滤掉任何不匹配的记录,因此您应该删除LEFT OUTER

SQLite每个(每个)子查询每个表最多可以使用一个索引。 因此,为了能够对搜索和排序使用相同的索引,两个操作必须使用相同的collation。 LIKE使用不区分大小写的排序规则,因此应声明ORDER BY使用相同的(ORDER BY items.standard_part_number COLLATE NOCASE)。 如果必须区分大小写区分编号,则无法进行此操作。 如果SQLite 实际不为两者使用相同的索引,则不需要这样做(使用EXPLAIN QUERY PLAN检查)。

在第一个子查询中,没有可用于items.standard_part_number LIKE '#{part_number}%'搜索的索引。 你需要这样的索引(LIKE需要NOCASE):

CREATE INDEX iii ON items(standard_part_number COLLATE NOCASE);

在第二个子查询中,SQLite可能会使用part_numbers作为连接中的外部表,因为它有两个已过滤的列。 这两个搜索的索引必须如下所示(第二列只有NOCASE ):

CREATE INDEX ppp ON part_numbers(account_id, value COLLATE NOCASE);

通过所有这些更改,查询及其EXPLAIN QUERY PLAN输出如下所示:

EXPLAIN QUERY PLAN
SELECT items.id, items.standard_part_number,
items.standard_price, items.quantity, 
part_numbers.value, items.metadata, 
items.image_file_name, items.updated_at
FROM items LEFT OUTER JOIN part_numbers ON items.id=part_numbers.item_id 
AND part_numbers.account_id='#{account_id}'
WHERE items.standard_part_number LIKE '#{part_number}%' 
UNION ALL 
SELECT items.id, items.standard_part_number, 
items.standard_price, items.quantity, 
part_numbers.value, items.metadata, 
items.image_file_name, items.updated_at
FROM items JOIN part_numbers ON items.id=part_numbers.item_id 
AND part_numbers.account_id='#{account_id}'
WHERE part_numbers.value LIKE '#{part_number}%' 
ORDER BY items.standard_part_number COLLATE NOCASE
LIMIT -1 OFFSET 0;
1|0|0|SEARCH TABLE items USING INDEX iii (standard_part_number>? AND standard_part_number<?)
1|1|1|SEARCH TABLE part_numbers USING COVERING INDEX index_part_numbers_item_id_and_account_id_and_value (item_id=? AND account_id=?)
2|0|1|SEARCH TABLE part_numbers USING INDEX ppp (account_id=? AND value>? AND value<?)
2|1|0|SEARCH TABLE items USING INTEGER PRIMARY KEY (rowid=?)
2|0|0|USE TEMP B-TREE FOR ORDER BY
0|0|0|COMPOUND SUBQUERIES 1 AND 2 (UNION ALL)

第二个子查询不能使用索引进行排序,因为part_numbers不是连接中的外部表,但是通过索引查找account_idvalue的速度很快比做明确排序步骤的减速要大。

仅对于此查询,您可以删除此处未提及的所有索引。

如果部件号可以以区分大小写的方式进行搜索,则应删除所有COLLATE NOCASE内容,并使用区分大小写的搜索(partnum BETWEEN 'abc' AND 'abcz')替换LIKE搜索。