SQL Server 2005 には、次の特性を持つかなり複雑なクエリがあります。
- 11 個のテーブル結合
- 8 ビュー結合
- 7 つの非相関クエリ結合
すべての結合には、共通のインデックス付き列があります。
通常は 30 秒未満で実行されますが、もう 1 つのテーブル結合 (結合されたインデックス付きの列) を追加すると、一見永遠に実行されます。
特定の既存のビューの結合を 1 つ削除すると、新しい結合でも再び高速に実行されることに気付きました。
以下は、ほとんどの結合が削除されたクエリの修正バージョンですが、それでもパフォーマンスの問題を示しています。
--This runs super slow
SELECT
L.LoanID
FROM dbo.Loan L
JOIN dbo.Company C ON c.CompanyKey = l.CompanyKey
JOIN dbo.Status S on L.LoanID = S.LoanID
JOIN dbo.Participation P on L.LoanID = P.LoanID
JOIN dbo.Delinquent D on L.LoanID = D.LoanID
JOIN dbo.Property Pr on Pr.Loanid = L.Loanid
JOIN dbo.MailingAddress ma ON ma.LoanID = L.LoanID
LEFT JOIN dbo.BorrowerPhonePivot bp ON bp.loanid = l.loanid
WHERE s.primstat=1 AND DATEADD(d,16,L.DueDate) <= C.TransPostDate
--This runs fast
SELECT
L.LoanID
FROM dbo.Loan L
JOIN dbo.Company C ON c.CompanyKey = l.CompanyKey
JOIN dbo.Status S on L.LoanID = S.LoanID
JOIN dbo.Participation P on L.LoanID = P.LoanID
JOIN dbo.Delinquent D on L.LoanID = D.LoanID
JOIN dbo.Property Pr on Pr.Loanid = L.Loanid
--JOIN dbo.MailingAddress ma ON ma.LoanID = L.LoanID
LEFT JOIN dbo.BorrowerPhonePivot bp ON bp.loanid = l.loanid
WHERE s.primstat=1 AND DATEADD(d,16,L.DueDate) <= C.TransPostDate
--This runs fast
SELECT
L.LoanID
FROM dbo.Loan L
JOIN dbo.Company C ON c.CompanyKey = l.CompanyKey
JOIN dbo.Status S on L.LoanID = S.LoanID
JOIN dbo.Participation P on L.LoanID = P.LoanID
JOIN dbo.Delinquent D on L.LoanID = D.LoanID
JOIN dbo.Property Pr on Pr.Loanid = L.Loanid
JOIN dbo.MailingAddress ma ON ma.LoanID = L.LoanID
--LEFT JOIN dbo.BorrowerPhonePivot bp ON bp.loanid = l.loanid
WHERE s.primstat=1 AND DATEADD(d,16,L.DueDate) <= C.TransPostDate
この問題は、他のクエリで以前にこの奇妙な動作に遭遇し、完全に解決できなかったため、このクエリに固有のものではありません。私の理論では、SQL Server が内部クエリ プランの制限に達し、おかしくなっているというものです。
DTA を介して推奨事項を生成するために、クエリを 1 つの状態にフィルター処理しました (1 つのレコードが返されましたが、10 分かかりました)。必要なインデックスをいくつか追加しましたが、違いはありませんでした。
実行計画の結果を調べましたが、異常は見つかりませんでした。
他に何を探すべきかについての提案はありますか?
これは私が試したことです:
- DTA - 生成された 4 つの推奨事項のうち、必要なインデックスを 1 つ追加しました。(その他はすでにカバーされています)
- @AaronBertrand によるスレッド化の問題かどうかを確認するための MAXDOP 1
- @AaronBertrand ごとのテーブルの UPDATE STATISTICS
- @AaronBertrand に従って、SQL セントリー プラン エクスプローラーで sqlplan (pastebin) を実行しました。奇妙なことに、[実際の行] 列には、実際よりも桁違いに大きい数値が表示されます。どういうわけか、クエリ プラン オプティマイザが混乱しています。
最初のクエリのプラン表示:
|--Nested Loops(Left Outer Join, OUTER REFERENCES:([L].[LoanID]))
|--Nested Loops(Inner Join, WHERE:([Expr1056]<=[Service_Prod].[dbo].[Company].[TransPostDate] as [C].[TransPostDate]))
| |--Hash Match(Inner Join, HASH:([ma].[LoanID])=([D].[LoanID]), RESIDUAL:([Service_Prod].[dbo].[Delinquent].[LoanID] as [D].[LoanID]=[Service_Prod].[dbo].[MailingAddress].[LoanID] as [ma].[LoanID]))
| | |--Hash Match(Inner Join, HASH:([ma].[LoanID])=([P].[LoanID]), RESIDUAL:([Service_Prod].[dbo].[Participation].[LoanID] as [P].[LoanID]=[Service_Prod].[dbo].[MailingAddress].[LoanID] as [ma].[LoanID]))
| | | |--Merge Join(Inner Join, MERGE:([Pr].[LoanID])=([ma].[LoanID]), RESIDUAL:([Service_Prod].[dbo].[Property].[LoanID] as [Pr].[LoanID]=[Service_Prod].[dbo].[MailingAddress].[LoanID] as [ma].[LoanID]))
| | | | |--Merge Join(Inner Join, MERGE:([Pr].[LoanID])=([L].[LoanID]), RESIDUAL:([Service_Prod].[dbo].[Loan].[LoanID] as [L].[LoanID]=[Service_Prod].[dbo].[Property].[LoanID] as [Pr].[LoanID]))
| | | | | |--Merge Join(Inner Join, MERGE:([Pr].[LoanID])=([S].[LoanID]), RESIDUAL:([Service_Prod].[dbo].[Status].[LoanID] as [S].[LoanID]=[Service_Prod].[dbo].[Property].[LoanID] as [Pr].[LoanID]))
| | | | | | |--Index Scan(OBJECT:([Service_Prod].[dbo].[Property].[aaaaaProperty_PK] AS [Pr]), ORDERED FORWARD)
| | | | | | |--Index Seek(OBJECT:([Service_Prod].[dbo].[Status].[_dta_index_Status_114_1122103038__K13_K1_K2_K3] AS [S]), SEEK:([S].[PrimStat]=(1)) ORDERED FORWARD)
| | | | | |--Compute Scalar(DEFINE:([Expr1056]=dateadd(day,(16),[Service_Prod].[dbo].[Loan].[DueDate] as [L].[DueDate])))
| | | | | |--Index Scan(OBJECT:([Service_Prod].[dbo].[Loan].[_dta_index_Loan_K1_K66_K10_K23] AS [L]), ORDERED FORWARD)
| | | | |--Index Scan(OBJECT:([Service_Prod].[dbo].[MailingAddress].[StatusMailingAddress] AS [ma]), ORDERED FORWARD)
| | | |--Index Scan(OBJECT:([Service_Prod].[dbo].[Participation].[Reference17] AS [P]))
| | |--Index Scan(OBJECT:([Service_Prod].[dbo].[Delinquent].[aaaaaDelinquent_PK] AS [D]))
| |--Table Scan(OBJECT:([Service_Prod].[dbo].[Company] AS [C]))
|--Stream Aggregate(GROUP BY:([l].[LoanID]))
|--Filter(WHERE:([Service_Prod].[dbo].[Loan].[LoanID] as [l].[LoanID]=[Service_Prod].[dbo].[Loan].[LoanID] as [L].[LoanID]))
|--Hash Match(Inner Join, HASH:([b].[LoanID], [b].[AssmRecCounter], [b].[BorrowerID])=([bp].[LoanID], [bp].[AssmRecCounter], [bp].[BorrowerID]), RESIDUAL:([Service_Prod].[dbo].[BorrowerPhone].[LoanID] as [bp].[LoanID]=[Service_Prod].[dbo].[Borrower].[LoanID] as [b].[LoanID] AND [Service_Prod].[dbo].[Borrower].[AssmRecCounter] as [b].[AssmRecCounter]=[Service_Prod].[dbo].[BorrowerPhone].[AssmRecCounter] as [bp].[AssmRecCounter] AND [Service_Prod].[dbo].[Borrower].[BorrowerID] as [b].[BorrowerID]=[Service_Prod].[dbo].[BorrowerPhone].[BorrowerID] as [bp].[BorrowerID]))
|--Hash Match(Inner Join, HASH:([Service_Prod].[dbo].[Company].[CompanyKey])=([l].[CompanyKey]))
| |--Index Scan(OBJECT:([Service_Prod].[dbo].[Company].[aaaaaCompany_PK]))
| |--Merge Join(Inner Join, MERGE:([s].[LoanID])=([b].[LoanID]), RESIDUAL:([Service_Prod].[dbo].[Status].[LoanID] as [s].[LoanID]=[Service_Prod].[dbo].[Borrower].[LoanID] as [b].[LoanID]))
| |--Index Seek(OBJECT:([Service_Prod].[dbo].[Status].[_dta_index_Status_114_1122103038__K13_K1_K2_K3] AS [s]), SEEK:([s].[PrimStat]=(1)) ORDERED FORWARD)
| |--Merge Join(Inner Join, MERGE:([l].[LoanID])=([b].[LoanID]), RESIDUAL:([Service_Prod].[dbo].[Loan].[LoanID] as [l].[LoanID]=[Service_Prod].[dbo].[Borrower].[LoanID] as [b].[LoanID] AND [Service_Prod].[dbo].[Loan].[AssmRecCounter] as [l].[AssmRecCounter]=[Service_Prod].[dbo].[Borrower].[AssmRecCounter] as [b].[AssmRecCounter]))
| |--Index Scan(OBJECT:([Service_Prod].[dbo].[Loan].[_dta_index_Loan_K1_K66_K10_K23] AS [l]), ORDERED FORWARD)
| |--Index Scan(OBJECT:([Service_Prod].[dbo].[Borrower].[aaaaaBorrower_PK] AS [b]), ORDERED FORWARD)
|--Index Seek(OBJECT:([Service_Prod].[dbo].[BorrowerPhone].[_dta_index_BorrowerPhone_K12_K1_K2_K3_K5] AS [bp]), SEEK:([bp].[ForeignPhone]=(0)), WHERE:([Service_Prod].[dbo].[BorrowerPhone].[PhoneType] as [bp].[PhoneType]=(0) OR [Service_Prod].[dbo].[BorrowerPhone].[PhoneType] as [bp].[PhoneType]=(1)) ORDERED FORWARD)
2 番目のクエリのプラン表示:
|--Parallelism(Gather Streams)
|--Hash Match(Left Outer Join, HASH:([L].[LoanID])=([l].[LoanID]), RESIDUAL:([Service_Prod].[dbo].[Loan].[LoanID] as [l].[LoanID]=[Service_Prod].[dbo].[Loan].[LoanID] as [L].[LoanID]))
|--Parallelism(Repartition Streams, Hash Partitioning, PARTITION COLUMNS:([L].[LoanID]))
| |--Hash Match(Inner Join, HASH:([Pr].[LoanID])=([D].[LoanID]), RESIDUAL:([Service_Prod].[dbo].[Delinquent].[LoanID] as [D].[LoanID]=[Service_Prod].[dbo].[Property].[LoanID] as [Pr].[LoanID]))
| |--Bitmap(HASH:([Pr].[LoanID]), DEFINE:([Bitmap1065]))
| | |--Hash Match(Inner Join, HASH:([Pr].[LoanID])=([P].[LoanID]), RESIDUAL:([Service_Prod].[dbo].[Participation].[LoanID] as [P].[LoanID]=[Service_Prod].[dbo].[Property].[LoanID] as [Pr].[LoanID]))
| | |--Bitmap(HASH:([Pr].[LoanID]), DEFINE:([Bitmap1064]))
| | | |--Hash Match(Inner Join, HASH:([S].[LoanID])=([Pr].[LoanID]), RESIDUAL:([Service_Prod].[dbo].[Status].[LoanID] as [S].[LoanID]=[Service_Prod].[dbo].[Property].[LoanID] as [Pr].[LoanID]))
| | | |--Bitmap(HASH:([S].[LoanID]), DEFINE:([Bitmap1063]))
| | | | |--Hash Match(Inner Join, HASH:([S].[LoanID])=([L].[LoanID]), RESIDUAL:([Service_Prod].[dbo].[Loan].[LoanID] as [L].[LoanID]=[Service_Prod].[dbo].[Status].[LoanID] as [S].[LoanID]))
| | | | |--Bitmap(HASH:([S].[LoanID]), DEFINE:([Bitmap1062]))
| | | | | |--Parallelism(Repartition Streams, Hash Partitioning, PARTITION COLUMNS:([S].[LoanID]))
| | | | | |--Index Seek(OBJECT:([Service_Prod].[dbo].[Status].[IDX_Status_PrimStat_INCLoanID] AS [S]), SEEK:([S].[PrimStat]=(1)) ORDERED FORWARD)
| | | | |--Parallelism(Repartition Streams, Hash Partitioning, PARTITION COLUMNS:([L].[LoanID]), WHERE:(PROBE([Bitmap1062],[Service_Prod].[dbo].[Loan].[LoanID] as [L].[LoanID])))
| | | | |--Nested Loops(Inner Join, WHERE:([Expr1053]<=[Service_Prod].[dbo].[Company].[TransPostDate] as [C].[TransPostDate]))
| | | | |--Parallelism(Distribute Streams, RoundRobin Partitioning)
| | | | | |--Table Scan(OBJECT:([Service_Prod].[dbo].[Company] AS [C]))
| | | | |--Compute Scalar(DEFINE:([Expr1053]=dateadd(day,(16),[Service_Prod].[dbo].[Loan].[DueDate] as [L].[DueDate])))
| | | | |--Index Scan(OBJECT:([Service_Prod].[dbo].[Loan].[_dta_index_Loan_K1_K66_K10_K23] AS [L]))
| | | |--Parallelism(Repartition Streams, Hash Partitioning, PARTITION COLUMNS:([Pr].[LoanID]))
| | | |--Index Scan(OBJECT:([Service_Prod].[dbo].[Property].[aaaaaProperty_PK] AS [Pr]), WHERE:(PROBE([Bitmap1063],[Service_Prod].[dbo].[Property].[LoanID] as [Pr].[LoanID])))
| | |--Parallelism(Repartition Streams, Hash Partitioning, PARTITION COLUMNS:([P].[LoanID]))
| | |--Index Scan(OBJECT:([Service_Prod].[dbo].[Participation].[Reference17] AS [P]), WHERE:(PROBE([Bitmap1064],[Service_Prod].[dbo].[Participation].[LoanID] as [P].[LoanID])))
| |--Parallelism(Repartition Streams, Hash Partitioning, PARTITION COLUMNS:([D].[LoanID]))
| |--Index Scan(OBJECT:([Service_Prod].[dbo].[Delinquent].[aaaaaDelinquent_PK] AS [D]), WHERE:(PROBE([Bitmap1065],[Service_Prod].[dbo].[Delinquent].[LoanID] as [D].[LoanID])))
|--Stream Aggregate(GROUP BY:([l].[LoanID]))
|--Sort(ORDER BY:([l].[LoanID] ASC))
|--Parallelism(Repartition Streams, Hash Partitioning, PARTITION COLUMNS:([l].[LoanID]))
|--Hash Match(Inner Join, HASH:([b].[LoanID], [b].[AssmRecCounter], [b].[BorrowerID])=([bp].[LoanID], [bp].[AssmRecCounter], [bp].[BorrowerID]), RESIDUAL:([Service_Prod].[dbo].[BorrowerPhone].[LoanID] as [bp].[LoanID]=[Service_Prod].[dbo].[Borrower].[LoanID] as [b].[LoanID] AND [Service_Prod].[dbo].[Borrower].[AssmRecCounter] as [b].[AssmRecCounter]=[Service_Prod].[dbo].[BorrowerPhone].[AssmRecCounter] as [bp].[AssmRecCounter] AND [Service_Prod].[dbo].[Borrower].[BorrowerID] as [b].[BorrowerID]=[Service_Prod].[dbo].[BorrowerPhone].[BorrowerID] as [bp].[BorrowerID]))
|--Bitmap(HASH:([b].[LoanID], [b].[AssmRecCounter], [b].[BorrowerID]), DEFINE:([Bitmap1068]))
| |--Parallelism(Repartition Streams, Hash Partitioning, PARTITION COLUMNS:([b].[LoanID], [b].[AssmRecCounter], [b].[BorrowerID]))
| |--Hash Match(Inner Join, HASH:([s].[LoanID], [l].[AssmRecCounter])=([b].[LoanID], [b].[AssmRecCounter]), RESIDUAL:([Service_Prod].[dbo].[Status].[LoanID] as [s].[LoanID]=[Service_Prod].[dbo].[Borrower].[LoanID] as [b].[LoanID] AND [Service_Prod].[dbo].[Loan].[AssmRecCounter] as [l].[AssmRecCounter]=[Service_Prod].[dbo].[Borrower].[AssmRecCounter] as [b].[AssmRecCounter]))
| |--Bitmap(HASH:([s].[LoanID], [l].[AssmRecCounter]), DEFINE:([Bitmap1067]))
| | |--Parallelism(Repartition Streams, Hash Partitioning, PARTITION COLUMNS:([s].[LoanID], [l].[AssmRecCounter]))
| | |--Hash Match(Inner Join, HASH:([Service_Prod].[dbo].[Company].[CompanyKey])=([l].[CompanyKey]))
| | |--Parallelism(Distribute Streams, Broadcast Partitioning)
| | | |--Index Scan(OBJECT:([Service_Prod].[dbo].[Company].[aaaaaCompany_PK]))
| | |--Hash Match(Inner Join, HASH:([s].[LoanID])=([l].[LoanID]), RESIDUAL:([Service_Prod].[dbo].[Loan].[LoanID] as [l].[LoanID]=[Service_Prod].[dbo].[Status].[LoanID] as [s].[LoanID]))
| | |--Bitmap(HASH:([s].[LoanID]), DEFINE:([Bitmap1066]))
| | | |--Parallelism(Repartition Streams, Hash Partitioning, PARTITION COLUMNS:([s].[LoanID]))
| | | |--Index Seek(OBJECT:([Service_Prod].[dbo].[Status].[IDX_Status_PrimStat_INCLoanID] AS [s]), SEEK:([s].[PrimStat]=(1)) ORDERED FORWARD)
| | |--Parallelism(Repartition Streams, Hash Partitioning, PARTITION COLUMNS:([l].[LoanID]))
| | |--Index Scan(OBJECT:([Service_Prod].[dbo].[Loan].[_dta_index_Loan_K1_K66_K10_K23] AS [l]), WHERE:(PROBE([Bitmap1066],[Service_Prod].[dbo].[Loan].[LoanID] as [l].[LoanID])))
| |--Parallelism(Repartition Streams, Hash Partitioning, PARTITION COLUMNS:([b].[LoanID], [b].[AssmRecCounter]))
| |--Index Scan(OBJECT:([Service_Prod].[dbo].[Borrower].[aaaaaBorrower_PK] AS [b]), WHERE:(PROBE([Bitmap1067],[Service_Prod].[dbo].[Borrower].[LoanID] as [b].[LoanID],[Service_Prod].[dbo].[Borrower].[AssmRecCounter] as [b].[AssmRecCounter])))
|--Parallelism(Repartition Streams, Hash Partitioning, PARTITION COLUMNS:([bp].[LoanID], [bp].[AssmRecCounter], [bp].[BorrowerID]))
|--Index Seek(OBJECT:([Service_Prod].[dbo].[BorrowerPhone].[_dta_index_BorrowerPhone_K12_K1_K2_K3_K5] AS [bp]), SEEK:([bp].[ForeignPhone]=(0)), WHERE:(([Service_Prod].[dbo].[BorrowerPhone].[PhoneType] as [bp].[PhoneType]=(0) OR [Service_Prod].[dbo].[BorrowerPhone].[PhoneType] as [bp].[PhoneType]=(1)) AND PROBE([Bitmap1068],[Service_Prod].[dbo].[BorrowerPhone].[LoanID] as [bp].[LoanID],[Service_Prod].[dbo].[BorrowerPhone].[AssmRecCounter] as [bp].[AssmRecCounter],[Service_Prod].[dbo].[BorrowerPhone].[BorrowerID] as [bp].[BorrowerID])) ORDERED FORWARD)