2013-09-04 2 views
5

Упрощенных, у меня есть две таблицы, contacts и donotcallиспользования Make индекса при JOIN'ing против нескольких столбцов

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_id и phone.

Когда я делаю следующее РЕГИСТРИРУЙТЕСЬ это занимает много времени (например, 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

Хотя если я 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 

Screenshot of execution plan

Execution plan on Pastebin

Мое предположение о том, что первый фрагмент кода работает медленно, потому что он не использует индекс на donotcall.
Итак, как сделать присоединение к нескольким столбцам и все еще использовать индекс?

+0

Что вам на самом деле нужно сделать, это исправить структуру данных datbase. У вас НИКОГДА не должно быть телефона1, phone2, phone3, phone4 - это означает, что вам нужен детский стол. – HLGEM

+0

@ HLGEM: Точка отмечена. Я бы сделал это по-другому, если бы у меня был выбор. Но иногда вы застряли со структурой, которую создали другие, без надежды на рефакторинг. – ANisus

ответ

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) 

Из запроса планов вы вывесили, это показывает первый план оценивается для получения 40K строк, но он просто возвращает 21 строк. Второй план оценивает 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-сервер не может разрешить код с помощью индекса. – ANisus

+2

Вы пытались сравнить планы запросов? (Запрос меню, а затем Включить фактический план выполнения.) – Andomar

+0

Я пытаюсь это сделать, но у меня очень мало опыта в анализе планов запросов.Из того, что я вижу, решение LEFT JOIN довольно прямолинейно, используя 4 Hash Matches (Right Outer Joins). Единственное решение JOIN делает это совершенно по-другому. Две вложенные петли (Внутренние соединения), сначала с индексом, а затем с таблицей контактов. – ANisus

0

С этой бедной структурой базы данных, союзная ALL запрос может быть быстрым.

+0

Хотя UNION ALL в настоящее время работает лучше, чем одно решение JOIN, оно немного медленнее, чем решение LEFT JOIN. – ANisus

Смежные вопросы