Hier ist die Übersicht: Ich mache eine Auswahlabfrage. Jede Spalte in den Klauseln WHERE
und ORDER BY
befindet sich in einem einzelnen nicht gruppierten Index IX_MachineryId_DateRecorded
, entweder als Teil des Schlüssels oder als INCLUDE
Spalten. Ich wähle alle Spalten aus, so dass eine Lesezeichensuche durchgeführt wird, aber ich nehme nur TOP (1)
, so dass der Server sicher sagen kann, dass die Suche am Ende nur einmal durchgeführt werden muss.
Vor allem, wenn ich die Abfrage zwinge, den Index zu verwenden, dauert IX_MachineryId_DateRecorded
es weniger als eine Sekunde. Wenn ich den Server entscheiden lasse, welcher Index verwendet werden soll, wählt er IX_MachineryId
und es dauert bis zu einer Minute. Das deutet wirklich darauf hin, dass ich den Index richtig gemacht habe und der Server gerade eine schlechte Entscheidung trifft. Warum?
CREATE TABLE [dbo].[MachineryReading] (
[Id] INT IDENTITY (1, 1) NOT NULL,
[Location] [sys].[geometry] NULL,
[Latitude] FLOAT (53) NOT NULL,
[Longitude] FLOAT (53) NOT NULL,
[Altitude] FLOAT (53) NULL,
[Odometer] INT NULL,
[Speed] FLOAT (53) NULL,
[BatteryLevel] INT NULL,
[PinFlags] BIGINT NOT NULL,
[DateRecorded] DATETIME NOT NULL,
[DateReceived] DATETIME NOT NULL,
[Satellites] INT NOT NULL,
[HDOP] FLOAT (53) NOT NULL,
[MachineryId] INT NOT NULL,
[TrackerId] INT NOT NULL,
[ReportType] NVARCHAR (1) NULL,
[FixStatus] INT DEFAULT ((0)) NOT NULL,
[AlarmStatus] INT DEFAULT ((0)) NOT NULL,
[OperationalSeconds] INT DEFAULT ((0)) NOT NULL,
CONSTRAINT [PK_dbo.MachineryReading] PRIMARY KEY CLUSTERED ([Id] ASC),
CONSTRAINT [FK_dbo.MachineryReading_dbo.Machinery_MachineryId] FOREIGN KEY ([MachineryId]) REFERENCES [dbo].[Machinery] ([Id]) ON DELETE CASCADE,
CONSTRAINT [FK_dbo.MachineryReading_dbo.Tracker_TrackerId] FOREIGN KEY ([TrackerId]) REFERENCES [dbo].[Tracker] ([Id]) ON DELETE CASCADE
);
GO
CREATE NONCLUSTERED INDEX [IX_MachineryId]
ON [dbo].[MachineryReading]([MachineryId] ASC);
GO
CREATE NONCLUSTERED INDEX [IX_TrackerId]
ON [dbo].[MachineryReading]([TrackerId] ASC);
GO
CREATE NONCLUSTERED INDEX [IX_MachineryId_DateRecorded]
ON [dbo].[MachineryReading]([MachineryId] ASC, [DateRecorded] ASC)
INCLUDE([OperationalSeconds], [FixStatus]);
Die Tabelle ist in Monatsbereiche unterteilt (obwohl ich immer noch nicht wirklich verstehe, was dort vor sich geht).
ALTER PARTITION SCHEME PartitionSchemeMonthRange NEXT USED [Primary]
ALTER PARTITION FUNCTION [PartitionFunctionMonthRange]() SPLIT RANGE(N'2016-01-01T00:00:00.000')
ALTER PARTITION SCHEME PartitionSchemeMonthRange NEXT USED [Primary]
ALTER PARTITION FUNCTION [PartitionFunctionMonthRange]() SPLIT RANGE(N'2016-02-01T00:00:00.000')
...
CREATE UNIQUE CLUSTERED INDEX [PK_dbo.MachineryReadingPs] ON MachineryReading(DateRecorded, Id) ON PartitionSchemeMonthRange(DateRecorded)
Die Abfrage, die ich normalerweise ausführen würde:
SELECT TOP (1) [Id], [Location], [Latitude], [Longitude], [Altitude], [Odometer], [ReportType], [FixStatus], [AlarmStatus], [Speed], [BatteryLevel], [PinFlags], [DateRecorded], [DateReceived], [Satellites], [HDOP], [OperationalSeconds], [MachineryId], [TrackerId]
FROM [dbo].[MachineryReading]
--WITH(INDEX(IX_MachineryId_DateRecorded)) --This makes all the difference
WHERE ([MachineryId] = @p__linq__0) AND ([DateRecorded] >= @p__linq__1) AND ([DateRecorded] < @p__linq__2) AND ([OperationalSeconds] > 0)
ORDER BY [DateRecorded] ASC
Abfrageplan: https://www.brentozar.com/pastetheplan/?id=r1c-RpxNx
Abfrageplan mit erzwungenem Index: https://www.brentozar.com/pastetheplan/?id=SywwTagVe
Die enthaltenen Pläne sind die tatsächlichen Ausführungspläne, befinden sich jedoch in der Staging-Datenbank (ungefähr 1/100 der Livegröße). Ich zögere, an der Live-Datenbank herumzuspielen, da ich erst vor einem Monat bei dieser Firma angefangen habe.
Ich habe das Gefühl, es liegt an der Partitionierung, und meine Abfrage erstreckt sich normalerweise über jede einzelne Partition (z. B. wenn ich die erste oder letzte OperationalSeconds
Aufzeichnung für eine Maschine erhalten möchte ). Die Abfragen, die ich von Hand geschrieben habe, werden jedoch alle gut 10 bis 100 Mal schneller ausgeführt als das, was EntityFramework generiert hat. Ich werde also nur eine gespeicherte Prozedur erstellen .