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()))
私はこの実行計画を取得します(インデックスが使用されることに注意してください):
2つのステートメントの唯一の違いは、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
これは興味深い珍しいことであり、QOがインデックスの4回のシークがこれらのクエリの1つだけでのスキャンよりも安くなることを認識している理由はわかりません。
奇妙な計画を立てるとき、最良の解決策はインデックスとクエリを変更することです。
おそらく何かのような
CREATE TABLE dbo.tblEvent
(
EventId INT IDENTITY PRIMARY KEY,
IntegrationEventStateId INT,
ModifiedDateUtc DATETIME,
OtherCol CHAR(1),
index IX_tblEvent_IntegrationEventStateId_ModifiedDateUtc(IntegrationEventStateId, ModifiedDateUtc)
include (OtherCol)
where IntegrationEventStateId in (1,2,4,5)
);
その後
select *
from dbo.tblEvent e
where
e.IntegrationEventStateId in (1,2,4,5)
and (e.IntegrationEventStateId <> 4 or e.ModifiedDateUtc >= dateadd(minute, -5, getutcdate()))