web-dev-qa-db-ja.com

カーソル内のカーソル

主な問題は、行のインデックスを1,2,3に変更することです。contact-idとtypeは同じです。ただし、元従業員が混乱してすべての行をcontact-idとtypeで更新するため、すべての列にまったく同じデータを含めることができます。どういうわけか、混乱しない行がありますが、インデックス行は同じです。それは完全な混乱です。

私は、外部カーソルから来る変数で内部カーソルを使用しようとしました。しかし、それは内部カーソルに引っかかっているようです。

クエリの一部は次のようになります。

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
27
Orkun Balkancı

"カーソルの現在の更新"の問題を完全には理解していませんが、内部カーソルにfetchステートメントを2回使用することで解決します。

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
0
Orkun Balkancı

あなたにはさまざまな問題があります。まず、特定の@@ FETCH_STATUS値を使用しているのはなぜですか? @@ FETCH_STATUS = 0である必要があります。

第二に、内部カーソルを選択していないinto何もありません。そして、この方法ですべてのフィールドを選択する状況を考えることはできません-それらを綴ってください!

以下にサンプルを示します。フォルダには、「出席者」の外部キーでもある「ClientID」のプライマリキーがあります。 Folder Attendidで分類されたすべてのAttend UIDを印刷しています。

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;

最後に、あなたは[〜#〜] sure [〜#〜]ストアドプロシージャでこのようなことをしたいですか?ストアドプロシージャを悪用するのは非常に簡単で、多くの場合、問題の特徴を明らかにする際の問題を反映しています。たとえば、私が提供したサンプルは、標準のselect呼び出しを使用してはるかに簡単に実現できます。

62

また、カーソルを完全に回避することで、ネストされたカーソルの問題、一般的なカーソルの問題、グローバル変数の問題を回避できます。

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
10
cmsjr

これ以上フェッチしますか?それらも表示する必要があります。コードの半分しか表示していません。

次のようになります。

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」を使用している人々から奇妙な動作を見てきました。

2
Amy B

これは、代わりにJOINで実行する必要がある何かのにおいがします。より大きな問題を私たちと共有できますか?


ねえ、私はこれを1つのステートメントにまとめることができるはずですが、私は今日まだそれで遊ぶ時間がありませんでしたし、到達することはできません。それまでの間、内部カーソルのクエリを編集して、 ROW_NUMBER() 関数を使用してクエリの一部として行番号を作成できることを知っておいてください。そこから、INNER JOINを実行することで、内部カーソルを外部カーソルに折り畳むことができます(サブクエリで結合できます)。最後に、次のメソッドを使用して、SELECTステートメントをUPDATEに変換できます。

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

どこ[YourTable/Alias]は、選択クエリで使用されるテーブルまたはエイリアスです。

2
Joel Coehoorn

同じ問題がありました

2番目のカーソルを次のように宣言する必要があります。DECLARE [second_cursor] Cursor LOCAL For

「CURSOR FOR」の代わりに「CURSOR LOCAL FOR」が表示されます

1
Julian Salinas