6

単純化すると、2 つのテーブルがcontactsあり、donotcall

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

DoNotCall 電話の特定のリストの電話番号と一致する連絡先を確認したいと思います。検索を高速化するために、 と にインデックスを付けましdonotcallた。list_idphone

次の JOIN を作成すると、長い時間がかかります (例: 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)  

実行計画のスクリーンショット

Pastebin での実行計画

一方、各電話フィールドで個別に LEFT JOIN を実行すると、はるかに高速に実行されます (例: 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

実行計画のスクリーンショット

Pastebin での実行計画

私の推測では、最初のスニペットは のインデックスを利用していないため、実行速度が遅いということdonotcallです。
では、複数の列に対して結合を行い、それでもインデックスを使用するにはどうすればよいでしょうか?

4

2 に答える 2

6

SQL Server はIN (c.phone1, c.phone2, c.phone3, c.phone4)、インデックスを使用した解決はコストが高すぎると考える場合があります。

ヒントを使用して、インデックスが高速になるかどうかをテストできます。

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)

投稿したクエリ プランから、最初のプランが 40,000 行を生成すると見積もられていることが示されていますが、21 行しか返されません。2 番目のプランは 1 行を見積もります (もちろん 21 も返します)。

あなたの統計は最新ですか?古い統計は、クエリ アナライザーが不適切な選択を行っていることを説明している可能性があります。統計は、自動的に、または毎週のジョブで更新する必要があります。次の方法で統計の年齢を確認します。

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

次の方法で手動で更新できます。

EXEC sp_updatestats;
于 2013-09-04T12:47:43.997 に答える
0

この貧弱なデータベース構造では、UNION ALL クエリが最も高速な場合があります。

于 2013-09-04T17:59:04.470 に答える