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