SQLite`解释查询计划`没有显示每一步?

fgw7neuy  于 2022-11-15  发布在  SQLite
关注(0)|答案(1)|浏览(161)

这是一些伪SQL,其中的“问题”很容易被复制:

create table Child (
    childId text primary key,
    some_int int not null
);

create table Person (
    personId text primary key,
    childId text,
    foreign key (childId) references Child (childId) on delete cascade
);

create index Person_childId on Person (childId);

explain query plan select count(1)
                   from Person
                            left outer join Child on Child.childId = Person.childId
                   where Person.childId is null or Child.some_int = 0;

查询计划的结果如下:

SCAN Person USING COVERING INDEX Person_childId
SEARCH Child USING INDEX sqlite_autoindex_Child_1 (childId=?) LEFT-JOIN

这看起来很棒,对吧?但我很好奇这是不是“完整”的计划。这是因为some_int没有索引。但查询计划没有发现这一点,我在任何地方都没有看到过滤。数据库必须过滤此字段,对吗?
当我在单独的查询中执行some_int字段时,它显示一个SCAN,这与我在前面的查询计划中看到的完全一样因为没有索引

explain query plan select * from Child where some_int = 0;

提供:

SCAN Child

现在我的问题是:

  • 为什么第一个查询计划中没有显示SCAN Child
  • 为什么Person上有SCAN,而不是SEARCH
  • 第一个查询计划是‘快速’,还是我仍然需要添加索引?
k3bvogb1

k3bvogb11#

您应该看看this page。它深入解释了SQLite查询规划器,您可以找到所有问题的答案。请注意,像WHERE some_int=0这样的过滤条件不会显示在查询计划中,因为它们不会影响计划,而只会影响结果集。
简而言之:
为什么第一个查询计划中没有显示Scan Child?
因为,由于LEFT JOIN的原因,SQLite需要SCAN Person,并且对于每一行Person,使用ChildID上的索引来查找Child中的相应记录。
为什么扫描的是Person,而不是搜索?
SCAN表示按存储顺序读取表中的所有行。SEARCH是在表中查找单个值,使用索引查找行ID,并使用行ID访问表中的该行,而不需要扫描所有TE表来查找该行。因为您的查询需要读取所有Person.childId,所以它会执行完全扫描。
第一个查询计划是“快速”的,还是我仍然需要添加索引?
您的查询已经使用了它可以使用的所有索引,所以它已经是您能得到的最快的了。

相关问题