Ответы:
Ответ, как обычно (хорошо, большую часть времени), лежит в плане выполнения.
Есть определенные операторы, которые требуют, чтобы все строки достигли их, прежде чем они смогут начать обрабатывать эти строки и передавать их в нисходящем направлении, например:
Из-за этого их либо называют блокирующими, либо останавливают операторов, и их часто выбирают, когда оптимизатор считает, что для их поиска придется обработать целый ряд данных.
Существуют другие операторы, которые могут начать потоковую передачу или сразу передать найденные строки
Когда запросы начинают немедленно возвращать данные, но не завершаются немедленно, это обычно является признаком того, что оптимизатор выбрал план для быстрого поиска и возврата некоторых строк с помощью операторов, которые имеют более низкие начальные затраты.
Это может произойти из-за целей строки, введенных вами или оптимизатором.
Это также может произойти, если по какой-то причине будет выбран плохой план (отсутствие SARGability, анализ параметров, недостаточная статистика и т. Д.), Но для выяснения этого потребуется больше усилий.
Для получения дополнительной информации, проверьте блог Роба Фарли здесь
И серия Пола Уайта о целях ряда здесь , здесь , здесь и здесь .
Следует также отметить, что, если вы говорите о SSMS, строки появляются только после того, как весь буфер заполнен, а не только невольно.
Если я понимаю, что вы наблюдаете, это то, как 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
), но это может происходить за счет гораздо более медленного извлечения для остальных строк и плана, который более неэффективный в целом, так что на самом деле не вариант перехода ИМХО.
Ваш вопрос не о SQLServer как таковом, а о:
Есть ли способ контролировать это?
Краткий ответ :
sqlcmd
вместо ssms
или sqlcmd
-режимssms
Длинный ответ :
Конечно! Но не один - проб
sqlcmd
или sqlcmd
в -mode в ssms.spid
и вы получите полный список настроек сеанса. Сравните с настройками sqlcmd
сессии. Если ничего не щёлкнет - скопируйте все настройки сеанса из профилировщика в скрипт запроса, выполните sqlcmd
команду -mode и, постепенно переключая настройки, вы обнаружите своего виновника.Удачи!
Чтобы добавить к ответу sp_BlitzErik, возьмите пример, используя a NOT IN ()
с дополнительным выбором. Чтобы определить, является ли элемент результатом вложенного запроса, (как правило) необходимо получить весь результат.
Таким образом, я нашел простой способ улучшить производительность таких запросов - переписать их как LEFT OUTER JOIN
условие with, где RIGHT
side имеет значение null (конечно, вы можете перевернуть его, но кто его использует RIGHT OUTER JOINS
?). Это позволяет сразу начать возвращать результаты.
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 В версии).
NOT EXISTS
но Oracle NOT IN
в запросах. Но сегодня это должно рассматриваться как ошибка в генераторе планов