当Field是表的主键时,SQLite“MyTable(Id)上的自动索引”

时间:2016-08-24 16:12:08

标签: c# sql sqlite

我有一个复杂的C#程序,它使用动态构建的查询从SQLite数据库中读取。 我注意到当我在调试器下运行程序时,我得到了很多输出,如:

SQLite warning (284): automatic index on MyTable(Id)

我查看了MyTable的架构,并将Id指定为主键,如下所示:

CREATE TABLE MyTable (Id varchar(50) collate nocase primary key not null,
Name varchar(50) not null, 
(etc)

我认为SQLite无论如何都为主键创建了索引,为什么还要制作另一个?

另外,在相关的说明中,我收到了很多关于子查询的自动索引警告。例如,在查询上:

SELECT MyTable.Id, MyTable.Name, Amount
FROM MyTable
LEFT JOIN (SELECT ArrangementId, Amount, AgreementDate FROM SubTable 
JOIN Organisations ON Organisations.Id = SubTable.OrganisationId AND Organisations.Direction = 1
) AS MyJoin ON MyJoin.ArrangementId = MyTable.Id
ORDER BY Id

其中

MyTable has Id as the primary key
Organisations has Id as the primary key
SubTable has a unique index on ArrangementId, OrganisationId

查询上的EXPLAIN QUERY PLAN产生:

1|0|0|SCAN TABLE SubTable
1|1|1|SEARCH TABLE Organisations USING INDEX sqlite_autoindex_Organisations_1 (Id=?)
0|0|0|SCAN TABLE Arrangements USING INDEX sqlite_autoindex_Arrangements_1
0|1|1|SEARCH SUBQUERY 1 AS MyJoin USING AUTOMATIC COVERING INDEX (ArrangementId=?)

我猜SQLite不够聪明,不知道子查询不需要进入临时表吗?

有没有办法重写查询以避免子查询?

2 个答案:

答案 0 :(得分:1)

collate nocase列会产生collate nocase索引。如果查找不使用相同的排序规则,则无法使用该索引。 (默认情况下,与该列的比较使用nocase,但当比较针对具有不同排序规则的其他列时,这无效。)

如果此查询很重要,请考虑使用正确的排序规则创建第二个索引。

在第二个查询中,数据库必须使用临时表评估子查询,因为它是左外连接(rule 3)的右操作数。

如果你确定含义保持不变,你可以尝试将查询重写为一系列简单连接:

FROM MyTable
LEFT JOIN SubTable ON MyTable.Id = SubTable.ArrangementId
LEFT JOIN Organisations ON Organisations.Id = SubTable.OrganisationId
                       AND Organisations.Direction = 1

答案 1 :(得分:0)

尝试进行以下更改:

  • 使ID成为整数主键。这实际上是内部rowid的别名,而不是单独的列。
  • 使您当前的ID(varchar列)成为一个单独的列。 (可选)添加唯一约束,如果这很重要的话。
  • 在子表中,使用整数ID而不是varchar列。此外,在列上添加外键。

一些注意事项:

  • MyTable中使用INTEGER PRIMARY KEY并将varchar移动到具有唯一约束的单独列,这对MyTable的大小没有影响。如上所述,主键列将是内部rowid列的别名,因此您并不真正添加任何新列。
  • 通过将子表切换为使用整数作为外键,这将导致更小的表。即使添加了外键约束,您的数据库也会比现在小。这也应该使您的连接更快(对于大型数据集,无论如何)。