Почему SQL Server возвращает некоторые строки при выполнении запроса, а иногда нет?

33

Есть запросы, где когда мы нажимаем «выполнить», он показывает несколько строк и продолжает расти, но запрос еще не закончен. И все же иногда он ждет до конца запроса.

Почему это происходит? Есть ли способ контролировать это?

Racer SQL
источник

Ответы:

43

Ответ, как обычно (хорошо, большую часть времени), лежит в плане выполнения.

Есть определенные операторы, которые требуют, чтобы все строки достигли их, прежде чем они смогут начать обрабатывать эти строки и передавать их в нисходящем направлении, например:

  • Hash Join (при построении хеш-таблицы)
  • Хэш Матч
  • Сортировка (за исключением хеш-потока)

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

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

  • Вложенные циклы
  • Индекс поддерживает объединение объединений
  • Агрегаты потоков

Когда запросы начинают немедленно возвращать данные, но не завершаются немедленно, это обычно является признаком того, что оптимизатор выбрал план для быстрого поиска и возврата некоторых строк с помощью операторов, которые имеют более низкие начальные затраты.

Это может произойти из-за целей строки, введенных вами или оптимизатором.

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

Для получения дополнительной информации, проверьте блог Роба Фарли здесь

И серия Пола Уайта о целях ряда здесь , здесь , здесь и здесь .

Следует также отметить, что, если вы говорите о SSMS, строки появляются только после того, как весь буфер заполнен, а не только невольно.

Эрик Дарлинг
источник
14

Если я понимаю, что вы наблюдаете, это то, как Management Studio отображает строки и не имеет ничего общего с тем, как SQL Server возвращает строки. На самом деле, часто, когда вы возвращаете большие результаты в SSMS и пытаетесь отобразить их в виде сетки, SSMS не может успевать, и SQL Server завершает ожидание, пока приложение обработает больше строк. В этом случае вы увидите, что SQL Server накапливает ASYNC_NETWORK_IOожидания.

Вы можете в некоторой степени управлять им, используя Результаты в текст вместо Результатов в сетку, поскольку SSMS может рисовать текст быстрее, чем сетку, но вы, вероятно, обнаружите, что это может повлиять на читабельность в зависимости от количества столбцов и используемых типов данных. На то и другое влияет то, когда SSMS решает фактически записать результаты в эту панель, что зависит от того, насколько заполнен выходной буфер.

Если у вас есть несколько операторов, и вы хотите принудительно заставить буфер отображать результаты вывода на панели сообщений, вы можете использовать небольшую хитрость печати между операторами:

RAISERROR('', 0, 1) WITH NOWAIT;

Но это не поможет, когда вы пытаетесь заставить SSMS рендерить строки быстрее, когда весь вывод поступает из одного оператора.

Более прямо, вы можете контролировать его, ограничивая количество результатов, которые вы отображаете в SSMS. Я часто вижу, как люди жалуются на то, как долго возвращается миллион строк в таблицу. Что, черт возьми, кто-то собирается делать с миллионами строк в сетке SSMS, я понятия не имею.

Есть несколько хаков типа OPTION (FAST 100), которые оптимизируют для извлечения этих первых 100 строк (или любых 100 строк, если нет внешних ORDER BY), но это может происходить за счет гораздо более медленного извлечения для остальных строк и плана, который более неэффективный в целом, так что на самом деле не вариант перехода ИМХО.

Аарон Бертран
источник
1

Ваш вопрос не о SQLServer как таковом, а о:

  • SQLServer
  • сеть
  • SSMS как клиентское приложение

Есть ли способ контролировать это?

Краткий ответ :

  1. Попробуй sqlcmdвместо ssmsили sqlcmd-режимssms
  2. Проверьте настройки подключения и сеанса

Длинный ответ :

Конечно! Но не один - проб

  1. Выполните ваш запрос с помощью sqlcmdили sqlcmdв -mode в ssms.
  2. Если вы хотите исключить роль сети - запустите запрос на сервере с подключением к общей памяти.
  3. Если производительность запроса неудовлетворительна даже при подключении к общей памяти - проанализируйте свои планы выполнения. Если запрос выполняется по сети - обратитесь к администратору сети за помощью. Если ваш запрос работает плохо только в SSMS - читайте дальше.
  4. Теперь мы уверены, что проблемы на стороне клиента (в данном случае ssms). Посмотрите на настройки соединения и сессии в SSMS. Не верьте интерфейсу ssms и проверьте с помощью SQL Profiler: найдите ваше соединение spidи вы получите полный список настроек сеанса. Сравните с настройками sqlcmdсессии. Если ничего не щёлкнет - скопируйте все настройки сеанса из профилировщика в скрипт запроса, выполните sqlcmdкоманду -mode и, постепенно переключая настройки, вы обнаружите своего виновника.

Удачи!

Алекс Ю
источник
-2

Чтобы добавить к ответу sp_BlitzErik, возьмите пример, используя a NOT IN ()с дополнительным выбором. Чтобы определить, является ли элемент результатом вложенного запроса, (как правило) необходимо получить весь результат.

Таким образом, я нашел простой способ улучшить производительность таких запросов - переписать их как LEFT OUTER JOINусловие with, где RIGHTside имеет значение null (конечно, вы можете перевернуть его, но кто его использует RIGHT OUTER JOINS?). Это позволяет сразу начать возвращать результаты.

JimmyJames
источник
Я так не думаю. Если сравниваемые столбцы не обнуляются, то результаты должны быть одинаковыми, а планы - обычно - одинаковыми для 3-х версий противодействия (NOT IN, NOT EXISTS, LEFT JOIN / IS NULL). Нет необходимости извлекать весь результат.
ypercubeᵀᴹ
Если подвыбор действительно сложный, то произведенный запрос должен оценить весь подвыбор перед проверкой условия NOT IN WHERE t.x IN (<complex SELECT subquery>), эквивалентного LEFT JOIN LEFT JOIN (<complex SELECT subquery>) AS r ON r.x = t.x .... WHERE r.x IS NULL, тогда подзапрос также будет оценен (такой же сложный план с NOT В версии).
ypercubeᵀᴹ
@ ypercubeᵀᴹ Это работало для меня в прошлом. Я видел, как запросы уходят от минут до возврата в секунду.
JimmyJames
@ ypercubeᵀᴹ Я собрал простой пример в Oracle (извините, у меня нет доступа к SQLServer в настоящее время), и у них определенно были разные планы объяснения. Возможно, они не были значимыми различиями, но они выглядят довольно по-другому.
JimmyJames
@JimmyJames: это не простой способ, если вам нужна стабильная производительность, и такие «оптимизации» очень чувствительны для версии SQLServer. И не делайте ошибку, обращаясь к Oracle (какая версия?). Исторически SQLServer предпочитал, NOT EXISTSно Oracle NOT INв запросах. Но сегодня это должно рассматриваться как ошибка в генераторе планов
Alex Yu