Как же я ненавижу курсоры в SQL запросах. Меня просто колбасит, и конвульсит когда я вижу запросы, в которых для банального обновления данных используются курсоры. курсоры тормозят выполнения запросы и базы данных очень сильно ограничены в попытках оптимизировать их выполнения, поэтому к данному способу работы с данными нужно прибегать в самую последнюю очередь.
Очень часто курсоры используют в тех случаях, когда нужно выполнить две операции над одними и теми же данными. Например, нужно взять данные из одной таблицы, и обновить ими две другие. В этом случае почему-то создается курсор, который бежит по данным и обновляет построчно две таблицы. Это никогда не будет работать быстрее, чем два банальных UPDATE на две таблицы. Курсоры проиграют в скорости, да и наглядности программирования.
Что можно посоветовать, когда начинаете писать запрос - никогда не мыслите циклами. Если вы начинаете думать о циклах при решении задачи, вы будете использовать курсоры. Нужно в голове сразу представлять все данные таблицы и мыслить, как их можно все обновить разом.
Второй тормоз - это временные таблицы. Хуже вещи я еще не видел. 99,99999% задач можно решить без временных таблиц. Опять же, некоторые считают, что если нужно дважды выполнить операцию над временными данными, то лучше создать временную таблицу, перенести туда нужные данные и работать с ними. Фигня из под коня. Выгодней три раза выполнить SELECT для получения нужных данных, чем создавать временную таблицу.
Очень и очень редко запросы SELECT выполняются на столько долго, что действительно приходится создавать временные таблицы. Но если у вас такой случай, то следуюет подумать на счет оптимизации запроса и таблиц. Возможно, у вас проблема с индексами или фигня запрос, но сразу же бросаться на создание таблиц не стоит. Временные таблицы - это не способ упрощения жизни, это крайний метод оптимизации, который нужно использовать в последнюю очередь, когда действительно SQL запрос невозможно переписать, а денормализовывать данные не представляется возможным.
Наверно поэтому я в своей книге Transact-SQL очень мало внимания уделял курсорам и временным таблицам. Я вообще в своей работе не использую их. Просто сейчас сижу на работе и засыпаю, пока запрос в курсоре обновит через временную таблицу миллион строк в базе данных транзакций и перенесет их в архив. Просто при переносе в архив, нужно распределять данные по двум таблицам, и чтобы сделать это, кто-то написал сценарий:
Я не понял, почему нельзя сразу фигачить данные в две архивные таблицы. Ну да, таблица транзакций состоит из 50 миллионов строк и выбор из этой таблицы 5 миллионов строк может быть не очень быстрой опирацией. Но вставка 5 миллионов строк во временную таблицу только ради обновления - это не жесть, это карозия металла. Сценарий фигачит уже 2 часа и ни конца не края этому нет.
Я написал книгу по Transact-SQL, но если мне сейчас скажут написать сценарий с курсорами, то без мануала я не смогу написать даже строчки. Я не помню, как это делается, потому что прибегаю к этому способу работы с данными только в последнюю очередь.
Однажды видел сценарий, который написал очень хороший программист и в нем банальное обновление данных одной таблицы шло через курсор. Запрос просто затирал одно поле во всей таблице случайным значением и без каких либо условий, затирание шло всей таблицы. Причем в этом же сценарии другая таблица затиралась без курсора простым update, но эта таблица была уже маленькой. Я не понял смысла, нафига усложнять себе жизнь в первом случае.
Понравилось? Кликни Лайк, чтобы я знал, какой контент более интересен читателям. Заметку пока еще никто не лайкал и ты можешь быть первым
Проход в цикле по строкам таблицы с помощью курсора - это может быть и жесть, и скучно, и долго, и неоптимально.
А теперь представьте процедуру, которая работает сразу с несколькими базами данных (какая-нибудь консолидированная отчётность). Плодить процедуры по количеству баз? В одной процедуре делать по нескольку копий каждого батча? Или лучше сделать цикл с обходом всех нужных БД, каждый раз составляя в стринге код запроса с нужным именем БД? Я выбираю последнее.
И против временных таблиц я ничего не имею. Если нужны примеры из жизни, где их применение обосновано (по моему мнению), я напрягусь и приведу.
Не всосал кайфа от твоего примера с отчетностью из нескольких баз. Отчетность - это запросы SELECT и курсоры в них использовать не вижу смысла.
Бывают случаи, когда действительно временные таблицы или курсоры нужны, но по моей практике это 0.0000000000000000000000001% из всех запросов.
"Отчетность - это запросы SELECT"
Ну, не знаю, что ты понимаешь под отчётностью, но то, с чем приходилось (и приходится) работать мне, одними селектами не сделаешь.
Но даже если и селекты, как, например, собрать данные из 10 баз? 10 аналогичных селектов? Или курсор и один динамически создаваемый в цикле запрос?
Я в таких случаях использовал репликацию. Если у тебя 10 серверов (возможно каждый обрабатывает свой филиал или другая фигня), то выделить еще один сервер под отчетность не проблема. В него собираешь данные репликацией с остальных серверов и на нем уже фигачишь запросы для отчетности. Так рабочие сервера практически не напрягаются при обработке твоей сложной логики, а просто отсылают изменения в данных серверу отчетности. А на сервере отчетности данные уже собраны, и не нужно так сильно напрягаться, поэтому отчеты будут работать быстрее:
1. не нужно тянуть данные с разных серверов (особенно, если они удаленные)
2. запросы проще и серверу жить проще
Может я чего не знаю о твоей структуре, но когда я работал с распределенными данными, репликация очень сильно упрощала жизнь.
Есть простой пример необходимости временных таблиц и табличных переменных. Например в какой-то процедуре нужно получить выборку из бд с помощью другой процедуры и использовать далее. Например нужно проверить, вернет ли ХП строки. В этом случае результат нужно будет куда-то сохранить, чтобы обратиться к нему как к таблице, а не как к ХП.
Курсоры могут быть просто необходимы, когда выполняется несколько действий или одно, но с помощью ХП в массовых операциях. Здесь даже сложным UPDATE не обойдешься. Уже не говорю о проверках, try..catch, raiserror.
Сами по себе курсоры не зло. Втыкание их везде и всюду - зло
Инструментарий достаточно развит, для того чтобы использовать правильные запросы, правильные индексы, в некоторых вычислительных случаях CTE, временные таблицы или табличные переменные
Если сначала обычно разработчик смотрит план запроса и обеспечивает покрытие индексами, то все гут, если же сразу пишет все на курсорах и использует исключительно табличные переменные с количеством строк миллиона на два - я его бью клавиатурой по пальцам ))
Сами по себе конечно же не зло. Но заметка же называется "Оптимизация", а с точки зрения производительности курсоры не могут работать быстрее, чем select классический запрос. Курсоры мощнее, потому что с их помощью можно решить задачи, которые недоступны простому SELECT, но если есть возможность написать запрос без курсора, то желательно это делать.
Хотите найти еще что-то интересное почитать? Можно попробовать отфильтровать заметки на блоге по категориям.