部分索引是在表的行的子集上的索引。
在普通索引中,表中的每一行在索引中仅存在一个条目。在部分索引中,表中仅行的某些子集具有相应的索引条目。例如,部分索引可能会省略其索引索引为NULL的条目。如果谨慎使用,部分索引可以导致数据库文件更小,并提高查询和写入性能。
通过在普通CREATE INDEX语句的末尾添加WHERE子句来创建部分索引。
任何在末尾包含WHERE子句的索引都被视为部分索引。省略WHERE子句的索引(或由CREATE TABLE语句内部的UNIQUE或PRIMARY KEY约束创建的索引)是普通的完整索引。
WHERE子句后面的表达式可以包含运算符,文字值和要建立索引的表中的列名称。WHERE子句不能包含子查询,对其他表的引用,不确定性函数或绑定参数。
索引中仅包含WHERE子句的评估结果为true的表中的行。如果对于表的某些行,WHERE子句表达式的计算结果为NULL或false,则将从索引中省略这些行。
部分索引的WHERE子句中引用的列可以是表中的任何列,而不仅仅是碰巧被索引的列。但是,部分索引的WHERE子句表达式在要索引的列上是简单表达式是很常见的。以下是一个典型示例:
在purchaseorder(parent_po)上创建索引po_parent,在其中parent_po不为空;
在上面的示例中,如果大多数采购订单没有“父”采购订单,则大多数parent_po值将为NULL。这意味着将只对采购订单表中的一小部分行建立索引。因此,索引将占用更少的空间。并且由于原始po_parent索引仅需要针对parent_po不为NULL的那些例外行进行更新,因此对原始购买订单表的更改将更快地运行。但是索引对于查询仍然有用。特别是,如果要了解特定采购订单“?1”的所有“子代”,则查询将是:
从采购订单中选择po_num,位置parent_po =?1;
上面的查询将使用po_parent索引来帮助找到答案,因为po_parent索引包含所有感兴趣的行的条目。请注意,由于po_parent小于完整索引,因此查询的运行速度也可能会更快。
部分索引定义可以包含UNIQUE关键字。如果是这样,则SQLite要求索引中的每个条目都是唯一的。这提供了一种在表中的行的某些子集上强制唯一性的机制。
例如,假设您有一个大型组织成员的数据库,其中每个人都被分配到一个特定的“团队”。每个团队都有一个“领导者”,他也是该团队的成员。该表可能如下所示:
创建表人( person_id整数主键, team_id INTEGER参考团队, is_team_leader BOOLEAN, -其他领域被淘汰 );
team_id字段不能唯一,因为同一团队中通常有多个人。因为每个团队通常都有多个非领导者,所以不能使team_id和is_team_leader的组合唯一。强制每个团队一名领导者的解决方案是在team_id上创建唯一索引,但仅限于is_team_leader为true的条目:
在人员(team_id)处创建唯一索引team_leader,在is_team_leader处;
巧合的是,相同的索引对于查找特定团队的团队负责人很有用:
在人员is_team_leader和team_id =?1的人员中选择person_id;
令X为部分索引的WHERE子句中的表达式,令W为使用索引表的查询的WHERE子句。然后,如果W⇒X,则允许查询使用部分索引,其中⇒运算符(通常发音为“ implies”)等于“ X or not W”的逻辑运算符。因此,确定部分索引在特定查询中是否可用减少了证明一阶逻辑的定理。
SQLite的也没有复杂的理论证明,用以确定W⇒X。相反,SQLite使用两个简单的规则来查找W⇒X为true的常见情况,并假定所有其他情况均为false。SQLite使用的规则是:
如果W是与AND相连的项,而X是与OR相连的项,并且W的任何一项作为X的项出现,则部分索引可用。
例如,让索引为
在a = 5或b = 6的tab1(a,b)上创建索引ex1
并将查询设为:
选择*从tab1,其中b = 6和a = 7; -使用部分索引
然后,索引可以被查询使用,因为“ b = 6”项同时出现在索引定义和查询中。请记住:索引中的术语应为“或”连接,而查询中的术语应为“与”连接。
W和X中的术语必须完全匹配。SQLite不会代数尝试使它们看起来相同。术语“ b = 6”与“ b = 3 + 3”或“ b-6 = 0”或“ b在6和6之间不匹配”。只要“ b = 6”在索引上并且“ 6 = b”在查询中,“ b = 6”将与“ 6 = b”匹配。如果索引中出现形式为“ 6 = b”的术语,则它将永远不会匹配任何内容。
如果X中的一项的形式为“ z IS NOT NULL”,并且W中的一项是除“ IS”之外的“ z”上的比较运算符,则这些项匹配。
示例:让索引为
在tab2(b,c)上在c不为空的地方创建索引ex2;
然后,对列“ c”使用运算符=,<,>,<=,> =,<>,IN,LIKE或GLOB的任何查询都可与部分索引一起使用,因为这些比较运算符仅在“ c”为真时才为true不为NULL。因此,以下查询可以使用部分索引:
选择*从tab2那里b = 456和c <> 0; -使用部分索引
但是下一个查询不能使用部分索引:
SELECT * FROM tab2 WHERE b = 456; -无法使用部分索引
后一个查询不能使用部分索引,因为表中可能存在b = 456且c为NULL的行。但是这些行将不在部分索引中。
这两个规则描述了撰写本文时(2013-08-01)的SQLite查询计划程序的工作方式。并且上述规则将始终得到遵守。但是,未来版本的SQLite可能会包含一个更好的定理证明器,它可以找到W⇒X为true的其他情况,从而可以找到更多使用部分索引的实例。
自3.8.0版 (2013-08-26)起,SQLite已支持部分索引。
3.8.0之前的SQLite版本无法读取或写入包含部分索引的数据库文件。但是,由SQLite 3.8.0创建的数据库文件仍可被以前的版本读取和写入,只要其架构不包含部分索引即可。只需在部分索引上运行DROP INDEX,就可以使旧版SQLite无法读取的数据库变得可读 。