この question はSOからのものであり、フラグが立てられてDBA.SEに移動します。
以下はテストデータです:
--Main Table
CREATE TABLE [dbo].[LogTable]
(
[LogID] [int] NOT NULL
IDENTITY(1, 1) ,
[DateSent] [datetime] NULL,
)
ON [PRIMARY]
GO
ALTER TABLE [dbo].[LogTable] ADD CONSTRAINT [PK_LogTable] PRIMARY KEY CLUSTERED ([LogID]) ON [PRIMARY]
GO
CREATE NONCLUSTERED INDEX [IX_LogTable_DateSent] ON [dbo].[LogTable] ([DateSent] DESC) ON [PRIMARY]
GO
CREATE NONCLUSTERED INDEX [IX_LogTable_DateSent_LogID] ON [dbo].[LogTable] ([DateSent] DESC) INCLUDE ([LogID]) ON [PRIMARY]
GO
--Cross table
CREATE TABLE [dbo].[LogTable_Cross]
(
[LogID] [int] NOT NULL ,
[UserID] [int] NOT NULL
)
ON [PRIMARY]
GO
ALTER TABLE [dbo].[LogTable_Cross] WITH NOCHECK ADD CONSTRAINT [FK_LogTable_Cross_LogTable] FOREIGN KEY ([LogID]) REFERENCES [dbo].[LogTable] ([LogID])
GO
CREATE NONCLUSTERED INDEX [IX_LogTable_Cross_UserID_LogID]
ON [dbo].[LogTable_Cross] ([UserID])
INCLUDE ([LogID])
GO
-- Script to populate them
INSERT INTO [LogTable]
SELECT TOP 100000
DATEADD(day, ( ABS(CHECKSUM(NEWID())) % 65530 ), 0)
FROM sys.sysobjects
CROSS JOIN sys.all_columns
INSERT INTO [LogTable_Cross]
SELECT [LogID] ,
1
FROM [LogTable]
ORDER BY NEWID()
INSERT INTO [LogTable_Cross]
SELECT [LogID] ,
2
FROM [LogTable]
ORDER BY NEWID()
INSERT INTO [LogTable_Cross]
SELECT [LogID] ,
3
FROM [LogTable]
ORDER BY NEWID()
GO
以下の簡単なクエリを使用すると、
SELECT DI.LogID
FROM LogTable DI
INNER JOIN LogTable_Cross DP ON DP.LogID = DI.LogID
WHERE DP.UserID = 1
ORDER BY DateSent DESC
クエリは並べ替えを示します
私が理解しているように、必要なインデックスがあるので、ソートのコストは回避する必要があります
CREATE NONCLUSTERED INDEX [IX_LogTable_DateSent] ON [dbo].[LogTable] ([DateSent] DESC) ON [PRIMARY]
また、プランを掘り下げると、同じインデックスが使用されていることがわかります。
私の質問は:
1。なぜソートコストがまだ存在するのか
2。正確には何をするのかordered property is true
手段 。
私が行ったいくつかの観察/作業:
1.Orderedプロパティがfalseに設定されています
だから私は以下のようなクエリを書き直しました
SELECT DI.LogID
FROM LogTable DI
INNER JOIN LogTable_Cross DP ON DP.LogID = DI.LogID
WHERE DP.UserID = 1 and di.datesent is not null
ORDER BY DateSent DESC
上記のように書き換えると、orderedプロパティがtrueになりますが、それでもソートが存在します
最も近い、私が見つけることができるポールホワイトによるこの記事: 2つのインデックスヒントの物語 、この記事では、以下に、ポイントがこの種類が発生する理由を明らかに
非常に大きなテーブルの場合、オプティマイザは、IAM駆動型スキャンが余分な並べ替えで消費されるよりも多くの時間を節約できる可能性があると計算し、順不同スキャン+並べ替えを特徴とするプランが選択されます。これはヒューリスティックな最適化です。オプティマイザはindeの実際の断片化レベルについて何も知りません。
しかし、このクエリは並べ替えを必要とし、IAMスキャンの条件を満たさないはずです
詳細が必要な場合はお知らせください
クエリプランでは、orderedプロパティをtrueに設定すると、IAM駆動型スキャンが実行されなかったことを意味します。データは、インデックス定義に従って論理的な順序で読み取られました。
ハッシュ結合は順序を保持しません 。最初のクエリにはハッシュ結合があるため、最後に明示的なSORT
が必要です。クエリでの並べ替えを回避する1つの方法は、LogTable
を外部テーブルとしてネストしたループ結合を行うことです。私のマシンでは、さまざまなヒントを使ってこれを実現できました。
SELECT DI.LogID
FROM LogTable DI
INNER JOIN LogTable_Cross DP ON DP.LogID = DI.LogID
WHERE DP.UserID = 1
ORDER BY DateSent DESC
OPTION (MAXDOP 1, LOOP JOIN, FORCE ORDER, NO_PERFORMANCE_SPOOL);
正しいインデックスがLogTable_Cross
テーブルで定義されていないため、これは非常に非効率的なクエリプランです。シークはそのテーブルに対して行われますが、UserId列は1つだけです。
LogID
のフィルタリングは、結合自体で行われます。探しているクエリプランをより適切にサポートするインデックスを作成できます。
CREATE INDEX IX_LogTable_Cross ON LogTable_Cross (LogID, UserID);
そのインデックスの作成は、ソートなしのクエリプランを保証するものではありません。 SQL Serverでは、並べ替えのあるプランの方がコストが低いと推定される場合があります。ただし、LOOP JOIN
以外のすべてのヒントを削除すると、並べ替えなしでかなり効率的なクエリプランが得られます。