3

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)
4

3 に答える 3

3

これはばかげているように聞こえるかもしれませんが、さまざまな組み合わせを試し、すべての結合条件を追加して、オプティマイザーが一致する可能性を最大限に高めてください。これは、テーブルの順序とカーディナリティによって、オプティマイザーがより良い計画を選択するのに役立つのを見てきました。

SELECT ac.AccountNum
    FROM dbo.Account AS ac
    INNER JOIN dbo.Address AS ad
      ON ad.AccountNum = ac.AccountNum
    INNER JOIN dbo.PhoneView AS p
      ON p.AccountNum = ac.AccountNum
      AND p.AccountNum = ad.AccountNum; -- extra help here

また、逆の順序で試してください。

SELECT ac.AccountNum
    FROM dbo.Account AS ac
    INNER JOIN dbo.PhoneView AS p
      ON p.AccountNum = ac.AccountNum
    INNER JOIN dbo.Address AS ad
      ON ad.AccountNum = p.AccountNum
      AND ad.AccountNum = ac.AccountNum; -- extra help here

dbo.各テーブルにプレフィックスを追加し(これを習慣にする必要があります)、それらを明示的に指定することでオプティマイザーの決定が改善されるかどうか (または少なくとも計画の重複を防止するかどうか) を確認し、読みやすさのために短いエイリアスを使用しました。

また、実行計画を調べることも忘れないでください。これらのテーブルの一部のサイズ/カーディナリティが原因で、間違ったタイプの結合が取得されている可能性があります。

編集すると、クエリ プランが作成されました。不適切なプランの全体に並列処理が含まれていることがわかります。クエリに追加を試みることもできMAXDOP 1ますが、これは一時的な救済にすぎず、実際の修正にはなりません。カーディナリティの見積もりが不適切で、複数のスレッドに作業が不適切に分散されていると思われます (一般的にCXPACKET待機が原因ですが、原因ではなく症状です)。クエリ内のさまざまなテーブルの統計を確認します。無料のSQL Sentry Plan Explorerで実際の実行計画を読み込むと、Plan Tree タブで、推定行数と実際の行数がひどくずれている場所をすぐに確認できます。これにより、どのテーブルが必要かがわかります。統計を更新する. 少なくとも、これがおそらく 90% の確率で並列処理がひどく実行される原因です。

いくつかの提案。company は 1 行しかないため、クエリに company を含める必要はまったくありません。代わりに、カットオフ日を変数に取り込み、16 日を差し引いて、Loan.DueDate のインデックスを使用できるようになりました (存在する場合)。

DECLARE @d SMALLDATETIME;

SELECT @d = DATEADD(DAY, -16, TransPostDate) FROM dbo.Company;

SELECT L.LoanID
FROM dbo.Loan AS L
  INNER JOIN dbo.[Status] AS S                 ON L.LoanID  = S.LoanID
  INNER JOIN dbo.Participation AS P            ON L.LoanID  = P.LoanID
  INNER JOIN dbo.Delinquent AS D               ON L.LoanID  = D.LoanID
  INNER JOIN dbo.Property AS Pr                ON Pr.LoanID = L.LoanID
  INNER JOIN dbo.MailingAddress AS ma          ON ma.LoanID = L.LoanID  
  LEFT OUTER JOIN dbo.BorrowerPhonePivot AS bp ON bp.loanid = L.loanid
WHERE 
  s.primstat = 1 
  AND L.DueDate <= @cutoff;

LoanID のみを返すので、これは同じことを行いませんか?

DECLARE @d SMALLDATETIME;

SELECT @d = DATEADD(DAY, -16, TransPostDate) FROM dbo.Company;

SELECT L.LoanID
FROM dbo.Loan AS L
  WHERE L.DueDate <= @cutoff
  AND EXISTS (SELECT 1 FROM dbo.Status         WHERE LoanID = L.LoanID AND primstat = 1)
  AND EXISTS (SELECT 1 FROM dbo.Participation  WHERE LoanID = L.LoanID)
  AND EXISTS (SELECT 1 FROM dbo.Delinquent     WHERE LoanID = L.LoanID)
  AND EXISTS (SELECT 1 FROM dbo.Property       WHERE LoanID = L.LoanID)
  AND EXISTS (SELECT 1 FROM dbo.MailingAddress WHERE LoanID = L.LoanID);

これはほんの始まりにすぎず、すべての問題を解決できるわけではありません。これらの結合されたテーブルの一部のすべての行が本当に必要なわけではないと私は信じなければなりません。

于 2012-05-30T20:07:45.243 に答える
2

2 つの提案があります。

1 つ目は、SQL Server データベース エンジン チューニング アドバイザーを使用して提案を確認することです。 パフォーマンスを向上させるために追加する必要があるが、明らかではないインデックスを検出する場合があります。

2 番目のオプションは、わずかに古いデータを処理できる場合、またはデータが更新されたときにこれから説明するプロセスをトリガーできる場合にのみ機能するオプションです。 定期的に (1 時間に数回程度) 更新されるデータでは機能しません。私たちの場合、データが 24 時間以内に正確であれば問題ないので、これから説明するプロセスを 1 日 1 回実行できます。その贅沢がない場合は、残りを無視してください。

私たちも同じような状況に陥り、何をしてもパフォーマンスを上げられませんでした。あまりにも多くの大きなテーブルで、結合が多すぎました。ビューから 431 行を返すのに 5 分ほどかかりました。

そこで、BI (ビジネス インテリジェンス) チームのアプローチを参考にして、正規化されたデータに基づいて非正規化テーブルを作成しました。基本的に、ミニデータ ウェアハウスを作成しました。

ビュー (実行に時間がかかる) は「vw_StoreData」という名前で、次のステートメントを使用してエクスポートします。

IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[tmpCompiledStoreData]') AND type in (N'U'))
DROP TABLE [dbo].[tmpCompiledStoreData]


Select * Into tmpCompiledStoreData FROM vw_StoreData

IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[CompiledStoreData]') AND type in (N'U'))
DROP TABLE [dbo].[CompiledStoreData]


exec sp_rename 'tmpCompiledStoreData', 'CompiledStoreData'

GRANT SELECT ON [dbo].[CompiledStoreData] TO [SomeUser]
GRANT SELECT ON [dbo].[CompiledStoreData] TO [SomeOtheruser]
...etc

結果のテーブル CompiledStoreData" は、ビューと比較して、非常に高速にクエリを実行できます。繰り返しますが、ビューから非正規化されたテーブルを作成することは、データの鮮度のニーズがリアルタイムでない場合にのみ機能します。

于 2012-05-30T19:15:28.560 に答える
2

統計が古くなっていることについて@AaronBertrandは正しいと思います。

私がすることの 1 つは、1 つの暗黙的な結合を削除することです。暗黙的な結合と明示的な結合を混在させると、奇妙なことが起こる可能性があることに気付きました (ただし、パフォーマンスよりも悪い結果セットの方が多いですが、オプティマイザーが最適ではないものを選択することで、複雑なクエリでのパフォーマンスに貢献する可能性があります)。

また、ビューがビューを呼び出すビューである場合は特に、ビューが遅くなる可能性があることを警告したいと思います。ビューを見て、それらをクエリ内の直接コードに置き換えることができるかどうかを確認することで、クエリが大幅に高速化されるのを見てきました。これは、各ビューが同じベース テーブルを呼び出す場合、およびビューがビューを呼び出す場合に特に当てはまります。したがって、統計を更新しても問題が解決しない場合は、ビューを置き換えることを検討してください。

于 2012-05-30T21:59:18.213 に答える