2012-03-29 4 views
12

Мои google-fu и so-fu меня не могут здесь, поэтому я мог бы также спросить.TSQL - Каков надлежащий порядок соединения таблиц?

У меня есть много запросов с несколькими объединениями в них.

В рамках одного запроса я соединяю заголовок/элемент/детали вместе, а также просматриваю различные биты информации для этих записей.

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

Верно ли это?

Лучше ли присоединяться к большим таблицам перед поисковыми таблицами? Или наоборот?

Должен ли я использовать подсказку loop при подключении к столикам и подсказку merge при подключении к openrowsets?

Я уверен, что ответ «это зависит», но некоторые общие рекомендации по эффективному и эффективному подключению были бы очень полезными. Благодаря!

+0

В большинстве случаев вам не нужно указывать тип соединения (merge/loop/hash). Если вы это сделаете, вероятно, будут лежащие в основе проблемы, которые следует решить, вместо того, чтобы объединить подсказку. –

+2

Если вы не знаете, что вы делаете, и вы понимаете планы выполнения запросов, НИКОГДА не используйте подсказки для запросов. – JotaBe

ответ

12

EDIT: По вашему запросу (и Kirk Woll's) комментарий, вы должны понимать, что order of your joins is aesthetic, and does not directly impact the resulting execution plan. Оптимизатор запросов будет выполнять соединения в наиболее эффективном порядке и использовать соответствующую операцию соединения большую часть времени без каких-либо намеков.

Когда приходит эстетика вашего заказа на объединение, это немного субъективно, но я бы сказал, что объединить ваши таблицы вместе в любом порядке имеет логический смысл при чтении запроса ... если вы начинаете с @HeaderId, начните с этой таблицей, а затем JOIN к столам детей:

FROM 
    Header h 
    JOIN Items i ON h.HeaderId = i.HeaderId 
    JOIN Details d ON i.ItemId = d.ItemId 
WHERE 
    h.HeaderId = @HeaderId 

Но, если вы начали свой запрос с @DetailId, я бы присоединиться в обратном порядке.

FROM 
    Details d 
    JOIN Items i ON d.ItemId = i.ItemId 
    JOIN Header h ON i.HeaderId = h.HeaderId 
WHERE 
    d.DetailId = @DetailId 

Однако, это субъективно и только мое личное предпочтение.

Это становится менее субъективным, когда вы начинаете включать OUTER JOIN s ... попытайтесь структурировать свой запрос, чтобы избежать любых RIGHT OUTER JOIN s, и вместо этого используйте LEFT OUTER JOIN.

Не используйте ссылки по умолчанию по умолчанию ... на самом деле вы почти никогда не будете их использовать. Оптимизатор запросов выполняет очень хорошую работу по выбору оптимального плана выполнения. Я столкнулся только с одним экземпляром, где мне нужно было дать подсказку о соединении для улучшения плана ... он очень сильно помог на сервере, на котором выполнялся запрос в то время, но когда база данных была перенесена на другой сервер, мой совет hint уничтожен производительность запроса. Таким образом, чтобы повторить, обычно бывает плохой идеей предоставить ссылки.

+1

Возможно, ОП не знает, что порядок присоединения не имеет отношения к плану выполнения и является чисто эстетичным? –

+0

@KirkWoll Это очень хороший момент, который я забыл ... Я уточню это в своем ответе. –

+0

@ Кирк, вот почему я спрашиваю. Заказ не имеет отношения вообще? – IronicMuffin

0

Заказ во многом [1] эстетический, но я считаю полезным иметь соглашение как для порядка таблиц, так и для условий в условиях ВКЛ, чтобы избежать путаницы.

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

[1] Я знаю одного случая, когда порядок имеет значение:

CREATE TABLE A (
    id int IDENTITY PRIMARY KEY, 
    name varchar 
); 

CREATE TABLE B (
    id int IDENTITY PRIMARY KEY, 
    a_id int FOREIGN KEY REFERENCES A (id), 
    name varchar 
); 

SELECT * 
FROM A 
INNER LOOP JOIN B on A.id = B.a_id; 

SELECT * 
FROM B 
INNER LOOP JOIN A on A.id = B.a_id; 

Первый выбор делает два индекса сканирования, второй делает сканирование и искать. Это еще одна веская причина, чтобы избежать намеков.