Как избежать дублирования выборки в 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
не равна нулю. Мне это кажется неуклюжим.
какие еще варианты у меня есть?
5 ответов
просто сказал, что нельзя... именно так работает большинство операторов where в SQL. Вам нужно получить первую строку перед циклом, а затем сделать это снова в инструкции while.
лучший вопрос, Как избавиться от курсора и попытаться решить ваш запрос без него.
есть хорошая структура, размещенная в интернете Крис Oldwood что делает это довольно элегантно:
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
вот к чему я прибегнул (о, позор!):
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
.
первый Fetch
не должно быть Fetch next
, просто fetch
.
тогда вы не повторяетесь.
Я бы потратил больше усилий на избавление от курсора и меньше на сухую догму (но если это действительно имеет значение, вы можете использовать GOTO
:) - Извините, М. Дейкстра)
GOTO Dry
WHILE @@FETCH_STATUS = 0
BEGIN
--- stuff here
Dry:
FETCH NEXT FROM Employee_Cursor;
END;
очевидно, что курсор-это указатель на текущую строку в наборе записей. Но простое указание не имеет смысла, если его нельзя использовать. Здесь приходит заявление Fetch в сцену. Это берет данные из набора записей, сохраняет их в предоставленных переменных. поэтому, если вы удалите первый оператор fetch, цикл while не будет работать, поскольку нет" извлеченной " записи для манипуляции, если вы удалите последний оператор fetch," while " не будет проходить цикл.
Так это необходимо иметь оба оператора fetch для цикла через полный набор записей.