2011-10-20 81 views
1

私は、600,000以上のレコードを含む1つのテーブルで複数の結合を持つストアドプロシージャを実行しています。問題は手続きが非常に遅く、実行に数分かかることがあることです。関連する表の列には索引が付けられていますが、まだ運がありません。SQL Server高速化クエリ

パフォーマンス向上のために何ができるでしょうか?クエリは以下に掲載されています。 @Joeは、あなたの質問にコメントで書いたように

おかげ

with CTE 
as 
(
select * from 
(
    select distinct c.ContactId, c.FirstName, c.LastName, 
    (select top 1 ce.Email from dbo.ContactEmails as ce where ce.ContactId = c.ContactId and ce.IsPrimary = 1) as Email, 
    comp.CompanyName, j.JobName, c.MobileNumber, c.OfficeNumber, cse.DateSent, MAX(cse.DateSent) over(partition by ce.email) as maxdate 

    from dbo.ContactSentEmails as cse 

    join dbo.ContactEmails as ce on cse.ContactId = ce.ContactId 
    join dbo.Contacts as c on ce.ContactId = c.ContactId 
    left join dbo.Jobs as j on c.JobId = j.JobId 
    left join dbo.Companies as comp on c.CompanyId = comp.CompanyId 

    join dbo.StaffProjects as sp on cse.StaffProjectId = sp.StaffProjectId 
    join dbo.Staff as s on sp.StaffId = s.StaffId 
    join dbo.Projects as p on sp.ProjectId = p.ProjectId 

    where (@ContactSourceId = -1 or c.ContactSourceId = @ContactSourceId) 
    and (@FirstName = '' OR c.FirstName LIKE '%' + @FirstName + '%') 
    and (@LastName = '' OR c.LastName LIKE '%' + @LastName + '%') 
    and (@EmailAddress = '' OR ce.Email LIKE '%' + @EmailAddress + '%') 
    and (@StaffId = -1 or sp.StaffId = @StaffId) 
    and (@ProjectId = -1 or sp.ProjectId = @ProjectId) 
    and (@OfficeId = -1 or p.OfficeId = @OfficeId) 
    and cse.DateSent between CONVERT(datetime, @startDate) and CONVERT(datetime, @endDate) 

    group by c.ContactId, c.FirstName, c.LastName, Email,comp.CompanyName, j.JobName, c.MobileNumber, c.OfficeNumber, cse.DateSent 
) as tbContacts 
) 

select ContactId, FirstName, LastName, Email, CompanyName, JobName, MobileNumber, OfficeNumber from CTE where cte.DateSent = CTE.maxdate order by CTE.Email 
+4

ワイルドカードを使用しているLIKEはおそらくあなたを傷つけているでしょう。索引付けの量は、パフォーマンスを向上させません。 –

+0

また、左(外側)ジョインに続いて(内側)ジョインがあります。これらの左結合は、テーブル順序に基づいて内部結合になります。クエリを壊さずにすべての内部結合の後に移動できるように見えます。 – billinkc

答えて

2

多くの欠陥!

欠陥1:指定された値を見ずにプランを選択することはできません。

(@ContactSourceId = -1 or c.ContactSourceId = @ContactSourceId) 

@ContactSourceIdが-1の場合、1つのクエリ実行計画があります。 ContactSourceIdが-1でない場合、別のクエリ実行計画があります。正しい実行計画を選択したい場合は、変数の値を調べずに、フィルタする予定の内容を知っているクエリを提供する必要があります。あなたは、この基準が7回を構築使用しましたので

、2^7つの潜在的な計画と右の1つを得ることのあなたの確率は、1/2^7 = 1/128 < 1%

あなたがする必要があるありますこのクエリテキストを128の異なるクエリに分解します。クエリオプティマイザはこれをあなたにとってひどく行うだけです。


不具合2:@FirstNameが供給され、他の変数ではないされたと仮定します。これらの128個のクエリの

一つの検索引数の検索基準を使用しないと、このように動作します。この場合、連絡先テーブルにアクセスするための主な基準は@FirstNameです。

c.FirstName LIKE '%' + @FirstName + '%' 

あなたはContactsテーブルにちょうどこの基準にクエリを書いた場合 - あなたが使用されるContactsテーブルに追加することができます何の指標もないでしょう。あなたはテーブルスキャンに苦しむ。 SARGableの検索条件について学びます。


欠陥3:各行の操作は、各行に対して同じ結果をもたらします。

cse.DateSent between CONVERT(datetime, @startDate) and CONVERT(datetime, @endDate) 

なぜ各行で変数を日付時刻に変換していますか?クエリを実行する前にこれらの変換を行います。


欠陥4:明確な時間の90%は、クエリで松葉杖

distinct 
top 1 
group by 

そう多くの「私を与える一つだけ」演算子は、クエリ作成者がちょうどものをしようと何スティックを見たことを意味しています。実際の意図に単純化する。私の推測は、別個のものは必要ではないということです。あなたがそれを必要としないときに別名を追加すると、あなたはそれを支払うことになります!

+0

+1。ワイルドカードは遅いですが、DISTINCTは痛い親指のように私にくっつきます。 'MAX(DateSent)'を取得するための相関サブクエリは、潜在的に大量のテーブル上のDISTINCTではなく、おそらくここでうまくいくでしょう。 –

+0

クエリプランの数については良い点がありますが、私はそれを「欠陥」と正確には説明しません。しかし、これを別々のクエリに分割することについてのあなたの指摘は、パフォーマンスを向上させるための良いパターンになる可能性があります(もちろん、保守は潜在的に高価になります)。これは、動的SQLを使用するか、SQLを動的に生成する利点の1つです。 –

2

は、LIKE事業者は、各検索文字列の先頭にワイルドカードを付加している、特にので、高価です。数多くのOR演算子は、クエリのパフォーマンスが低下する可能性があります。

Erland Sommarskogの古典的な記事Dynamic Search Conditions in T-SQLを参照してください。

[また、SELECT句で、ほぼすべての列でSELECT TOP 1group by ...とは何ですか?]

+1

グループを推測しているのは、エラーでグループに含まれていない列を取得してグループにすべてを追加したからです。 – Purplegoldfish

+0

返信いただきありがとうございます。選択トップは、プライマリに設定されているメールアドレスが複数ある可能性があるためです。グループ節を削除する必要はありません。ありがとう – user1005344

1

@Joeと@Kennyの両方として、LIKE事業者は、おそらくあなたの問題の最大の原因であると言います。これを修正すると、パフォーマンスが最も向上する可能性があります。 full text searchを調査し、ニーズに合っているかどうか確認してください。

ただし、select句のサブ選択として電子メールを選択する方法もリファクタリングします。これは、通常、SQL Serverでクエリを実行するための非常に高価な方法です。連絡先に複数のメールアドレスがありますか?IsPrimary = 1?そうでない場合は、メインのFROMの表に参加してください。複数のビューを持つことができる場合は、連絡先ごとに一番上の電子メールアドレスを返すビュー(インデックス付きビューなど)を作成することを検討してください。その後、あなたはそれに加わることができます。

+0

はい、プライマリ= 1の場合、複数のメールが送信されることがあります。連絡先を返信して提案した内容を確認し、そのメールに参加します。投稿ありがとう – user1005344

関連する問題