【问题标题】:Cursor inside cursor光标内的光标
【发布时间】:2010-10-02 21:53:25
【问题描述】:

主要问题是将行的索引更改为 1,2,3.. 其中联系人 ID 和类型相同。但所有列都可以包含完全相同的数据,因为一些前雇员搞砸了,并按联系人 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

【问题讨论】:

  • 还有一个问题:什么版本的sql server,因为这将决定我们可以使用什么来创建行号来替换内部游标中的@counter。
  • 你知道基于集合的逻辑吗?游标应该作为最后的手段......
  • 那么您能提供一个答案吗?如果您需要有关情况的更多信息,我很乐意提供。

标签: sql sql-server tsql sql-server-2005 database-cursor


【解决方案1】:

你还做更多的抓取吗?你也应该展示这些。您只向我们展示了一半的代码。

应该是这样的:

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】:

你有各种各样的问题。首先,为什么要使用特定的 @@FETCH_STATUS 值?它应该只是@@FETCH_STATUS = 0。

其次,你没有选择你的内部光标进入任何东西。而且我想不出任何情况下您会以这种方式选择所有字段 - 拼写出来!

这是一个示例。文件夹有一个主键“ClientID”,它也是Attend 的外键。我只是打印所有的参加 UID,按文件夹 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 调用更容易地完成。

【讨论】:

  • 我没有选择任何内容,因为我只是想更新内部游标的当前行。我必须使用 @somevariable 吗?
  • 对于每个“Contact_ID”,您的联系人表中是否有多个记录?如果是这样,你的“拥有”是虚假的——它永远是虚假的。如果没有,那没关系。我只是想弄清楚你的逻辑。另外,在have子句之后不应该有一个结束的“)”吗?
  • Yikes - 您正在逐个记录地重新分配到 INDEX_NO 字段,但仅适用于那些即使在考虑旧 INDEX_NO 后仍存在倍数的记录?
  • 是的,虽然数据很敏感,但我无法删除或更新任何其他列。
  • 是的 index_no 成了问题。但是运行几次查询就解决了这个问题。
【解决方案3】:

您还可以通过完全避免游标来回避嵌套游标问题、一般游标问题和全局变量问题。

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 行或更多行可以包含完全相同的数据或不同的数据,所以您不能冒险丢失。您的此解决方案的问题是无法将数据与外部表匹配。
【解决方案4】:

这闻起来应该用 JOIN 代替。您能与我们分享更大的问题吗?


嘿,我应该可以把它归结为一个语句,但我今天还没有时间进一步研究它,可能无法达到。同时,您应该能够使用ROW_NUMBER() 函数编辑内部游标的查询以创建行号作为查询的一部分。从那里,您可以通过对其执行 INNER JOIN 将内部光标折叠到外部光标(您可以加入子查询)。最后,任何 SELECT 语句都可以使用此方法转换为 UPDATE:

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

其中[YourTable/Alias] 是选择查询中使用的表或别名。

【讨论】:

    【解决方案5】:

    我不完全理解 "update current of cursor" 的问题是什么,但通过对内部游标使用两次 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
    

    【讨论】:

      【解决方案6】:

      我遇到了同样的问题,

      您要做的就是将第二个游标声明为: DECLARE [second_cursor] 本地光标

      您看到的是“CURSOR LOCAL FOR”而不是“CURSOR FOR”

      【讨论】:

      • LOCAL 应该只是一个范围修饰符,它将光标可用性限制在当前过程范围而不是当前连接范围。不确定这将如何影响上述问题。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-01-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多