#sql #sql-server #sql-server-2005 #query-optimization #sql-optimization
#sql #sql-сервер #sql-server-2005 #оптимизация запросов #sql-оптимизация
Вопрос:
У меня есть следующие два запроса — первый исходный, а второй — мое небольшое «обновление». Выполнение первого занимает почти секунду, а второй завершается до того, как я успеваю полностью убрать палец с кнопки обновления.
Мой вопрос: почему?
Единственное различие между первым и вторым заключается в том, что первый использует coalesce для получения значения для сравнения berp.ID_PART_SESSION с и второй использует объединение для объединения двух операторов select для выполнения одной и той же задачи.
Я все еще думаю, что первый должен быть быстрее (первоначальная причина, по которой я использовал coalesce), поскольку кажется, что он должен выполнять меньше работы, чтобы получить тот же результат. Учитывая, насколько слабо я разбираюсь в планах выполнения, не мог бы кто-нибудь, пожалуйста, объяснить, почему второй запрос намного лучше первого?
declare @animator varchar
SELECT TOP 1 @animator = FULL_NAME
FROM T_BERP berp
INNER JOIN dbo.T_INTERV i ON i.ID_INTERV = berp.ID_INTERV
WHERE berp.VERSION = 1
AND berp.PRINCIPAL = 1
AND berp.DELETED = 0
AND berp.CANCELLED = 0
AND berp.ID_PART_SESSION = (
select coalesce(pss.ID_PART_SESSION, psst.ID_PART_SESSION)
from t_bersp b
LEFT JOIN T_PART_SESSION pss ON b.ID_PART_SESSION = pss.ID_PART_SESSION
LEFT JOIN T_PSS_TEMP psst ON b.ID_PSS_TEMP = psst.ID_PSS_TEMP
where ID_BERSP = 4040)
против
declare @animator varchar
SELECT TOP 1 @animator = FULL_NAME
FROM dbo.T_BERP berp
INNER JOIN dbo.T_INTERV i ON i.ID_INTERV = berp.ID_INTERV
WHERE berp.VERSION = 1
AND berp.PRINCIPAL = 1
AND berp.DELETED = 0
AND berp.CANCELLED = 0
AND berp.ID_PART_SESSION IN (
select pss.ID_PART_SESSION
from dbo.t_bersp b
LEFT JOIN dbo.T_PART_SESSION pss ON b.ID_PART_SESSION = pss.ID_PART_SESSION
where ID_BERSP = 4040
union
select psst.ID_PART_SESSION
from dbo.t_bersp b
LEFT JOIN dbo.T_PSS_TEMP psst ON b.ID_PSS_TEMP = psst.ID_PSS_TEMP
where ID_BERSP = 4040)
Комментарии:
1. Всякий раз, когда мне нужно сравнить два запроса, обычно это простой первый шаг, чтобы посмотреть, как выглядит план выполнения . Я бы добавил план к вашему вопросу, как только он у вас будет.
2. Моей первоначальной мыслью было
=
вверху противIN
внизу для подзапроса, но это не коррелировано, поэтому я не думаю, что это должно иметь значение…3. У вас есть еще одно отличие в том, что в первом запросе используется ‘=’, а во втором — ‘IN’. Также в качестве несвязанного предупреждения использование TOP 1 без ORDER BY может привести к некоторым очень трудно отслеживаемым ошибкам. Порядок, в котором возвращаются несколько записей, не определен, если у вас нет order by . Обычно он будет соответствовать порядку сортировки индекса или физическому порядку на диске, но гарантий нет.
4. @RozWel — здесь есть эхо относительно
=
иIN
? 🙂5. У меня есть планы выполнения, но они представляют собой большие (~ 450 и ~ 375 строк) XML-файлы. Можно ли помещать их туда напрямую? Кажется, я не могу найти функцию вложения файла…
Ответ №1:
Было бы трудно дать окончательный ответ, не понимая относительных размеров и индексов различных таблиц в ваших запросах. Одна из возможностей: если t_part_session и t_pss_temp оба большие, оптимизатор запросов может делать что-то неэффективное с двумя левыми объединениями во внутреннем ВЫБОРЕ вашего первого запроса.
ОТРЕДАКТИРУЙТЕ, чтобы уточнить: Да, в обоих запросах есть левые соединения, но я предположил, что объединение двух вместе (запрос 1) может отрицательно повлиять на производительность по сравнению с ОБЪЕДИНЕНИЕМ (запрос 2). Извините, если это было непонятно изначально.
Кроме того, я настоятельно рекомендую такой инструмент, как Instant SQL Formatter (в сочетании со значком {} в редакторе StackOverflow), чтобы упростить чтение запросов в вашем вопросе:
DECLARE @animator VARCHAR
SELECT TOP 1 @animator = full_name
FROM t_berp berp
INNER JOIN dbo.t_interv i
ON i.id_interv = berp.id_interv
WHERE berp.version = 1
AND berp.principal = 1
AND berp.deleted = 0
AND berp.cancelled = 0
AND berp.id_part_session = (SELECT
Coalesce(pss.id_part_session, psst.id_part_session)
FROM t_bersp b
LEFT JOIN t_part_session pss
ON b.id_part_session =
pss.id_part_session
LEFT JOIN t_pss_temp psst
ON b.id_pss_temporaire =
psst.id_pss_temporaire
WHERE id_bersp = 4040)
против
DECLARE @animator VARCHAR
SELECT TOP 1 @animator = full_name
FROM dbo.t_berp berp
INNER JOIN dbo.t_interv i
ON i.id_interv = berp.id_interv
WHERE berp.version = 1
AND berp.principal = 1
AND berp.deleted = 0
AND berp.cancelled = 0
AND berp.id_part_session IN (SELECT pss.id_part_session
FROM dbo.t_bersp b
LEFT JOIN dbo.t_part_session pss
ON b.id_part_session =
pss.id_part_session
WHERE id_bersp = 4040
UNION
SELECT psst.id_part_session
FROM dbo.t_bersp b
LEFT JOIN dbo.t_pss_temp psst
ON b.id_pss_temporaire =
psst.id_pss_temporaire
WHERE id_bersp = 4040)
Комментарии:
1. -1 —
LEFT JOIN
оба запроса также присутствуют во втором запросе, поэтому ваш «ответ» в основном сводится к «Я не знаю, но, пожалуйста, лучше отформатируйте свой код»2. Да, левые соединения присутствуют в обоих запросах, но я предполагал, что совместное использование двух может отрицательно повлиять на производительность. Извините, если это было непонятно. Как бы то ни было, я согласен с комментарием Криса о том, что план выполнения будет иметь большое значение для решения этой проблемы.
3. Извините за это, я попытался отформатировать код и увидел, что он делает это с обратными кавычками, поэтому я заключил свой код в обратные кавычки, предполагая, что этого достаточно.
4. Не беспокойтесь и извините, если мой ответ показался вам высокомерным — очевидно, у меня недостаточно репутации, чтобы редактировать напрямую. Просто пытаюсь помочь. Не уверен, что я достаточно квалифицирован, чтобы прокомментировать, можно ли вставлять планы выполнения. 🙂 Кроме того, когда я вставил форматированный код из SQL Formatter, предварительный просмотр исказил его до тех пор, пока я не использовал {}, который выглядит так, как будто он просто сделал отступ в текстовом редакторе.
5. Очень признателен. По совпадению, этот разговор теперь обеспечил мне достаточную репутацию, чтобы я мог прокомментировать сообщение вместо того, чтобы просить разъяснений через ответ. Раньше у меня такого не было. 🙂
Ответ №2:
Держу пари, что это оператор coalesce . Я полагаю, что coalesce в конечном итоге будет применен перед предложением where . Итак, на самом деле он просматривает каждую комбинацию двух таблиц, а затем фильтрует те, которые соответствуют предложению where.
Комментарии:
1. Это был бы невероятно глупый способ для SQL Server обрабатывать вещи… Возможно, вы правы, и это объяснило бы загадку, но меня удивило бы, что такая неоптимальная вещь может быть выполнена.
Ответ №3:
Вы можете поместить оба запроса в один пакет в SSMS и показать план выполнения — это не только позволит вам видеть их рядом, но и покажет относительную стоимость.
Я подозреваю, что IN (ОБЪЕДИНЕНИЕ) означает, что второй может быть легко распараллелен.
Комментарии:
1. Ну, это еще более запутанно — это говорит мне о том, что стоимость первого (и самого медленного) запроса (относительно пакета) составляет 27%, а второго запроса — 73%.
2. @Alex In Paris это фактический или расчетный? Использует ли второй параллелизм? Как выглядит фактическая статистика для операций чтения?
3. У меня было выбрано «Включить фактический план выполнения». Я нигде не вижу упоминания о parallel. В первом запросе есть один элемент, из которого исходит подсказка — сканирование кластеризованного индекса с затратой 42% от стоимости этого запроса. 35% для поиска по кластеризованному индексу. Второй запрос имеет одну большую стоимость элемента: 74% за поиск по ключу. Не уверен, поможет ли что-нибудь из этого…