【问题标题】:there must be some index scheme to make this work必须有一些索引方案才能使这项工作
【发布时间】:2013-03-06 22:00:17
【问题描述】:

Sqlite 有一个限制,即每次查询只能使用一个索引。这个限制目前正在困扰着我,但我需要 Sqlite,因为我不知道有任何其他本地数据库引擎可以竞争插入速度(尽管我愿意接受建议)。

我有一个包含一百万到一千万行的简单表格(以及其他表格):

CREATE TABLE [Events] (
  [Id] INTEGER PRIMARY KEY, 
  [TelemetryId] INTEGER NOT NULL, 
  [TimestampTicks] INTEGER NOT NULL, 
  [Value] TEXT NOT NULL)

查看我的数据,我有大约 2000 个唯一 TelemetryId 值和每个唯一 TelemetryId 大约 25000 行。我一直在使用这个索引:

CREATE INDEX [IX_Events_TimestampTicks_TelemetryId] 
  ON [Events] ([TimestampTicks], [TelemetryId])

但是,该索引在我没有传入 TimestampTicks 约束的查询中失败(显然)。该索引是在我尝试了 TimestampTicks 和 TelemetryId 上的单个索引之后。根据我的测试,甚至在运行ANALYZE 之后,Sqlite 只会在引用 TelemetryId 时使用索引——这在我限制为时间戳范围的查询中是错误的。如果我颠倒组合索引中列的顺序,我之前快速的查询就会变慢。

这是我的查询的完整列表。你能看到一个适用于所有这些的索引方案吗?

INSERT INTO Events (TelemetryId, TimestampTicks, Value) 
  VALUES(@TelemetryId, @TimestampTicks, @Value); SELECT last_insert_rowid()

SELECT * FROM Events e 
  INNER JOIN Telemetry ss ON ss.Id = e.TelemetryId 
  INNER JOIN Services s ON s.Id = ss.ServiceId 
  WHERE s.AssetId = @AssetId AND e.TimestampTicks >= @StartTime 
  ORDER BY e.TimestampTicks LIMIT 10000

SELECT * FROM Events e 
  WHERE e.TimestampTicks >= @StartTime 
  ORDER BY e.TimestampTicks LIMIT 10000

SELECT * FROM Events 
  WHERE TelemetryId = @TelemetryId AND TimestampTicks <= @TimestampTicks 
  ORDER BY TimestampTicks DESC LIMIT 1

SELECT MIN(TimestampTicks) FROM Events
SELECT MAX(TimestampTicks) FROM Events
SELECT COUNT(*) FROM Events

SELECT TimestampTicks, [Value] FROM Events 
  WHERE TelemetryId = @TelemetryId

SELECT Id FROM Events 
  WHERE TelemetryId = @TelemetryId LIMIT 2

SELECT MIN(e.TimestampTicks) FROM Events e 
  INNER JOIN Telemetry ss ON ss.ID = e.TelemetryID 
  INNER JOIN Services s ON s.ID = ss.ServiceID 
  WHERE s.AssetID = @AssetId

SELECT MAX(e.TimestampTicks) FROM Events e 
  INNER JOIN Telemetry ss ON ss.ID = e.TelemetryID 
  INNER JOIN Services s ON s.ID = ss.ServiceID 
  WHERE s.AssetID = @AssetId

SELECT * FROM Events 
  WHERE TimestampTicks <= @TimestampTicks AND TelemetryId = @TelemetryId 
  ORDER BY TimestampTicks DESC LIMIT 1

SELECT e.Id, e.TelemetryId, e.TimestampTicks, e.Value 
  FROM (SELECT e2.Id AS [Id], MIN(e2.TimestampTicks) as [TimestampTicks]
        FROM Events e2 WHERE e2.TimestampTicks 
            BETWEEN @Min AND @Max AND e2.TelemetryId in @TelemetryIds                                          
            GROUP BY e2.TelemetryId) AS grp
  INNER JOIN Events e ON grp.Id = e.Id

【问题讨论】:

  • 我建议在 AssetID、TelemetryID 和 TimeStampTicks 上使用简单的非复合索引。

标签: sql sqlite query-optimization database-indexes


【解决方案1】:

没有人阻止您创建多个索引 - 每个索引都可以帮助处理某些查询。

如果我是你,我会创建至少以下两个索引:

CREATE INDEX events_1_ix ON Events(TimestampTicks,TelemetryId);

(您一直在使用的),以及

CREATE INDEX events_2_ix ON Events(TelemetryId);

SQLite 可以在以下情况下使用这些索引:

  1. 在提供TimestampTicksTelemetryId 时进行搜索(第一个索引)
  2. 仅在提供TimestampTicks 时搜索(也是第一个索引)
  3. 仅在提供TelemetryId 时搜索(第二个索引)

如果您只为TimestampTicksTelemetryId 创建单独的索引,这将使选项2 和3 保持快速,但选项1 将不可用。

您可以根据需要创建任意数量的索引,但请记住,索引维护并不是免费的。首先,它会占用更多磁盘空间——索引占用表大小的 10%-30% 并不少见。因此,如果创建太多索引,它们的总大小可能会超过可用表的大小。此外,当有许多索引时,插入或更新速度会比没有它们时慢得多。

关于您原来的说法,即 SQLite 只能每个查询使用一个索引 - 这并不完全正确。

正确的说法是 SQLite 只能在给定查询中每个表使用一个索引。如果您的 SQL 连接了多个表,则每个表都可以使用能够提供最佳性能的索引来访问该表。

【讨论】:

    猜你喜欢
    • 2019-05-31
    • 2013-01-20
    • 2021-02-24
    • 2023-03-31
    • 1970-01-01
    • 2020-06-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多