Я оптимизировал запрос на SQL Server и столкнулся с тем, чего не ожидал. В базе есть таблица tblEvent
, в ней среди прочих столбцов есть IntegrationEventStateId
и ModifiedDateUtc
. По этим столбцам есть индекс:
create index IX_tblEvent_IntegrationEventStateId_ModifiedDateUtc
on dbo.tblEvent (
IntegrationEventStateId,
ModifiedDateUtc
)
Когда я выполняю следующий оператор:
select *
from dbo.tblEvent e
where
e.IntegrationEventStateId = 1
or e.IntegrationEventStateId = 2
or e.IntegrationEventStateId = 5
or (e.IntegrationEventStateId = 4 and e.ModifiedDateUtc >= dateadd(minute, -5, getutcdate()))
Я получаю этот план выполнения (обратите внимание, что индекс НЕ используется):
Но когда я выполняю этот оператор:
select *
from dbo.tblEvent e
where
1 = e.IntegrationEventStateId
or 2 = e.IntegrationEventStateId
or 5 = e.IntegrationEventStateId
or (4 = e.IntegrationEventStateId and e.ModifiedDateUtc >= dateadd(minute, -5, getutcdate()))
Я получаю этот план выполнения (обратите внимание, что индекс используется):
Единственная разница между этими двумя операторами заключается в порядке сравнений в предложении where
. Кто-нибудь может объяснить, почему я получаю разные планы выполнения?
Обновление 1 — полный сценарий воспроизведения приведен ниже
CREATE TABLE dbo.tblEvent
(
EventId INT IDENTITY PRIMARY KEY,
IntegrationEventStateId INT,
ModifiedDateUtc DATETIME,
OtherCol CHAR(1),
index IX_tblEvent_IntegrationEventStateId_ModifiedDateUtc(IntegrationEventStateId, ModifiedDateUtc)
);
INSERT INTO dbo.tblEvent
SELECT TOP 356525 3,
DATEADD(SECOND, ROW_NUMBER() OVER (ORDER BY @@SPID)%63424, GETUTCDATE()),
'A'
FROM sys.all_objects o1,
sys.all_objects o2;
UPDATE STATISTICS dbo.tblEvent WITH FULLSCAN
select *
from dbo.tblEvent e
where
e.IntegrationEventStateId = 1
or e.IntegrationEventStateId = 2
or e.IntegrationEventStateId = 5
or (e.IntegrationEventStateId = 4 and e.ModifiedDateUtc >= dateadd(minute, -5, getutcdate()))
select *
from dbo.tblEvent e
where
1 = e.IntegrationEventStateId
or 2 = e.IntegrationEventStateId
or 5 = e.IntegrationEventStateId
or (4 = e.IntegrationEventStateId and e.ModifiedDateUtc >= dateadd(minute, -5, getutcdate()))
Обновление 2 – DDL исходной таблицы
CREATE TABLE [dbo].[tblEvent]
(
[EventId] [int] NOT NULL IDENTITY(1, 1),
[EventTypeId] [int] NOT NULL,
[ScorecardId] [int] NULL,
[ScorecardAreaId] [int] NULL,
[AreaId] [int] NULL,
[ScorecardTopicId] [int] NULL,
[TopicId] [int] NULL,
[ScorecardRequirementId] [int] NULL,
[RequirementId] [int] NULL,
[DocumentId] [int] NULL,
[FileId] [int] NULL,
[TopicTitle] [nvarchar] (100) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
[ScorecardTopicStatus] [nvarchar] (255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
[RequirementText] [nvarchar] (500) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
[ScorecardRequirementStatus] [nvarchar] (255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
[DocumentName] [nvarchar] (260) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
[CreatedByUserId] [int] NOT NULL,
[CreatedByUserSessionId] [int] NOT NULL,
[CreatedDateUtc] [datetime2] (4) NOT NULL CONSTRAINT [DF__tblEvent__Create__0737E4A2] DEFAULT (sysutcdatetime()),
[CreatedDateLocal] [datetime2] (4) NOT NULL CONSTRAINT [DF__tblEvent__Create__082C08DB] DEFAULT (sysdatetime()),
[ModifiedByUserId] [int] NOT NULL,
[ModifiedByUserSessionId] [int] NOT NULL,
[ModifiedDateUtc] [datetime2] (4) NOT NULL CONSTRAINT [DF__tblEvent__Modifi__09202D14] DEFAULT (sysutcdatetime()),
[ModifiedDateLocal] [datetime2] (4) NOT NULL CONSTRAINT [DF__tblEvent__Modifi__0A14514D] DEFAULT (sysdatetime()),
[IsDeleted] [bit] NOT NULL,
[RowVersion] [timestamp] NOT NULL,
[ScorecardRequirementPriority] [nvarchar] (255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
[AffectedUserId] [int] NULL,
[UserId] [int] NULL,
[CorrelationId] [nvarchar] (255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
[IntegrationEventStateId] [int] NULL,
[IntegrationEventId] [nvarchar] (255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
[IntegrationEventContent] [nvarchar] (max) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
[IntegrationEventType] [nvarchar] (255) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
[IntegrationEventTryCount] [int] NULL
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
ALTER TABLE [dbo].[tblEvent] ADD CONSTRAINT [PK_dbo.tblEvent] PRIMARY KEY CLUSTERED ([EventId]) ON [PRIMARY]
GO
CREATE NONCLUSTERED INDEX [IX_tblEvent_IntegrationEventStateId_ModifiedDateUtc] ON [dbo].[tblEvent] ([IntegrationEventStateId], [ModifiedDateUtc]) ON [PRIMARY]
GO
ALTER TABLE [dbo].[tblEvent] ADD CONSTRAINT [FK_dbo.tblEvent_dbo.tblEventType_EventTypeId] FOREIGN KEY ([EventTypeId]) REFERENCES [dbo].[tblEventType] ([EventTypeId])
GO
ALTER TABLE [dbo].[tblEvent] ADD CONSTRAINT [FK_dbo.tblEvent_dbo.tblIntegrationEventState_IntegrationEventStateId] FOREIGN KEY ([IntegrationEventStateId]) REFERENCES [dbo].[tblIntegrationEventState] ([IntegrationEventStateId])
GO
Microsoft SQL Server 2016 (SP1-CU5) (KB4040714) - 13.0.4451.0 (X64) Sep 5 2017 16:12:34 Copyright (c) Microsoft Corporation Standard Edition (64-bit) on Windows Server 2012 R2 Standard 6.3 <X64> (Build 9600: ) (Hypervisor)
. Уровень совместимости БД:SQL Server 2016 (130)
. Поведение повторяется, я запускал запросы несколько раз и постоянно получал вышеуказанные планы выполнения. - person Andrew   schedule 28.03.2020where 1=1
к первому плану? - person Nick.McDermaid   schedule 28.03.2020StatementOptmEarlyAbortReason="GoodEnoughPlanFound"
, а в первый нет. Все еще смотрит. - person AlwaysLearning   schedule 28.03.2020DBCC FREEPROCCACHE
заранее? (Кстати, я предполагаю, что это не рабочий сервер.) - person AlwaysLearning   schedule 28.03.2020and 1=1
и запросы с добавлениемor 1=0
, выполняяDBCC FREEPROCCACHE
каждый раз перед выполнением запроса. Никаких изменений в исполнительных планах. - person Andrew   schedule 28.03.2020356,525
строки будут соответствовать предикату, то есть 100% строк в таблице. Вероятно, поэтому он не выполняет план с поиском + поиск. Непонятно, почему это оценивается, когда оценки индивидуальных запросов составляют4
во втором плане. - person Martin Smith   schedule 28.03.2020IX_tblEvent_IntegrationEventStateId_ModifiedDateUtc
? Я могу воспроизвести плохие оценки, если вставлю 356 525 строк, каждая из которых имеет3
вместоIntegrationEventStateId
, но это относится к обоим запросам. - person Martin Smith   schedule 28.03.2020[ModifiedDateUtc] [datetime2] (4) NOT NULL CONSTRAINT [DF__tblEvent__Modifi__09202D14] DEFAULT (sysutcdatetime())
- person Andrew   schedule 29.03.2020dbcc freeproccache
, чтобы убедиться, что я по-прежнему получаю те же планы exec со статистикой, которую публикую. - person Andrew   schedule 29.03.2020datetime2(4)
, это не имело никакого значения. - person Andrew   schedule 29.03.2020