我有以下复合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);
答案 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_id
和value
的速度很快比做明确排序步骤的减速要大。
仅对于此查询,您可以删除此处未提及的所有索引。
如果部件号可以以区分大小写的方式进行搜索,则应删除所有COLLATE NOCASE内容,并使用区分大小写的搜索(partnum BETWEEN 'abc' AND 'abcz'
)替换LIKE搜索。