以下のようなクエリがあります。
Declare @CompanyID as int = 10;
Select Top 50 UserID, AuditTypeID , [Action], ActionDate , [Description] ,
RecordID, S.ID as SiteUserID from AuditTrial A
inner Join SiteUser S on S.ID = A.UserID
where A.CompanyID = @CompanyID And A.AuditTypeID <> 1
And
UserID in
(
1,2,3 -- will be a subquery - will have upto 100+ rows.
)
Order by ID desc -- Latest rows
これは少し大きなクエリの一部であり、いくつかの追加のおよび条件があり、一時的に削除したため、このストリップされたクエリの実行速度が遅くなります。
主なものは、上位50、IDによる注文最新、およびユーザーIDの条件。
テーブルには100万を超える行が含まれています。 UserID-100〜20000の範囲で指定できます。
自動生成の主キーがあった
UserID、CompanyIDなどのインデックス
条件の行数が少ないUserIDは1秒未満で十分に実行されますが、ユーザーの数が増えると、より長い時間、つまり10秒を超える時間がかかります。
SqlBulkCopyを使用して数千の行がこのテーブルに追加されることがあり、通常の通常の挿入が一般的です。
DBAではない-クラスタ化/非クラスタ化インデックスについてはあまり意識していません。
どんな助けでもありがたいです。
すでにインデックスを作成している主キーの他に、次のようなものがあります。
UserID、CompanyID、PermissionIDなどの列で使用されるすべてのin列。 複数の列を持つ非クラスター化インデックスはありませんでした
CREATE NONCLUSTERED INDEX [IX_AuditTrial_1] ON [dbo].[AuditTrial]
(
[UserID] ASC
)
、In '節で50個の値をコンマで区切ってUserIDをハードコーディングすると、サブクエリが即座に実行されます。
フルライブクエリ
declare @SiteUserID as int = 6484,
@CompanyID as int = 34;
DECLARE @ColleageUsers TABLE(
ID int
)
Insert into @ColleageUsers
Select SUB.SiteUserID from SiteUserBroker SUB (nolock)
inner join (Select Count(*) as TotalBrokers , SiteUserID from SiteUserBroker group by SiteUserID)Nested on Sub.SiteUserID = Nested.SiteUserID
where BrokerID in (
Select BrokerID from SiteUserBroker (nolock) where SiteUserID = @SiteUserID)
group by SUB.SiteUserID
Having Count(*) - MIN(TotalBrokers) >= 0
Select Top 50 UserID, AuditTypeID , [Action], ActionDate , [Description] , RecordID, ActionedBy = S.FirstName + ' ' + S.LastName , S.ID as SiteUserID from AuditTrial (nolock) A
inner Join SiteUser (nolock) S on S.ID = A.UserID
where A.CompanyID = @CompanyID And A.AuditTypeID 1 -- 1 Means Audit Type Login
And
(
184 = (Select Distinct Top 1 PermissionID from PermissionGroup (nolock) where GroupID in
(Select GroupID from SiteUserGroup (nolock) where SiteUserID = @SiteUserID) and PermissionID = 184)
OR
A.AuditTypeID 20 - Not equal to
)
And (A.PermissionID is null Or A.PermissionID in ( Select PermissionID from PermissionGroup (nolock) where GroupID in
(Select GroupID from SiteUserGroup (nolock) where SiteUserID = @SiteUserID)))
And
(
(
UserID in
(
Select Id from @ColleageUsers
)
)
)
Order by A.id desc
以下は、個別にまたはまとめて試すいくつかのことです。
OPTION(RECOMPILE)
クエリテキスト自体で宣言された変数を使用しているため、OPTION(RECOMPILE)
を使用して、実行ごとにクエリプランを再作成できます。このようにして、オプティマイザは実行時に変数の値を「確認」し、これらの値により適したクエリプランを取得する必要があります。
行の目標を無効にする
TOP()演算子により行の目標が設定されているため、その行の目標をtraceflag:4138で無効にしてみることができます。
計画の連結演算子の行ゴールの例:
OPTION(QUERYTRACEON 4138);
を使用して、クエリレベルで行の目標を無効にします。
行の目標についての詳細 ここ
これらをクエリと一緒にテストします。
SELECT TOP 50 UserID, AuditTypeID , [Action], ActionDate , [Description] , RecordID, ActionedBy = S.FirstName + ' ' + S.LastName , S.ID as SiteUserID
FROM AuditTrial (nolock) A
INNER JOIN SiteUser (nolock) S on S.ID = A.UserID
WHERE
A.CompanyID = @CompanyID
AND A.AuditTypeID <> 1 -- 1 Means Audit Type Login
AND
(
184 = (Select Distinct Top 1 PermissionID from PermissionGroup (nolock) where GroupID in
(Select GroupID from SiteUserGroup (nolock) where SiteUserID = @SiteUserID) and PermissionID = 184)
OR
A.AuditTypeID <> 20
)
AND (A.PermissionID
IS NULL
OR A.PermissionID IN
( SELECT PermissionID
FROM PermissionGroup (nolock)
WHERE GroupID in
(SELECT GroupID FROM SiteUserGroup (nolock)
WHERE SiteUserID = @SiteUserID)
)
)
AND
(
(
UserID IN
(
SELECT Id FROM @ColleageUsers
)
)
)
ORDER BY A.id desc
OPTION(RECOMPILE,QUERYTRACEON 4138 );
ORを削除してUNION ALLを使用する
クエリを書き換える必要がある他のことは、OR
をUNION ALL
で分離することです。
このようにして、より多くのデータを処理する代わりに、索引付け/演算子をより効率的に使用できます。これは、使用するフィルターとインデックスによって異なります。
クエリの例:
SELECT TOP 50 UserID, AuditTypeID , [Action], ActionDate , [Description] , RecordID, ActionedBy,SiteUserID
FROM
(
SELECT UserID, AuditTypeID , [Action], ActionDate , [Description] , RecordID, ActionedBy = S.FirstName + ' ' + S.LastName , S.ID as SiteUserID , A.id
FROM AuditTrial (nolock) A
INNER JOIN SiteUser (nolock) S on S.ID = A.UserID
WHERE
A.CompanyID = @CompanyID
AND A.AuditTypeID <> 1 -- 1 Means Audit Type Login
AND
(
184 = (Select Distinct Top 1 PermissionID from PermissionGroup (nolock) where GroupID in
(Select GroupID from SiteUserGroup (nolock) where SiteUserID = @SiteUserID) and PermissionID = 184)
OR
A.AuditTypeID <> 20
)
AND (A.PermissionID
IS NULL
)
AND
(
(
UserID IN
(
SELECT Id FROM @ColleageUsers
)
)
)
UNION ALL
SELECT TOP 50 UserID, AuditTypeID , [Action], ActionDate , [Description] , RecordID, ActionedBy = S.FirstName + ' ' + S.LastName , S.ID as SiteUserID ,A.id
FROM AuditTrial (nolock) A
INNER JOIN SiteUser (nolock) S on S.ID = A.UserID
WHERE
A.CompanyID = @CompanyID
AND A.AuditTypeID <> 1 -- 1 Means Audit Type Login
AND
(
184 = (Select Distinct Top 1 PermissionID from PermissionGroup (nolock) where GroupID in
(Select GroupID from SiteUserGroup (nolock) where SiteUserID = @SiteUserID) and PermissionID = 184)
OR
A.AuditTypeID <> 20
)
AND A.PermissionID IN
( SELECT PermissionID
FROM PermissionGroup (nolock)
WHERE GroupID in
(SELECT GroupID FROM SiteUserGroup (nolock)
WHERE SiteUserID = @SiteUserID)
)
AND
(
(
UserID IN
(
SELECT Id FROM @ColleageUsers
)
)
)) AS A;
ORDER BY A.id desc
OPTION(RECOMPILE,QUERYTRACEON 4138 );
テーブル変数/一時テーブル
私が試すもう一つのことは、テーブル変数を一時テーブルに変更することです
CREATE TABLE #ColleageUsers(
ID int
)
一時オブジェクトの統計情報を取得するには、テーブル変数には統計情報がありませんが、一時テーブルにはあります。
一時テーブルを使用してクエリを複数に分割
別の実行プランを取得しようとする別のことは、事前にいくつかの作業を行い、これらの結果を一時テーブルに格納することです。多くのテーブルに触れると、正確な見積もりを取得することが難しくなり、作業を分割することで、各パーツの見積もりを計算しやすくなります。
クエリの例:
declare @SiteUserID as int = 6484,
@CompanyID as int = 34;
SELECT PermissionID
INTO #TEMP
FROM PermissionGroup (nolock)
WHERE GroupID in
(SELECT GroupID FROM SiteUserGroup (nolock)
WHERE SiteUserID = @SiteUserID)
DECLARE @PermissionID INT
SELECT DISTINCT Top 1 @PermissionID=PermissionID
FROM PermissionGroup (nolock) where GroupID in
(Select GroupID from SiteUserGroup (nolock)
where SiteUserID = @SiteUserID) and PermissionID = 184
SELECT TOP 50 UserID, AuditTypeID , [Action], ActionDate , [Description] , RecordID, ActionedBy,SiteUserID
FROM
(
SELECT UserID, AuditTypeID , [Action], ActionDate , [Description] , RecordID, ActionedBy = S.FirstName + ' ' + S.LastName , S.ID as SiteUserID , A.id
FROM AuditTrial (nolock) A
INNER JOIN SiteUser (nolock) S on S.ID = A.UserID
WHERE
A.CompanyID = @CompanyID
AND A.AuditTypeID <> 1 -- 1 Means Audit Type Login
AND
(
184 = (@PermissionID)
OR
A.AuditTypeID <> 20
)
AND (A.PermissionID
IS NULL
)
AND
(
(
UserID IN
(
SELECT Id FROM @ColleageUsers
)
)
)
UNION ALL
SELECT TOP 50 UserID, AuditTypeID , [Action], ActionDate , [Description] , RecordID, ActionedBy = S.FirstName + ' ' + S.LastName , S.ID as SiteUserID ,A.id
FROM AuditTrial (nolock) A
INNER JOIN SiteUser (nolock) S on S.ID = A.UserID
WHERE
A.CompanyID = @CompanyID
AND A.AuditTypeID <> 1 -- 1 Means Audit Type Login
AND
(
184 = (Select Distinct Top 1 PermissionID from PermissionGroup (nolock) where GroupID in
(Select GroupID from SiteUserGroup (nolock) where SiteUserID = @SiteUserID) and PermissionID = 184)
OR
A.AuditTypeID <> 20
)
AND A.PermissionID IN
( SELECT PermissionID
FROM #TEMP
)
AND
(
(
UserID IN
(
SELECT Id FROM @ColleageUsers
)
)
)) AS A
ORDER BY A.id desc
OPTION(RECOMPILE,QUERYTRACEON 4138 );