Курсор внутри курсора - PullRequest
27 голосов
/ 22 января 2009

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

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

Часть запроса выглядит следующим образом:

Fetch NEXT FROM OUTER_CURSOR INTO @CONTACT_ID,  @TYPE
While (@@FETCH_STATUS <> -1)
BEGIN
IF (@@FETCH_STATUS <> -2)

    DECLARE INNER_CURSOR Cursor 
    FOR 
    SELECT * FROM CONTACTS
    where CONTACT_ID = @CONTACT_ID
    and TYPE = @TYPE 

    Open INNER_CURSOR 

    Fetch NEXT FROM INNER_CURSOR 
    While (@@FETCH_STATUS <> -1)
    BEGIN
    IF (@@FETCH_STATUS <> -2)

В чем может быть проблема? Является ли @@ FETCH_STATUS двусмысленным или чем-то еще?

РЕДАКТИРОВАТЬ: все выглядит хорошо, если я не использую этот код внутри внутреннего курсора:

UPDATE CONTACTS
SET INDEX_NO = @COUNTER
where current of INNER_CURSOR

РЕДАКТИРОВАТЬ: вот большая картина:

BEGIN TRAN

DECLARE @CONTACT_ID VARCHAR(15)
DECLARE @TYPE VARCHAR(15)
DECLARE @INDEX_NO  SMALLINT
DECLARE @COUNTER SMALLINT
DECLARE @FETCH_STATUS INT 

DECLARE OUTER_CURSOR CURSOR 

FOR 

