通常,SQL索引引用表的列。但是,也可以在涉及表列的表达式上形成索引。
例如,请考虑下表,该表跟踪各种“帐户”上的美元金额变化:
创建表account_change( chng_id整数主键, acct_no INTEGER REFERENCES帐户, location整数参考位置, amt INTEGER,以美分为单位 授权TEXT, 评论TEXT ); CREATE INDEX acctchng_magnitude ON account_change(acct_no,abs(amt));
account_change表中的每个条目都记录了向帐户中的存款或提款。存款有一个正的“金额”,而提款有一个负的“金额”。
acctchng_magnitude索引位于帐号(“ acct_no”)之上,且位于金额的绝对值上。该索引允许人们对帐户变更幅度进行有效的查询。例如,要列出对帐户$ xyz的所有超过$ 100.00的更改,您可以说:
选择* FROM account_change其中acct_no = $ xyz并且abs(amt)> = 10000;
或者,要以降幅顺序列出对一个特定帐户($ xyz)的所有更改,则可以这样写:
SELECT * FROM account_change WHERE acct_no = $ xyz 按Abs(amt)DESC排序;
上面的两个示例查询在没有acctchng_magnitude索引的情况下都可以正常工作。acctchng_magnitude索引索引仅帮助查询更快地运行,尤其是在每个帐户的表中有许多条目的数据库上。
使用CREATE INDEX语句在一个或多个表达式上创建新索引,就像在列上创建索引一样。唯一的区别是表达式被列为要索引的元素,而不是列名。
当被索引的表达式出现在查询的WHERE子句或ORDER BY子句中时,SQLite查询计划人员将考虑在表达式上使用索引,这与在CREATE INDEX语句中编写的完全相同。查询计划程序不执行代数。为了使WHERE子句约束和ORDER BY项与索引匹配,SQLite要求表达式相同,除了较小的语法差异(如空格更改)。因此,如果您有:
创建表t2(x,y,z); 在t2(x + y)上创建索引t2xy;
然后运行查询:
选择*从t2在y + x = 22;
然后将不使用索引,因为CREATE INDEX语句(x + y)上的表达式与查询(y + x)中出现的表达式不同。这两个表达式在数学上可能是等效的,但是SQLite查询计划程序坚持认为它们是相同的,而不仅仅是等效的。考虑这样重写查询:
选择*从t2那里x + y = 22;
第二个查询可能会使用索引,因为现在WHERE子句(x + y)中的表达式与索引中的表达式完全匹配。
对出现在CREATE INDEX语句中的表达式有一些合理的限制:
CREATE INDEX语句中的表达式只能引用要建立索引的表的列,而不能引用其他表中的列。
CREATE INDEX语句中的表达式可以包含函数调用,但只能用于其输出始终完全由其输入参数确定的函数(又称 确定性函数)。显然,像random()这样的函数在索引中不能很好地工作。但是像sqlite_version()这样的函数,尽管它们在任何一个数据库连接中都是恒定的,但在基础数据库文件的生命周期中却不是恒定的,因此可能无法在CREATE INDEX语句中使用。
请注意,默认情况下,应用程序定义的SQL函数被认为是不确定的,除非在注册函数时使用SQLITE_DETERMINISTIC标志,否则不得在CREATE INDEX语句中使用该函数。
CREATE INDEX语句中的表达式不能使用子查询。
表达式只能在CREATE INDEX语句中使用,而不能在 CREATE TABLE语句的UNIQUE或PRIMARY KEY约束中使用。
索引表达式的功能已添加到版本3.9.0(2015-10-14)的SQLite中 。在早期版本的SQLite上将无法使用在表达式上使用索引的数据库。