我试图通过创建索引来优化 SQL 查询以获得最佳性能。
表定义
CREATE TABLE Mots (
numero INTEGER NOT NULL,
fk_dictionnaires integer(5) NOT NULL,
mot varchar(50) NOT NULL,
ponderation integer(20) NOT NULL,
drapeau varchar(1) NOT NULL,
CONSTRAINT pk_mots PRIMARY KEY(numero),
CONSTRAINT uk_dico_mot_mots UNIQUE(fk_dictionnaires, mot),
CONSTRAINT fk_mots_dictionnaires FOREIGN KEY(fk_dictionnaires) REFERENCES Dictionnaires(numero)
);
索引定义
CREATE INDEX idx_dictionnaires ON mots(fk_dictionnaires DESC);
CREATE INDEX idx_mots_ponderation ON mots(ponderation);
CREATE UNIQUE INDEX idx_mots_unique ON mots(fk_dictionnaires, mot);
SQL 查询:
SELECT numero, mot, ponderation, drapeau
FROM mots
WHERE mot LIKE 'ar%'
AND fk_dictionnaires=1
AND LENGTH(mot)>=4
ORDER BY ponderation DESC
LIMIT 5;
查询计划
0|0|0|SEARCH TABLE mots USING INDEX idx_dictionnaires (fk_dictionnaires=?) (~2 rows)
0|0|0|USE TEMP B-TREE FOR ORDER BY
定义的索引似乎没有使用并且查询持续(根据 .timer):
CPU Time: user 0.078001 sys 0.015600
但是,当我删除 fk_dictionnaires=1 时。我的索引被正确使用,性能约为 0.000000-0.01XXXXXX 秒
0|0|0|SCAN TABLE mots USING INDEX idx_mots_ponderation (~250000 rows)
我在 stackoverflow 上发现了一些类似的问题,但没有 anwser 帮助我。
如何通过使用索引或/和更改 SQL 查询来提高性能?
提前致谢。
最佳答案
SQLite 似乎认为 idx_dictionnaires
索引非常稀疏并得出结论,如果它使用 idx_dictionnaires
进行扫描,它只需要检查几行。但是,您引用的性能结果表明它必须检查的不仅仅是几行。首先,为什么不试试ANALYZE mots
,所以 SQLite 将有关于每个可用索引的基数的最新信息?
以下是 SQLite 文档中可能有帮助的其他内容:
WHERE 子句的术语可以通过在列名前添加一元 + 运算符来手动取消与索引一起使用的资格。一元 + 是空操作,不会减慢对术语指定的测试的评估。但它会阻止该术语限制索引。因此,在上面的示例中,如果将查询重写为:
SELECT z FROM ex2 WHERE +x=5 AND y=6;
x 列上的 + 运算符将阻止该术语限制索引。这将强制使用 ex2i2 索引。
请注意,一元 + 运算符还会从表达式中删除类型关联,在某些情况下,这可能会导致表达式含义发生细微变化。在上面的示例中,如果列 x 具有 TEXT 亲和性,则比较“x=5”将作为文本进行。但是 + 运算符删除了亲和力。所以比较 "+x=5"会将 x 列中的文本与数值 5 进行比较,并且始终为假。
如
ANALYZE mots
不足以帮助 SQLite 选择要使用的最佳索引,您可以使用此功能强制它使用您想要的索引。你也可以尝试复合索引——看起来你已经在
fk_dictionnaires,mot
上定义了一个,但 SQLite 没有使用它。对于“快速”查询,SQLite 似乎更喜欢使用 ponderation
上的索引。 , 以避免在查询结束时对行进行排序。如果您在 fk_dictionnaires,ponderation DESC
上添加索引,而 SQLite 实际上使用它,它可以挑选出匹配 fk_dictionnaires=1
的行没有表扫描并避免在最后排序。POSTSCRIPT :我上面建议的复合索引“修复”了 OP 的性能问题,但他也询问了它如何以及为什么起作用。 @AGeiser,我将使用一个简短的说明来尝试帮助您直观地理解数据库索引:
想象一下,您需要找到镇上所有姓氏以“A”开头的人。您有一个包含所有名称的目录,但它们的顺序是随机的。你做什么工作?您别无选择,只能通读整个目录,并挑选出以“A”开头的目录。听起来工作量很大,对吧? (这就像一个没有索引的数据库表。)
但是如果有人给你一本电话簿,所有的名字都按字母顺序排列呢?现在您可以找到以“A”开头的第一个和最后一个条目(使用类似二进制搜索的方法),并获取该范围内的所有条目。您甚至不必查看书中的所有其他名称。这会更快。 (这就像一个带有索引的数据库表;在这种情况下,将其称为
last_name,first_name
上的索引。)现在,如果您想要所有名字以“A”开头的人,但在 2 个人的名字相同的情况下,您希望他们按邮政编码排序怎么办?即使您使用“电话簿”(即
last_name,first_name
上的索引)快速获得所需的名称,您仍然必须手动对它们进行排序……因此听起来又像是大量工作。什么可以让这份工作变得真正容易?这将需要另一个“电话簿”——但其中条目首先按名称排序,然后按邮政编码排序。使用这样的“电话簿”,您可以快速选择您需要的条目范围,甚至不需要对它们进行排序——它们已经按照所需的顺序排列了。 (这是
last_name,first_name,postal_code
上的索引。)我认为这个插图应该清楚地说明索引如何帮助 SELECT 查询,不仅通过减少必须检查的行数,而且还通过(可能)消除在找到所需行后对单独的“排序”阶段的需要.希望它也清楚地表明
a,b
上的复合索引与b,a
上的完全不同.我可以继续提供更多“电话簿”示例,但是这个答案会变得很长,以至于它更像是一篇博客文章。为了建立对哪些索引可能对查询有益的直觉,我推荐 O'Reilly 的“SQL Antipatterns”一书(特别是第 13 章,“Index Shotgun”)。
关于sql - 使用索引优化SQLite3上的SQL查询,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11983924/