A. *_*ser 7 sql indexing optimization performance
我正在尝试通过创建索引来优化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)
);
Run Code Online (Sandbox Code Playgroud)
索引定义
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);
Run Code Online (Sandbox Code Playgroud)
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;
Run Code Online (Sandbox Code Playgroud)
查询计划
0|0|0|SEARCH TABLE mots USING INDEX idx_dictionnaires (fk_dictionnaires=?) (~2 rows)
0|0|0|USE TEMP B-TREE FOR ORDER BY
Run Code Online (Sandbox Code Playgroud)
似乎没有使用定义的索引,查询持续(根据.timer):
CPU Time: user 0.078001 sys 0.015600
Run Code Online (Sandbox Code Playgroud)
但是,当我删除fk_dictionnaires = 1时.我的索引使用正确,性能大约为0.000000-0.01XXXXXX秒
0|0|0|SCAN TABLE mots USING INDEX idx_mots_ponderation (~250000 rows)
Run Code Online (Sandbox Code Playgroud)
我在stackoverflow上发现了一些类似的问题,但没有anwser帮助我.
如何通过使用索引或/和更改SQL查询来提高性能?提前致谢.
SQLite似乎认为idx_dictionnaires索引非常稀疏,并得出结论,如果它使用扫描idx_dictionnaires,它只需要检查几行.但是,您引用的性能结果表明它必须检查的不仅仅是几行.首先,为什么不尝试ANALYZE mots,所以SQLite会有关于每个索引的基数的最新信息?
以下是SQLite文档中可能有用的其他内容:
通过在列名称前加上一元+运算符,可以手动取消WHERE子句的条款以与索引一起使用.一元+是无操作,不会减慢对该术语指定的测试的评估.但它会阻止该术语限制索引.因此,在上面的示例中,如果查询被重写为:
SELECT z FROM ex2 WHERE +x=5 AND y=6;
Run Code Online (Sandbox Code Playgroud)
x列上的+运算符将阻止该术语约束索引.这将强制使用ex2i2索引.
请注意,一元+运算符还会从表达式中删除类型相关性,在某些情况下,这会导致表达式含义的细微变化.在上面的示例中,如果列x具有TEXT亲和性,则比较"x = 5"将作为文本完成.但+运算符会删除亲和力.因此,比较"+ x = 5"将比较x列中的文本与数值5,并且始终为false.
如果ANALYZE mots不足以帮助SQLite选择要使用的最佳索引,则可以使用此功能强制它使用所需的索引.
您也可以尝试复合索引 - 看起来您已经定义了一个fk_dictionnaires,mot,但SQLite没有使用它.对于"快速"查询,SQLite似乎更喜欢使用索引ponderation,以避免在查询结束时对行进行排序.如果你添加一个索引fk_dictionnaires,ponderation DESC,并且SQLite实际上使用它,它可以选择匹配的行fk_dictionnaires=1而不进行表扫描,并避免在最后进行排序.
POSTSCRIPT:我上面建议的复合指数"修复"了OP的性能问题,但他也询问了它的工作原理和原因.@AGeiser,我将使用简短的插图来帮助您直观地理解数据库索引:
想象一下,你需要找到你镇上姓氏以"A"开头的所有人.您有一个包含所有名称的目录,但它们是随机排列的.你是做什么?你别无选择,只能阅读整个目录,并挑选出以"A"开头的目录.听起来很多工作,对吧?(这就像没有索引的数据库表.)
但是,如果有人给你一本电话簿,所有的名字都按字母顺序排列呢?现在,您可以找到以"A"开头的第一个和最后一个条目(使用二进制搜索之类的东西),并获取该范围内的所有条目.您甚至不必查看本书中的所有其他名称.这将是方式更快.(这就像带有索引的DB表;在这种情况下,将其称为索引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").
| 归档时间: |
|
| 查看次数: |
4068 次 |
| 最近记录: |