sql - 使用索引优化SQLite3上的SQL查询

标签 sql performance optimization indexing

我试图通过创建索引来优化 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 帮助我。
  • Removing a Temporary B Tree Sort from a SQLite Query
  • Similar issue

  • 如何通过使用索引或/和更改 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/

    相关文章:

    sql - 表类型存储在 sql server 中的什么位置?我们如何改变它?

    sql - 使用 FOR XML PATH 时如何删除嵌套查询中的冗余 namespace

    javascript - 所有参数的总和(有些参数是数组);总和( [1,2,3,4], 50, 10, [10, 20],1 )

    c++ - 在内存中处理一些长 vector 时如何不弄乱缓存?

    python - 如何根据数据框中的共置约束设置列值?

    c# - 在 C# 中根据用户输入条件编写动态 sql 查询

    sql - ruby 新手,很难理解码合的 ruby​​/sql 语法(这也很难用谷歌搜索)

    java - 对两个 RxJava 可观察对象(Android)进行基准测试?

    performance - Big O 无缓冲区链表去重速度

    c++ - 嵌套的 std::transform 效率低吗?