Как избежать дублирования 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" не будет проходить через петлю.

Таким образом, необходимо, чтобы оба оператора выборки перебирали полный набор записей.