Как избежать дублирования FETCH в T-SQL при использовании курсора?
В T-SQL при повторении результатов из курсора представляется обычной практикой повторять оператор FETCH
перед циклом WHILE
. Ниже приведен пример из Microsoft:
DECLARE Employee_Cursor CURSOR FOR
SELECT EmployeeID, Title FROM AdventureWorks2012.HumanResources.Employee
WHERE JobTitle = 'Marketing Specialist';
OPEN Employee_Cursor;
FETCH NEXT FROM Employee_Cursor;
WHILE @@FETCH_STATUS = 0
BEGIN
FETCH NEXT FROM Employee_Cursor;
END;
CLOSE Employee_Cursor;
DEALLOCATE Employee_Cursor;
GO
(Обратите внимание, что FETCH NEXT FROM Employee_Cursor;
появляется дважды.)
Если FETCH
выбирает длинный список переменных, то мы имеем большой дублированный оператор, который является как уродливым, так и, конечно, кодом "не сухим".
Мне не известно о пост-условии-регуляторе T-SQL-инструкции, поэтому мне кажется, что мне нужно прибегнуть к WHILE(TRUE)
, а затем BREAK
, когда @@FETCH_STATUS
не равно нулю. Мне это неудобно.
Какие еще параметры у меня есть?
Ответы
Ответ 1
Проще говоря, вы не можете... это то, как работают большинство операторов SQL. Вам нужно получить первую строку перед циклом, а затем сделать это снова в инструкции while.
Лучший вопрос, как избавиться от курсора и попытаться решить ваш запрос без него.
Ответ 2
Там хорошая структура, размещенная онлайн Крис Олдвуд, которая делает это довольно элегантно:
DECLARE @done bit = 0
WHILE (@done = 0)
BEGIN
-- Get the next author.
FETCH NEXT FROM authors_cursor
INTO @au_id, @au_fname, @au_lname
IF (@@FETCH_STATUS <> 0)
BEGIN
SET @done = 1
CONTINUE
END
--
-- stuff done here with inner cursor elided
--
END
Ответ 3
Это то, к чему я прибегал (о позор его):
WHILE (1=1)
BEGIN
FETCH NEXT FROM C1 INTO
@foo,
@bar,
@bufar,
@fubar,
@bah,
@fu,
@foobar,
@another,
@column,
@in,
@the,
@long,
@list,
@of,
@variables,
@used,
@to,
@retrieve,
@all,
@values,
@for,
@conversion
IF (@@FETCH_STATUS <> 0)
BEGIN
BREAK
END
-- Use the variables here
END
CLOSE C1
DEALLOCATE C1
Вы можете понять, почему я задал вопрос. Мне не нравится, как управление потоком скрывается в инструкции if
, когда оно должно быть в while
.
Ответ 4
Первый Fetch
не должен быть Fetch next
, просто a Fetch
.
Тогда вы не повторяетесь.
Я потратил бы больше усилий, чтобы избавиться от курсора и меньше от СУХОЙ догмы (но если это действительно имеет значение, вы можете использовать GOTO
:) - Извините, М. Дейкстра)
GOTO Dry
WHILE @@FETCH_STATUS = 0
BEGIN
--- stuff here
Dry:
FETCH NEXT FROM Employee_Cursor;
END;
Ответ 5
Очевидно, что курсор является указателем на текущую строку в наборе записей. Но простое указание не имеет смысла, если оно не может быть использовано. Сюда входит оператор Fetch в сцену. Это берет данные из набора записей, сохраняет его в указанной переменной (ей). поэтому, если вы удалите первый оператор выборки, цикл while не будет работать, поскольку для манипуляции не будет записи "FETCHED", если вы удалите последний оператор выборки, "while" не будет проходить через петлю.
Таким образом, необходимо, чтобы оба оператора выборки перебирали полный набор записей.