SELECT CONTACT_ID, TYPE, INDEX_NO FROM CONTACTS
WHERE  
CONTACT_ID IN (SELECT CONTACT_ID FROM dbo.CONTACTS
WHERE CONTACT_ID IN(...)
GROUP BY CONTACT_ID, TYPE, INDEX_NO
HAVING COUNT(*) > 1

OPEN OUTER_CURSOR 

FETCH NEXT FROM OUTER_CURSOR INTO @CONTACT_ID,  @TYPE, @INDEX_NO
WHILE (@@FETCH_STATUS <> -1)
BEGIN
IF (@@FETCH_STATUS <> -2)

SET @COUNTER = 1

        DECLARE INNER_CURSOR CURSOR 
        FOR 
        SELECT * FROM CONTACTS
        WHERE CONTACT_ID = @CONTACT_ID
        AND TYPE = @TYPE 
        FOR UPDATE 

        OPEN INNER_CURSOR 

        FETCH NEXT FROM INNER_CURSOR 

        WHILE (@@FETCH_STATUS <> -1)
        BEGIN
        IF (@@FETCH_STATUS <> -2)

        UPDATE CONTACTS
        SET INDEX_NO = @COUNTER
        WHERE CURRENT OF INNER_CURSOR

        SET @COUNTER = @COUNTER + 1

        FETCH NEXT FROM INNER_CURSOR 
        END
        CLOSE INNER_CURSOR
        DEALLOCATE INNER_CURSOR

FETCH NEXT FROM OUTER_CURSOR INTO @CONTACT_ID,  @TYPE, @INDEX_NO
END
CLOSE OUTER_CURSOR
DEALLOCATE OUTER_CURSOR

COMMIT TRAN

Ответы [ 6 ]

62 голосов
/ 22 января 2009

У вас множество проблем. Во-первых, почему вы используете свои конкретные значения @@ FETCH_STATUS? Это должно быть просто @@ FETCH_STATUS = 0.

Во-вторых, вы не выбираете свой внутренний Курсор в . И я не могу придумать ни одного обстоятельства, при котором вы бы выбрали все поля таким образом - изложите их!

Вот образец, чтобы пройти. Папка имеет первичный ключ «ClientID», который также является внешним ключом для Attend. Я просто распечатываю все идентификаторы посещений с разбивкой по папке ClientID:

Declare @ClientID int;
Declare @UID int;

DECLARE Cur1 CURSOR FOR
    SELECT ClientID From Folder;

OPEN Cur1
FETCH NEXT FROM Cur1 INTO @ClientID;
WHILE @@FETCH_STATUS = 0
BEGIN
    PRINT 'Processing ClientID: ' + Cast(@ClientID as Varchar);
    DECLARE Cur2 CURSOR FOR
        SELECT UID FROM Attend Where ClientID=@ClientID;
    OPEN Cur2;
    FETCH NEXT FROM Cur2 INTO @UID;
    WHILE @@FETCH_STATUS = 0
    BEGIN
        PRINT 'Found UID: ' + Cast(@UID as Varchar);
        FETCH NEXT FROM Cur2 INTO @UID;
    END;
    CLOSE Cur2;
    DEALLOCATE Cur2;
    FETCH NEXT FROM Cur1 INTO @ClientID;
END;
PRINT 'DONE';
CLOSE Cur1;
DEALLOCATE Cur1;

Наконец, вы УВЕРЕНЫ вы хотите делать что-то подобное в хранимой процедуре? Очень легко злоупотреблять хранимыми процедурами и часто отражает проблемы, характеризующие вашу проблему. Например, пример, который я привел, может быть гораздо проще выполнить с помощью стандартных вызовов select.

9 голосов
/ 22 января 2009

Вы также можете обойти проблемы с вложенными курсорами, общие проблемы с курсорами и проблемы с глобальными переменными, полностью исключив курсоры.

declare @rowid int
declare @rowid2 int
declare @id int
declare @type varchar(10)
declare @rows int
declare @rows2 int
declare @outer table (rowid int identity(1,1), id int, type varchar(100))
declare @inner table (rowid int  identity(1,1), clientid int, whatever int)

insert into @outer (id, type) 
Select id, type from sometable

select @rows = count(1) from @outer
while (@rows > 0)
Begin
    select top 1 @rowid = rowid, @id  = id, @type = type
    from @outer
    insert into @innner (clientid, whatever ) 
    select clientid whatever from contacts where contactid = @id
    select @rows2 = count(1) from @inner
    while (@rows2 > 0)
    Begin
        select top 1 /* stuff you want into some variables */
        /* Other statements you want to execute */
        delete from @inner where rowid = @rowid2
        select @rows2 = count(1) from @inner
    End  
    delete from @outer where rowid = @rowid
    select @rows = count(1) from @outer
End
2 голосов
/ 22 января 2009

Это пахнет чем-то, что должно быть сделано вместо JOIN. Вы можете поделиться с нами большей проблемой?


Эй, я должен быть в состоянии свести это к одному утверждению, но у меня еще не было времени поиграть с ним еще сегодня и, возможно, не получится. Тем временем знайте, что вы должны иметь возможность отредактировать запрос для своего внутреннего курсора, чтобы создать номера строк как часть запроса, используя функцию ROW_NUMBER () . Оттуда вы можете сложить внутренний курсор во внешний, выполнив INNER JOIN (вы можете присоединиться к подзапросу). Наконец, любой оператор SELECT может быть преобразован в UPDATE, используя этот метод:

UPDATE [YourTable/Alias]
   SET [Column] = q.Value
FROM
(
   ... complicate select query here ...
) q

Где [YourTable/Alias] - таблица или псевдоним, используемые в запросе выбора.

2 голосов
/ 22 января 2009

Ты что-нибудь еще делаешь? Вы должны показать это также. Вы показываете нам только половину кода.

Это должно выглядеть так:

FETCH NEXT FROM @Outer INTO ...
WHILE @@FETCH_STATUS = 0
BEGIN
  DECLARE @Inner...
  OPEN @Inner
  FETCH NEXT FROM @Inner INTO ...
  WHILE @@FETCH_STATUS = 0
  BEGIN
  ...
    FETCH NEXT FROM @Inner INTO ...
  END
  CLOSE @Inner
  DEALLOCATE @Inner
  FETCH NEXT FROM @Outer INTO ...
END
CLOSE @Outer
DEALLOCATE @Outer

Кроме того, убедитесь, что вы не называете курсоры одинаковыми ... и любой код (проверьте ваши триггеры), который вызывается, не использует курсор с таким же именем. Я видел странное поведение людей, использующих theCursor в нескольких слоях стека.

1 голос
/ 17 февраля 2017

У меня была такая же проблема,

что вам нужно сделать, это объявить второй курсор как: ОБЪЯВИТЬ [second_cursor] Курсор МЕСТНЫЙ Для

Вы видите «КУРСОР МЕСТНЫЙ ДЛЯ» вместо «КУРСОР ДЛЯ»

0 голосов
/ 23 января 2009

Я не до конца понимаю, в чем проблема с «обновлением тока курсора» , но она решается двойным использованием оператора fetch для внутреннего курсора:

FETCH NEXT FROM INNER_CURSOR

WHILE (@@FETCH_STATUS <> -1)
BEGIN

UPDATE CONTACTS
SET INDEX_NO = @COUNTER
WHERE CURRENT OF INNER_CURSOR

SET @COUNTER = @COUNTER + 1

FETCH NEXT FROM INNER_CURSOR
FETCH NEXT FROM INNER_CURSOR
END
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...