Inilah run-down: Saya melakukan kueri pemilihan. Setiap kolom dalam klausa WHERE
dan ORDER BY
berada dalam indeks tunggal non-cluster IX_MachineryId_DateRecorded
, baik sebagai bagian dari kunci, atau sebagai INCLUDE
kolom. Saya memilih semua kolom, sehingga akan menghasilkan pencarian bookmark, tapi saya hanya mengambil TOP (1)
, jadi pasti server dapat memberitahu pencarian hanya perlu dilakukan sekali, pada akhirnya.
Yang paling penting, ketika saya memaksakan kueri untuk menggunakan indeks IX_MachineryId_DateRecorded
, itu berjalan dalam waktu kurang dari satu detik. Jika saya membiarkan server memutuskan indeks mana yang akan digunakan, itu mengambil IX_MachineryId
, dan itu memakan waktu hingga satu menit. Itu benar-benar menunjukkan kepada saya bahwa saya telah membuat indeks benar, dan server hanya membuat keputusan yang buruk. Mengapa?
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]);
Tabel dipartisi ke dalam rentang bulan (meskipun saya masih tidak benar-benar mengerti apa yang terjadi di sana).
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)
Kueri yang biasanya saya jalankan:
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
Paket pertanyaan: https://www.brentozar.com/pastetheplan/?id=r1c-RpxNx
Rencana kueri dengan indeks paksa: https://www.brentozar.com/pastetheplan/?id=SywwTagVe
Rencana yang dimasukkan adalah rencana pelaksanaan aktual, tetapi pada basis data pementasan (sekitar 1/100 dari ukuran live). Saya ragu untuk mengutak-atik database hidup karena saya baru mulai di perusahaan ini sekitar sebulan yang lalu.
Saya merasa itu karena partisi, dan permintaan saya biasanya mencakup setiap partisi (misalnya ketika saya ingin mendapatkan yang pertama atau terakhir OperationalSeconds
yang direkam untuk satu mesin). Namun, pertanyaan yang saya tulis sendiri semuanya berjalan dengan baik 10 - 100 kali lebih cepat daripada yang dihasilkan EntityFramework , jadi saya hanya akan membuat prosedur tersimpan.