2013-09-04 20 views
5

簡体複数の列に対してJOIN'ing、私は接点がDoNotCall電話の特定のリストに電話番号が一致するかを確認したい2つのテーブルcontactsdonotcallメイクの使用

CREATE TABLE contacts 
(
    id int PRIMARY KEY, 
    phone1 varchar(20) NULL, 
    phone2 varchar(20) NULL, 
    phone3 varchar(20) NULL, 
    phone4 varchar(20) NULL 
); 
CREATE TABLE donotcall 
(
    list_id int NOT NULL, 
    phone varchar(20) NOT NULL 
); 
CREATE NONCLUSTERED INDEX IX_donotcall_list_phone ON donotcall 
(
    list_id ASC, 
    phone ASC 
); 

を持っています。 より速い検索のために、donotcalllist_idphoneに索引付けしました。

私は次のようにそれを登録しよ作る長時間かかる(例えば9秒。):

SELECT DISTINCT c.id 
FROM contacts c 
JOIN donotcall d 
    ON d.list_id = 1 
    AND d.phone IN (c.phone1, c.phone2, c.phone3, c.phone4) 

Screenshot of execution plan

Execution plan on Pastebin

私は別々に各電話フィールドでそれを登録しよ放置すれば一方ではるかに速く実行されます(たとえば、1.5秒):

SELECT c.id 
FROM contacts c 
LEFT JOIN donotcall d1 
    ON d1.list_id = 1 
    AND d1.phone = c.phone1 
LEFT JOIN donotcall d2 
    ON d2.list_id = 1 
    AND d2.phone = c.phone2 
LEFT JOIN donotcall d3 
    ON d3.list_id = 1 
    AND d3.phone = c.phone3 
LEFT JOIN donotcall d4 
    ON d4.list_id = 1 
    AND d4.phone = c.phone4 
WHERE 
    d1.phone IS NOT NULL 
    OR d2.phone IS NOT NULL 
    OR d3.phone IS NOT NULL 
    OR d4.phone IS NOT NULL 

Screenshot of execution plan

Execution plan on Pastebin

私の仮定は、それがdonotcallにインデックスを利用していないので、最初のスニペットはゆっくり走るということです。
したがって、複数の列に対して結合を行い、インデックスを使用する方法はありますか?

+0

実際に行う必要があるのは、データベースの構造を修正することです。 phone1、phone2、phone3、phone4 - 子テーブルが必要であることを示す必要はありません。 – HLGEM

+0

@HLGEM:ポイントが記載されています。もし私が選択肢を持っていれば、私はそれを別の方法でやったでしょう。しかし、時には、リファクタリングを期待せずに、他の人が作成した構造に悩まされていることもあります。 – ANisus

答えて

6

インデックスを使用してIN (c.phone1, c.phone2, c.phone3, c.phone4)を解決するとSQL Serverが考えている可能性があります。インデックスが速くヒントとなる場合

あなたがテストすることができます:あなたが投稿問い合わせ計画から

SELECT c.* 
FROM contacts c 
JOIN donotcall d with (index(IX_donotcall_list_phone)) 
    ON d.list_id = 1 
    AND d.phone IN (c.phone1, c.phone2, c.phone3, c.phone4) 

、それは最初の計画は40kの行を生成すると推定されて示していますが、それだけで21行を返します。 2番目の計画は1行を見積もります(もちろん21も戻ります)。

statisticsは最新ですか?古い統計では、クエリアナライザが悪い選択をしていると説明できます。統計は、自動的に、または毎週の仕事で更新する必要があります。手動で

select object_name(ind.object_id) as TableName 
,  ind.name as IndexName 
,  stats_date(ind.object_id, ind.index_id) as StatisticsDate 
from sys.indexes ind 
order by 
     stats_date(ind.object_id, ind.index_id) desc 

することはできupdate them:とあなたの統計の年齢を確認

EXEC sp_updatestats; 
+0

良い提案。私はヒントを追加しようとしました。しかし、それは何も変わらなかった。 LEFT JOINバージョンはまだまだ高速です。 SQL Serverがインデックスを使用してコードを解決できないようです。 – ANisus

+2

クエリプランを比較しようとしましたか? (Menu Query、次に実際の実行計画を含む) – Andomar

+0

私はこれをやろうとしていますが、クエリプランの分析にはほとんど経験がありません。私が見ることができるように、LEFT JOINソリューションは、4つのハッシュ・マッチ(右外部結合)を使用して非常に単純です。単一のJOINソリューションは完全に異なった方法です。 2つのネストされたループ(インナー結合)。最初はインデックスを使用し、次に接触テーブルを使用します。 – ANisus

0

をこの貧しいデータベース構造では、UNION ALLは、クエリが最速かもしれません。

+0

UNION ALLは現在単一のJOINソリューションよりも優れていますが、LEFT JOINソリューションよりも少し遅いです。 – ANisus