0

Sqlite には、クエリごとに 1 つのインデックスしか使用しないという制限があります。現時点ではその制限に悩まされていますが、挿入速度を競うことができる他のローカル DB エンジンを認識していないため、Sqlite が必要です (ただし、提案は受け付けています)。

100万から1000万行のこの単純なテーブル(とりわけ)があります:

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

私のデータを見ると、一意の TelemetryId 値が約 2000 あり、一意の TelemetryId ごとに約 25000 行あります。私はこのインデックスを使用しています:

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

ただし、そのインデックスは、(明らかに) TimestampTicks 制約を渡さないクエリで失敗します。そのインデックスは、TimestampTicks と TelemetryId で個々のインデックスを試行した後のものです。私のテストから、そして を実行した後でもANALYZE、Sqlite は TelemetryId が参照されるたびにそのインデックスしか使用しませんでした。これは、Timestamp 範囲に制限しているクエリでは間違っています。コンボ インデックスの列の順序を逆にすると、以前は高速だったクエリが遅くなります。

これが私のクエリの完全なリストです。それらすべてで機能する索引付けスキームがわかりますか?

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
4

1 に答える 1

1

複数のインデックスを作成することを誰も止めません。各インデックスは特定のクエリに役立ちます。

私があなたなら、少なくとも次の 2 つのインデックスを作成します。

CREATE INDEX events_1_ix ON Events(TimestampTicks,TelemetryId);

(あなたが使用しているもの)、および

CREATE INDEX events_2_ix ON Events(TelemetryId);

SQLite は、次の状況でこれらのインデックスを利用できます。

  1. TimestampTicksいつ、提供されているかを検索TelemetryId(1 番目のインデックス)
  2. のみが提供されている場合に検索TimestampTicks(1 番目のインデックスも)
  3. のみが提供されている場合に検索TelemetryId(2 番目のインデックス)

と に個別のインデックスのみを作成するTimestampTicksTelemetryId、オプション 2 と 3 は高速に保たれますが、オプション 1 は利用できなくなります。

インデックスはいくつでも作成できますが、インデックスのメンテナンスは無料ではないことに注意してください。まず、より多くのディスク容量が必要になります。インデックスがテーブル サイズの 10% ~ 30% を占めることは珍しくありません。そのため、作成するインデックスが多すぎると、それらの合計サイズが使用可能なテーブル サイズを超える可能性があります。また、多数のインデックスがある場合、挿入または更新の速度はインデックスがない場合よりも大幅に遅くなる可能性があります。

SQLite はクエリごとに 1 つのインデックスしか使用できないという元のステートメントについては、これは正しくありません。

正しいステートメントは、SQLite は特定のクエリでテーブルごとに 1 つのインデックスしか使用できないということです。SQL が複数のテーブルを結合する場合、各テーブルは、そのテーブルへのアクセスに最適なパフォーマンスを提供するインデックスを利用できます。

于 2013-03-07T04:44:02.070 に答える