Какой из двух способов кодирования внутреннего соединения быстрее? - PullRequest
6 голосов
/ 09 июля 2009

Я предпочитаю кодировать в t-sql, используя то, что фактически является встроенным соединением, а не иметь длинный список объединений в конце хранимой процедуры или представления.

Например, я кодирую:

SELECT      PKey  ,    Billable, 
    (SELECT LastName  FROM Contact.dbo.Contacts WHERE (Pkey = Contacts_PKey)),
    (SELECT Description FROM Common.dbo.LMain WHERE (PKey= DType)),  
    (SELECT  TaskName  FROM  Common.dbo.LTask WHERE  (PKey =  TaskType)) ,  
    StartTime,  EndTime,  SavedTime
FROM   dbo.TopicLog   where  StartTime > '7/9/09'  ORDER BY  StartTime

Вместо

SELECT t.PKey, t.Billable, c.LastName, m.Description, lt.TaskName, t.StartTime, t.EndTime, t.SavedTime
FROM dbo.TopicLog AS t     
inner join  Contact.dbo.Contacts as c   on  c.Pkey = t.Contacts_PKey and t.StartTime > '7/9/09'
inner join  Common.dbo.LMain  as m  on  m.PKey = t.DType
inner join  Common.dbo.LTask  as lt on lt.PKey = t.TaskType
ORDER BY t.StartTime

Я предпочитаю этот тип синтаксиса, потому что он намного меньше сбивает с толку при записи или отладке, особенно когда происходит объединение многих таблиц или других вещей (операторы case, функции t-sql, self-join и т. Д.) *

Но мой вопрос - я испытываю снижение производительности, обращаясь к базе данных таким образом.

У меня еще недостаточно собранных данных, чтобы можно было измерить разницу, но я буду в какой-то момент в будущем.

Я хотел бы узнать, прежде чем продолжить. Я не хотел бы возвращаться позже и перекодировать все, чтобы улучшить производительность.

Ответы [ 8 ]

20 голосов
/ 09 июля 2009

Второй (фактическое внутреннее соединение), как правило. Первый (подзапросы) выполняет 3 запроса для каждой строки, но обычно этим управляет компилятор, чтобы различия были смягчены.

Лучше всего: Проверьте планы выполнения запроса для себя!

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

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

10 голосов
/ 09 июля 2009

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

3 голосов
/ 09 июля 2009

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

Второй пример выглядит нормально.

Вы должны знать, что старый способ выполнения внутренних соединений выглядит так:

SELECT t.PKey, t.Billable, 
 c.LastName, m.Description, lt.TaskName, 
 t.StartTime, t.EndTime, t.SavedTime
FROM 
 dbo.TopicLog as t, Contact.dbo.Contacts as c, 
 Common.dbo.LMain as m,  Common.dbo.LTask as lt   
WHERE c.Pkey = t.Contacts_PKey and t.StartTime > '7/9/09'
  AND m.PKey = t.DType
  AND lt.PKey = t.TaskType
ORDER BY  t.StartTime

И, по-видимому, это эквивалентно современному синтаксису "внутреннего соединения таблица в поле " после его анализа.

Как говорит другой ответ, если вы ищете более быстрые запросы, первое, что нужно сделать, это проверить, что индексы таблиц отсортированы. Затем посмотрите на план выполнения запроса.

1 голос
/ 13 июля 2009

Вообще говоря, нет разницы в производительности простых подзапросов по сравнению с объединениями - распространенное заблуждение, что подзапросы намного медленнее (поскольку SQL-сервер должен проходить через внутренний запрос), однако, вообще говоря, это просто неверно! Во время процесса компиляции SQL-сервер создает дерево выполнения, и часто в этих деревьях подзапросы эквивалентны объединениям.

Стоит отметить, что ваши два запроса не являются логически одинаковыми и дали разные результаты для меня, второй запрос должен действительно что-то читать по строкам: (это все еще не идентично, но ближе)

SELECT t.PKey, t.Billable, c.LastName, m.Description, lt.TaskName, t.StartTime, t.EndTime, t.SavedTime
FROM dbo.TopicLog AS t     
LEFT OUTER JOIN Contact.dbo.Contacts as c   on  c.Pkey = t.Contacts_PKey
LEFT OUTER JOIN Common.dbo.LMain  as m  on  m.PKey = t.DType
LEFT OUTER JOIN Common.dbo.LTask  as lt on lt.PKey = t.TaskType
WHERE t.StartTime > '7/9/09'
ORDER BY t.StartTime

В моем тестировании подзапрос выдал план выполнения с резко меньшим числом операций чтения (15 по сравнению с 1000), но немного более высоким процессором - в среднем время выполнения было примерно эквивалентным.

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

1 голос
/ 09 июля 2009

Два запроса в OP говорят о разных вещах и дают одинаковые результаты только при наличии правильных предположений модели данных:

  1. Каждый из столбцов, используемых в поиске, не имеет ограничений NULL и ограничений внешнего ключа.

  2. Используется первичный ключ или уникальный ключ таблицы соответствия.

Может быть, в конкретном случае ОП эти предположения верны, но в общем случае они отличаются.

Как уже отмечали другие, подзапрос больше похож на внешнее соединение, так как он возвращает нулевые значения для столбцов LastName, Description и Taskname вместо полной фильтрации строки.

Кроме того, если один из подзапросов возвращает более одной строки, вы получите сообщение об ошибке.

Что касается личных предпочтений, я предпочитаю второй пример с синтаксисом соединения, но это субъективно.

0 голосов
/ 13 июля 2009

Я думаю, что второй выполняется быстрее. Причина этого заключается в использовании псевдонима (t, c, m и т. Д. В вашем примере). Реляционный движок имен может легко найти указатель на расположение таблицы.

Я думаю, что это один из советов по настройке SQL.

0 голосов
/ 09 июля 2009

Многие программисты SQL совершенно не знают, что оптимизатор часто преобразует подзапросы в объединения. Вероятно, нет причин для проблем производительности в любом запросе.

Просмотр плана выполнения!

0 голосов
/ 09 июля 2009

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...