2009-01-22 2 views
22

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

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

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

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 двусмысленным или что-то еще?

EDIT: все выглядит нормально, если я не использую этот код внутри внутреннего курсора:

UPDATE CONTACTS 
SET INDEX_NO = @COUNTER 
where current of INNER_CURSOR 

EDIT: здесь большая картина:

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 
+0

Еще один вопрос: какая версия SQL-сервера, потому что это определит, что мы можем использовать для создания номеров строк, чтобы заменить ваш @counter во внутреннем курсоре. –

+0

Знаете ли вы, что логика основана на множестве? Курсоры должны использоваться в качестве последнего средства ... –

+0

Итак, можете ли вы быть достаточно любезны, чтобы предложить ответ? если вам нужна дополнительная информация о ситуации, я с удовольствием предоставляю ее. –

ответ

-1

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

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 
2

ли вы какие-либо другие получений фидов? Вы должны показать их также. Вы только показываете нам половину кода.

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

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» в нескольких слоях стека.

+0

Остальная часть запроса выглядит так, как вы описали. имена курсоров и выборки правильно названы. –

54

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

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

Вот образец, который нужно пройти. Папка имеет первичный ключ «ClientID», который также является внешним ключом для Attend. Я просто печатать все ПРИСУТСТВУЙТЕ UIDs, с разбивкой по папкам 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 [email protected]; 
    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; 

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

+0

Я не выбираю ничего, потому что я просто хотел обновить текущую строку внутреннего курсора. мне нужно использовать в @somevariable? –

+0

У вас есть несколько записей в таблице контактов для каждого «Contact_ID»? Если это так, ваше «наличие» является ложным - оно всегда будет ложным. Если нет, тогда не возражайте. Я просто пытаюсь понять вашу логику. Кроме того, не должно быть закрытия ")" после предложения оговорки? –

+0

Yikes - вы переназначаете поле INDEX_NO на основе записи по записи, но только для тех записей, где есть кратные даже после учета старого INDEX_NO? –

8

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

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 
+0

Спасибо, много приятного примера! проблема в том, что я должен использовать текущую строку курсора. потому что думать о 2 или более строках может содержать точно такие же данные или разные данные, которые вы не можете потерять. проблема с вашим решением заключается в невозможности сопоставления данных с внешней таблицей. –

2

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


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

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

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

0

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

, что вы должны сделать, это объявить второй курсор, как: DECLARE [second_cursor] курсор LOCAL для

вы видите "CURSOR LOCAL дЛЯ" вместо "CURSOR FOR"

+0

LOCAL должен быть просто модификатором диапазона, который ограничивает доступность курсора к текущей области действия, а не к текущей области подключения. Не знаете, как это повлияет на вышеупомянутую проблему. – Milan

Смежные вопросы