Запрос против запроса — почему этот быстрее другого?

#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% за поиск по ключу. Не уверен, поможет ли что-нибудь из этого…