7

我正在尝试通过创建索引来优化 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上发现了一些类似的问题,但没有帮助我。

如何通过使用索引或/和更改 SQL 查询来提高性能?提前致谢。

4

1 回答 1

5

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 进行比较,并且始终为 false。


如果ANALYZE mots不足以帮助 SQLite 选择要使用的最佳索引,您可以使用此功能强制它使用您想要的索引。

您也可以尝试复合索引——看起来您已经在 上定义了一个fk_dictionnaires,mot,但 SQLite 没有使用它。对于“快速”查询,SQLite 似乎更喜欢使用 on 索引ponderation,以避免在查询结束时对行进行排序。如果您在 上添加索引,并且 SQLite 实际使用它,它可以在没有表扫描的情况下fk_dictionnaires,ponderation DESC挑选出匹配的行,避免在最后进行排序。fk_dictionnaires=1


POSTSCRIPT:我在上面建议的复合索引“修复”了 OP 的性能问题,但他也询问了它的工作原理和原因。@AGeiser,我将使用一个简短的说明来帮助您直观地理解数据库索引:

想象一下,您需要找到镇上所有姓氏以“A”开头的人。您有一个包含所有名称的目录,但它们的顺序是随机的。你做什么工作?您别无选择,只能通读整个目录,然后选择以“A”开头的目录。听起来工作量很大,对吧?(这就像一个没有索引的数据库表。)

但是如果有人给你一个电话簿,所有的名字都按字母顺序排列怎么办?现在您可以找到以“A”开头的第一个和最后一个条目(使用类似于二进制搜索的东西),并获取该范围内的所有条目。您甚至不必查看书中的所有其他名称。这会更快。(这就像一个带有索引的数据库表;在这种情况下,将其称为 . 上的索引last_name,first_name。)

现在如果你想要所有名字以“A”开头的人,但是如果有两个人的名字相同,你希望他们按邮政编码排序?即使您使用“电话簿”(即索引last_name,first_name)快速获得所需的名称,您仍然必须手动对它们进行排序......所以听起来又开始做很多工作了。什么能让这份工作变得非常容易?

这将需要另一个“电话簿”——但其中条目首先按名称排序,然后按邮政编码排序。使用这样的“电话簿”,您可以快速选择所需的条目范围,甚至不需要对它们进行排序——它们已经按照所需的顺序排列。(这是关于 的索引last_name,first_name,postal_code。)

我认为这个插图应该清楚地说明索引如何帮助 SELECT 查询,不仅通过减少必须检查的行数,而且通过(可能)消除在找到所需行之后对单独的“排序”阶段的需要. 希望它也清楚地表明复合索引 on 与 ona,b完全不同b,a。我可以继续提供更多“电话簿”示例,但这个答案会变得很长,以至于更像是一篇博客文章。为了让您了解哪些索引可能对查询有益,我推荐 O'Reilly 的《SQL 反模式》一书(尤其是第 13 章,“Index Shotgun”)。

于 2012-08-16T09:44:33.343 回答