Почему люди так ненавидят SQL-курсоры?

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

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

В чем причина такого уровня безумной ненависти? Имеет ли какая-то "отмеченная власть" выдача фетвы против курсоров? Разве какое-то невыразимое зло скрывается в сердце курсоров, которое развращает мораль детей или что-то еще?

Вопрос Wiki, больше интересуется ответом, чем rep.

Дополнительная информация:

быстрые курсоры SQL Server

EDIT: позвольте мне уточнить: я понимаю, что курсоры не должны использоваться вместо обычных реляционных операций; это непросто. То, что я не понимаю, - это люди, которые собираются избегать курсоров, например, у них есть приемы или что-то еще, даже когда курсор является более простым и/или более эффективным решением. Это иррациональная ненависть, которая меня сбивает с толку, а не очевидная техническая эффективность.

Ответ 1

"Накладные расходы" с курсорами являются лишь частью API. Курсоры - это то, как части СУРБД работают под капотом. Часто CREATE TABLE и INSERT имеют операторы SELECT, а реализация - очевидная внутренняя реализация курсора.

Использование высокоуровневых "операторов на основе набора" объединяет результаты курсора в единый результирующий набор, что означает меньше API назад и вперед.

Курсоры предшествуют современным языкам, которые предоставляют первоклассные коллекции. Старые C, COBOL, Fortran и т.д. Должны обрабатывать строки по одному, потому что не было понятия "коллекция", которое можно было бы широко использовать. Java, С#, Python и т.д. Имеют первоклассные структуры списков, которые содержат результирующие наборы.

Медленный выпуск

В некоторых кругах реляционные объединения являются загадкой, и люди пишут вложенные курсоры, а не простое соединение. Я видел действительно эпические вложенные операции цикла, написанные как много-много курсоров. Преодоление оптимизации РСУБД. И работает очень медленно.

Простые SQL переписывают, чтобы заменить вложенные петли курсора на объединения, а один, плоский курсорный цикл может сделать программы за 100 раз. [Они думали, что я бог оптимизации. Все, что я сделал, это заменить вложенные петли на объединения. Все еще используемые курсоры.]

Эта путаница часто приводит к обвинительному заключению. Однако это не курсор, это неправильное использование курсора, что проблема.

Проблема с размером

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

Альтернативы

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

Ответ 2

Курсоры заставляют людей чрезмерно применять процессуальное мышление к среде с множеством.

И они SLOW!!!

От SQLTeam:

Обратите внимание, что курсоры - это МЕДЛЕННЫЙ способ доступа к данным внутри SQL Сервер. Его следует использовать только тогда, когда вам действительно нужно получить доступ к одной строке время. Единственная причина, по которой я могу думать для этого нужно вызвать хранимую процедуру на каждой строке. В Курсор Эффективная статья Я обнаружил что курсоры более тридцати раз медленнее, чем установленные альтернативы.

Ответ 3

Там есть ответ, в котором говорится, что "курсоры - это МЕДЛЕННЫЙ способ доступа к данным внутри SQL Server... курсоры более чем в тридцать раз медленнее, чем установленные альтернативы".

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

В отсутствие других действий с базой данных операции на основе набора становятся все быстрее. В производственных системах это зависит.

Ответ 4

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

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

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

Ответ 5

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

Ответ 6

В ответах выше недостаточно подчеркивается важность блокировки. Я не большой поклонник курсоров, потому что они часто приводят к блокировкам на уровне таблицы.

Ответ 7

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

Ответ 8

В Oracle PL/SQL курсоры не будут приводить к блокировке таблиц, и можно использовать сборку/сборку по объему.

В Oracle 10 часто используется неявный курсор

  for x in (select ....) loop
    --do something 
  end loop;

автоматически выводит по 100 строк за раз. Явная сборка/сборка навалом также возможна.

Однако курсоры PL/SQL - это что-то крайнее средство, используйте их, когда вы не можете решить проблему с SQL на основе набора.

Другая причина - распараллеливание, для базы данных проще распараллеливать большие операторы с множеством, чем строгий императивный код. Это по той же причине, почему функциональное программирование становится все более популярным (Haskell, F #, Lisp, С# LINQ, MapReduce...), функциональное программирование облегчает распараллеливание. Количество процессоров на компьютер растет, поэтому распараллеливание становится все более и более проблемой.

Ответ 9

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

Ответ 11

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

Ответ 12

Можете ли вы опубликовать этот пример курсора или ссылку на вопрос? Вероятно, это еще лучший способ, чем рекурсивный CTE.

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

Ответ 13

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

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

Ответ 14

basicaly 2 блока кода, которые делают то же самое. может быть, это немного странный пример, но это доказывает суть. SQL Server 2005:

SELECT * INTO #temp FROM master..spt_values
DECLARE @startTime DATETIME

BEGIN TRAN 

SELECT @startTime = GETDATE()
UPDATE #temp
SET number = 0
select DATEDIFF(ms, @startTime, GETDATE())

ROLLBACK 

BEGIN TRAN 
DECLARE @name VARCHAR

DECLARE tempCursor CURSOR
    FOR SELECT name FROM #temp

OPEN tempCursor

FETCH NEXT FROM tempCursor 
INTO @name

SELECT @startTime = GETDATE()
WHILE @@FETCH_STATUS = 0
BEGIN

    UPDATE #temp SET number = 0 WHERE NAME = @name
    FETCH NEXT FROM tempCursor 
    INTO @name

END 
select DATEDIFF(ms, @startTime, GETDATE())
CLOSE tempCursor
DEALLOCATE tempCursor

ROLLBACK 
DROP TABLE #temp

однократное обновление занимает 156 мс, а курсор занимает 2016 мс.