Производительность запросов к SQL Server - устранение необходимости в хешировании (внутреннее объединение) - PullRequest
20 голосов
/ 28 августа 2011

У меня следующий запрос, который выполняет очень мало и является примером того типа соединений, которые я выполняю во всей системе.

select t1.PrimaryKeyId, t1.AdditionalColumnId
from TableOne t1
    join TableTwo t2 on t1.ForeignKeyId = t2.PrimaryKeyId
    join TableThree t3 on t1.PrimaryKeyId = t3.ForeignKeyId
    join TableFour t4 on t3.ForeignKeyId = t4.PrimaryKeyId
    join TableFive t5 on t4.ForeignKeyId = t5.PrimaryKeyId
where 
    t1.StatusId = 1
    and t5.TypeId = 68

Индексы есть во всех столбцах соединения, однако производительность невелика. Проверка плана запроса выявляет много совпадений по хэшу (внутренних объединений), когда я действительно хочу увидеть присоединения с вложенным циклом.

Количество записей в каждой таблице:

select count(*) from TableOne

= 64393

select count(*) from TableTwo

= 87245

select count(*) from TableThree

= 97141

select count(*) from TableFour

= 116480

select count(*) from TableFive

= 62

Как лучше всего повысить производительность запросов такого типа?

Ответы [ 2 ]

28 голосов
/ 28 августа 2011

Первые мысли:

  1. Изменить на EXISTS (меняет equi-join на semi-join)
  2. У вас должны быть индексы для t1.StatusId, t5.TypeId и INCLUDE t1.AdditionalColumnID

Я бы пока не беспокоился о твоем методе соединения ...

Лично я никогда не использовал подсказку JOIN. Они работают только с теми данными, индексами и статистикой, которые у вас есть на данный момент. По мере того, как они меняются, ваша подсказка JOIN ограничивает оптимизатор

select t1.PrimaryKeyId, t1.AdditionalColumnId
from
    TableOne t1
where 
    t1.Status = 1
    AND EXISTS (SELECT *
        FROM
          TableThree t3
          join TableFour t4 on t3.ForeignKeyId = t4.PrimaryKeyId
          join TableFive t5 on t4.ForeignKeyId = t5.PrimaryKeyId
        WHERE
          t1.PrimaryKeyId = t3.ForeignKeyId
          AND
          t5.TypeId = 68)
    AND EXISTS (SELECT *
        FROM
          TableTwo t2
        WHERE
          t1.ForeignKeyId = t2.PrimaryKeyId)

Индекс для tableOne .. один из

  • (Status, ForeignKeyId) INCLUDE (AdditionalColumnId)
  • (ForeignKeyId, Status) INCLUDE (AdditionalColumnId)

Индекс для tableFive ... вероятно (typeID, PrimaryKeyId)

Редактировать: обновлены СОЕДИНЕНИЯ и СУЩЕСТВОВАНИЕ для соответствия исправлениям вопросов

10 голосов
/ 28 августа 2011

SQL Server довольно хорош в оптимизации запросов, но он также консервативен: он оптимизирует запросы для худшего случая. Соединение цикла обычно приводит к поиску по индексу и поиску закладок для каждой строки. Поскольку объединения циклов приводят к значительному ухудшению качества больших наборов, SQL Server не решается использовать их, если не уверен в количестве строк.

Вы можете использовать подсказку запроса forceseek , чтобы вызвать поиск индекса:

inner join TableTwo t2 with (FORCESEEK) on t1.ForeignKeyId = t2.PrimaryKeyId

Кроме того, вы можете принудительно соединить цикл с помощью ключевого слова loop:

inner LOOP join TableTwo t2 on t1.ForeignKeyId = t2.PrimaryKeyId

Советы по запросу ограничивают свободу SQL Server, поэтому он больше не может адаптироваться к изменившимся обстоятельствам. Рекомендуется избегать подсказок по запросу, если нет бизнес-потребностей, которые невозможно удовлетворить без них.

...