요약은 다음과 같습니다. 선택 쿼리를 수행하고 있습니다. WHERE
and ORDER BY
절의 모든 열 IX_MachineryId_DateRecorded
은 키의 일부 또는 INCLUDE
열 로서 하나의 비 클러스터형 인덱스 에 있습니다 . 모든 열을 선택 하므로 책갈피 조회가 발생하지만을 가져 오는 TOP (1)
것이므로 서버는 조회를 한 번만 수행해야한다고 알릴 수 있습니다.
가장 중요한 것은 쿼리가 index를 사용하도록 강제하면 IX_MachineryId_DateRecorded
1 초 안에 실행됩니다. 서버가 사용할 인덱스를 결정하게하면을 선택 IX_MachineryId
하고 최대 1 분이 걸립니다. 그것은 내가 인덱스를 올바르게 만들었다는 것을 나에게 제안하고 서버는 잘못된 결정을 내립니다. 왜?
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]);
테이블은 월 범위로 분할되어 있습니다 (그러나 나는 아직도 무슨 일이 일어나고 있는지 이해하지 못합니다).
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)
내가 정상적으로 실행하는 쿼리 :
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
쿼리 계획 : https://www.brentozar.com/pastetheplan/?id=r1c-RpxNx
강제 색인이있는 쿼리 계획 : https://www.brentozar.com/pastetheplan/?id=SywwTagVe
포함 된 계획은 실제 실행 계획이지만 준비 데이터베이스 (실제 크기의 약 1/100)입니다. 약 한 달 전에이 회사에서 시작했기 때문에 라이브 데이터베이스를 다루는 것이 주저합니다.
파티션으로 인한 느낌이 들며, 쿼리는 일반적으로 모든 단일 파티션에 걸쳐 있습니다 (예 : OperationalSeconds
한 머신에 대해 처음 또는 마지막 으로 기록을 원할 때 ). 그러나 필자가 직접 작성 한 쿼리는 EntityFramework 에서 생성 된 것보다 10-100 배 빠릅니다 . 따라서 저장 프로 시저를 만들 것입니다.