Оптимизация SQL запросов - курсоры

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

Очень часто курсоры используют в тех случаях, когда нужно выполнить две операции над одними и теми же данными. Например, нужно взять данные из одной таблицы, и обновить ими две другие. В этом случае почему-то создается курсор, который бежит по данным и обновляет построчно две таблицы. Это никогда не будет работать быстрее, чем два банальных UPDATE на две таблицы. Курсоры проиграют в скорости, да и наглядности программирования.

Что можно посоветовать, когда начинаете писать запрос - никогда не мыслите циклами. Если вы начинаете думать о циклах при решении задачи, вы будете использовать курсоры. Нужно в голове сразу представлять все данные таблицы и мыслить, как их можно все обновить разом.

Второй тормоз - это временные таблицы. Хуже вещи я еще не видел. 99,99999% задач можно решить без временных таблиц. Опять же, некоторые считают, что если нужно дважды выполнить операцию над временными данными, то лучше создать временную таблицу, перенести туда нужные данные и работать с ними. Фигня из под коня. Выгодней три раза выполнить SELECT для получения нужных данных, чем создавать временную таблицу.

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

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

  • Получить нужные данные во временную таблицу (запрос простейший и свистит на ура)
  • Скопировать нужные столбцы в таблицу 1
  • Скопировать нужные столбцы в таблицу 2
  • Фигануть данные из исходной таблицы

Я не понял, почему нельзя сразу фигачить данные в две архивные таблицы. Ну да, таблица транзакций состоит из 50 миллионов строк и выбор из этой таблицы 5 миллионов строк может быть не очень быстрой опирацией. Но вставка 5 миллионов строк во временную таблицу только ради обновления - это не жесть, это карозия металла. Сценарий фигачит уже 2 часа и ни конца не края этому нет.

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

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


Понравилось? Кликни Лайк, чтобы я знал, какой контент более интересен читателям. Заметку пока еще никто не лайкал и ты можешь быть первым


Комментарии

ronin

06 Мая 2010

Что можно посоветовать, когда начинаете писать запрос - никогда не мыслите циклами. Если вы начинаете думать о циклах при решении задачи, вы будете использовать курсоры. Нужно в голове сразу представлять все данные таблицы и мыслить, как их можно все обновить разом.


задумался.... надо будет попробовать... хотя иногда такие сложные процедуры расчёта и анализа проходят что я пока не представляю как это можно сделать без прохода в цикле по строкам таблицы источника, но может быть... заставил задуматься :)


Евгений

12 Мая 2010

Проход в цикле по строкам таблицы с помощью курсора - это может быть и жесть, и скучно, и долго, и неоптимально.
А теперь представьте процедуру, которая работает сразу с несколькими базами данных (какая-нибудь консолидированная отчётность). Плодить процедуры по количеству баз? В одной процедуре делать по нескольку копий каждого батча? Или лучше сделать цикл с обходом всех нужных БД, каждый раз составляя в стринге код запроса с нужным именем БД? Я выбираю последнее.
И против временных таблиц я ничего не имею. Если нужны примеры из жизни, где их применение обосновано (по моему мнению), я напрягусь и приведу.


Михаил Фленов

12 Мая 2010

Не всосал кайфа от твоего примера с отчетностью из нескольких баз. Отчетность - это запросы SELECT и курсоры в них использовать не вижу смысла.

Бывают случаи, когда действительно временные таблицы или курсоры нужны, но по моей практике это 0.0000000000000000000000001% из всех запросов.


Евгений

14 Мая 2010

"Отчетность - это запросы SELECT"
Ну, не знаю, что ты понимаешь под отчётностью, но то, с чем приходилось (и приходится) работать мне, одними селектами не сделаешь.
Но даже если и селекты, как, например, собрать данные из 10 баз? 10 аналогичных селектов? Или курсор и один динамически создаваемый в цикле запрос?


Михаил Фленов

14 Мая 2010

Я в таких случаях использовал репликацию. Если у тебя 10 серверов (возможно каждый обрабатывает свой филиал или другая фигня), то выделить еще один сервер под отчетность не проблема. В него собираешь данные репликацией с остальных серверов и на нем уже фигачишь запросы для отчетности. Так рабочие сервера практически не напрягаются при обработке твоей сложной логики, а просто отсылают изменения в данных серверу отчетности. А на сервере отчетности данные уже собраны, и не нужно так сильно напрягаться, поэтому отчеты будут работать быстрее:

1. не нужно тянуть данные с разных серверов (особенно, если они удаленные)
2. запросы проще и серверу жить проще

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


Костянчик

12 Августа 2015

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

Курсоры могут быть просто необходимы, когда выполняется несколько действий или одно, но с помощью ХП в массовых операциях. Здесь даже сложным UPDATE  не обойдешься. Уже не говорю о проверках, try..catch, raiserror.


Andrew

17 Aпреля 2020

Сами по себе курсоры не зло. Втыкание их везде и всюду - зло

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

Если сначала обычно разработчик смотрит план запроса и обеспечивает покрытие индексами, то все гут, если же сразу пишет все на курсорах и использует исключительно табличные переменные с количеством строк миллиона на два - я его бью клавиатурой по пальцам ))


Михаил Фленов

18 Aпреля 2020

Сами по себе конечно же не зло. Но заметка же называется "Оптимизация", а с точки зрения производительности курсоры не могут работать быстрее, чем select классический запрос. Курсоры мощнее, потому что с их помощью можно решить задачи, которые недоступны простому SELECT, но если есть возможность написать запрос без курсора, то желательно это делать.


Добавить Комментарий

Еще что-нибудь

Хотите найти еще что-то интересное почитать? Можно попробовать отфильтровать заметки на блоге по категориям.

О блоге

Программист, автор нескольких книг серии глазами хакера и просто блогер. Интересуюсь безопасностью, хотя хакером себя не считаю

Обратная связь

Без проблем вступаю в неразборчивые разговоры по e-mail. Стараюсь отвечать на письма всех читателей вне зависимости от страны проживания, вероисповедания, на русском или английском языке.

Пишите мне