Курсоры SQL…Какие варианты использования вы бы защищали?

StackOverflow https://stackoverflow.com/questions/172526

  •  05-07-2019
  •  | 
  •  

Вопрос

Я пойду первым.

Я на 100% в лагере постановщиков.Но что происходит, когда установленная логика для всего требуемого входного домена приводит к такому большому объему поиска, что выполнение запроса значительно замедляется, доходит до обхода или, по сути, занимает бесконечное время?

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

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

Это было полезно?

Решение

Конечно, есть ряд мест, где курсоры могут быть лучше, чем операции на основе наборов.

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

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

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

Другие советы

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

Бывают также случаи, когда курсоры просто превосходят по эффективности, потому что оптимизатор недостаточно умен.В таких случаях либо у вас в голове есть метаинформация, которая просто не раскрывается оптимизатору через индексы или статистику в таблицах, либо код настолько сложен, что соединения (и, как правило, повторные соединения) просто не могут быть оптимизированы таким образом, чтобы вы могли визуализировать их с помощью курсора.Я полагаю, что в SQL Server 2005 CTE, как правило, делают это намного проще в коде, но трудно понять, считает ли оптимизатор их также более простыми - все сводится к сравнению плана выполнения с тем, как, по вашему мнению, это можно было бы сделать наиболее эффективно, и выполнению вызова.

Общее правило - не используйте курсор без необходимости.Но когда это необходимо, не расстраивайтесь по этому поводу.

Есть много разных поведение курсора.

  • СТАТИЧЕСКИЙ НАБОР КЛЮЧЕЙ против ДИНАМИЧЕСКОГО
  • ПРОКРУТКА ПРОТИВ ТОЛЬКО ПЕРЕМОТКИ ВПЕРЕД против БЫСТРОЙ ПЕРЕМОТКИ ВПЕРЕД
  • БЕСЧУВСТВЕННЫЙ или нет
  • ОПТИМИСТИЧНЫЙ, или ТОЛЬКО ДЛЯ ЧТЕНИЯ, или нет
  • ЛОКАЛЬНЫЙ против ГЛОБАЛЬНОГО (по крайней мере, это легко)

Ты должен никогда не используйте курсор если только ты не сможешь объясните все эти варианты и какие из них включено по умолчанию.

И вот, я никогда этого не делаю.

Вместо этого, когда я чувствую желание зациклиться на чем-то в T-SQL...Я загружаю его в таблицу переменных, которая представляет собой что-то вроде ЛОКАЛЬНОГО СТАТИЧЕСКОГО курсора ПРОКРУТКИ...за исключением того , что он может быть проиндексирован и объединен (править:и обратная сторона предотвращения использования параллелизма).

Очень редко вы получаете операцию, для которой требуется курсор, но в T-SQL это довольно редко.Столбцы Identity (int) или последовательности упорядочивают вещи определенным образом в рамках операций set.Агрегации, в которых вычисления могут изменяться в определенные моменты (например, накопление утверждений с нуля до предельной или избыточной точки), по своей сути процедурны, поэтому они являются кандидатами на роль курсора.

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

Вместе с чем Дэвид Б сказал, что я тоже предпочитаю циклический / табличный подход.

Учитывая это, один из вариантов использования курсоров и подхода "цикл / таблица" предполагает чрезвычайно большие обновления.Допустим, вам нужно обновить 1 миллиард строк.Во многих случаях это может не обязательно быть транзакцией.Например, это может быть агрегированное хранилище данных, где у вас есть возможность восстановить данные из исходных файлов, если что-то пойдет не так.

В этом случае, возможно, лучше всего выполнять обновление "порциями", возможно, по 1 миллиону или 10 миллионам строк за раз.Это помогает свести использование ресурсов к минимуму и позволяет максимально увеличить одновременное использование компьютера при обновлении этого миллиарда строк.Возможно, здесь лучше всего подходит зацикленный / фрагментированный подход.Обновления миллиардов строк на менее чем звездном оборудовании, как правило, вызывают проблемы.

В чистой среде SQL я бы предпочел избегать курсоров, как вы предлагаете.Но как только вы переходите на процедурный язык (например, PL / SQL), появляется ряд применений.Например, если вы хотите получить определенные строки и хотите "сделать" что-то более сложное, чем обновлять их с их помощью.

Курсоры также удобны, когда вы хотите запустить системный процесс несколько раз с разными входными значениями.У меня нет намерения пытаться переписывать системные процедуры на основе настроек, поэтому тогда я буду использовать курсор.Кроме того, вы обычно просматриваете очень ограниченное количество объектов.Вы можете сделать то же самое с существующей процедурой, которая вставляет только одну запись за раз, но с точки зрения производительности это обычно плохо, если вам нужно просмотреть много записей.Те, которые я перепишу, будут основаны на наборе.

Подсчет итогов, как обсуждалось другими, может быть более быстрым.

Если вы отправляете электронное письмо из базы данных (не лучшая идея, но иногда именно с этим вы сталкиваетесь), то курсор может гарантировать, что клиент a не увидит адрес электронной почты клиента b, когда вы отправляете одно и то же электронное письмо.

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

Но почему вы вообще выполняете такого рода работу в хранимой процедуре?Действительно ли это лучшее использование вашего сервера базы данных?Действительно ли T-SQL является подходящим языком для использования при генерации кода?

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

Если таблица по какой-либо причине неиндексирована, курсор будет быстрее, чем другие методы перебора по таблице.Я нашел эту информацию в это сообщение в блоге о курсорах в SQL Server в прошлом году.

Хотя автор выступает за подход "использовать только в крайнем случае" (как и все присутствующие здесь), она находит пару случаев, когда курсоры работают так же хорошо, как и другие доступные альтернативы (включая текущие итоги, указанные Робертом Россни).Среди других интересных замечаний, которые она делает, она указывает, что курсоры работают более эффективно внутри хранимых процедур, чем в виде специальных запросов.Автор также проделывает отличную работу, указывая, когда начинают возникать проблемы с производительностью, которые мы все связываем с курсорами.

Запись в блоге содержит фактический код, поэтому читатели могут сами попробовать запросы и увидеть результаты.

Ну, одна из операций, где курсоры лучше, чем наборы, - это вычисление текущего итога и тому подобное.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top