İşte halsiz: Bir seçme sorgusu yapıyorum. WHERE
Ve ORDER BY
deyimlerindeki her sütun IX_MachineryId_DateRecorded
, anahtarın bir parçası olarak veya INCLUDE
sütunlar olarak tek bir kümelenmemiş dizinde bulunur. Ben seçerek ediyorum bütün bu imi arama sonuçlanacaktır sütunlar, ama sadece alıyorum TOP (1)
, bu yüzden mutlaka sunucu araması yalnızca sonunda, bir kez yapılması gereken söyleyebilir.
En önemlisi, sorguyu dizin kullanmaya zorladığında, IX_MachineryId_DateRecorded
bir saniyeden daha kısa sürede çalışır. Sunucunun hangi dizinin kullanılacağına karar vermesine izin verirsem IX_MachineryId
, alır ve bir dakika kadar sürer. Bu gerçekten indeksi doğru yaptığımı gösteriyor ve sunucu sadece kötü bir karar veriyor. Neden?
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]);
Tablo ay aralıklarına ayrılmıştır (yine de orada neler olduğunu gerçekten anlamıyorum).
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)
Normalde çalıştıracağım sorgu:
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
Sorgu planı: https://www.brentozar.com/pastetheplan/?id=r1c-RpxNx
Zorunlu indeksli sorgu planı: https://www.brentozar.com/pastetheplan/?id=SywwTagVe
Dahil edilen planlar gerçek yürütme planlarıdır, ancak hazırlama veritabanında (canlı boyutunun yaklaşık 1 / 100'ü). Canlı veritabanıyla uğraşmakta tereddüt ediyorum çünkü bu şirkette sadece bir ay önce başladım.
Bu bölümleme nedeniyle bir duygu var ve benim sorgu genellikle her bölüm (örneğin OperationalSeconds
bir makine için şimdiye kadar kaydedilen ilk veya son almak istediğinizde ) yayılır. Ancak, elle yazdığım sorguların hepsi EntityFramework'un ürettiğinden 10 - 100 kat daha iyi çalışıyor , bu yüzden sadece saklı bir prosedür yapacağım.