【问题标题】:solving a problem with cursors解决游标问题
【发布时间】:2010-08-31 12:19:27
【问题描述】:

我有一个问题。我正在研究游标。每次,在获取最后一条记录并打印其数据后,游标会打印一个附加行。要理解我的意思,请考虑以下示例: 我只想打印关于 10 个客户的信息。

USE Northwind
GO

DECLARE myCursor CURSOR 
FOR SELECT TOP(10) ContactName FROM Customers
DECLARE @RowNo int,@ContactName nvarchar(30)
SET @RowNo=1
OPEN myCursor
FETCH NEXT FROM myCursor INTO @ContactName
PRINT  LEFT(CAST(@rowNo as varchar) + '      ',6)+'  '+ @ContactName
SET @RowNo=@RowNo+1
SET @ContactName=''
WHILE @@FETCH_STATUS=0
  BEGIN
        FETCH NEXT FROM myCursor INTO @ContactName
        PRINT + LEFT(CAST(@rowNo as varchar) + '      ',6)+'  '+ @ContactName
        SET @RowNo=@RowNo+1
        SET @ContactName=''
  END
CLOSE myCursor
DEALLOCATE myCursor

现在看看输出:

1       Maria Anders
2       Ana Trujillo
3       Antonio Moreno
4       Thomas Hardy
5       Christina Berglund
6       Hanna Moos
7       Frédérique Citeaux
8       Martín Sommer
9       Laurence Lebihan
10      Elizabeth Lincoln
11      

第 11 行也已打印。它是光标中的问题还是总是发生? 有没有办法不打印这个添加数据?谢谢 (我使用 sql erver 2008)

【问题讨论】:

    标签: sql-server sql-server-2008 cursors


    【解决方案1】:

    要么...

    FETCH NEXT FROM myCursor INTO @ContactName
    WHILE @@FETCH_STATUS = 0
    BEGIN
        -- do stuff
    
        FETCH NEXT FROM myCursor INTO @ContactName
    END
    

    或者……

    WHILE @@FETCH_STATUS = 0
    BEGIN
        FETCH NEXT FROM myCursor INTO @ContactName
        IF @@FETCH_STATUS = 0
        BEGIN
            -- do stuff
        END
    END
    

    或者……

    WHILE (1 = 1)
    BEGIN
        FETCH NEXT FROM myCursor INTO @ContactName
        IF @@FETCH_STATUS <> 0
            BREAK
    
        -- do stuff
    END
    

    【讨论】:

    • +1,我喜欢WHILE (1 = 1)的版本,只有一个'fetch`,它在循环的顶部,所以CONTINUE可以使用
    • WHILE 1=1 FETCH/BREAK 是要走的路。
    • 是的,我这辈子都去哪儿了?我一直使用第一种形式,并且讨厌冗余。 (对不起,我知道这个评论对于@KM 来说有点多余。)
    【解决方案2】:

    您提到您使用的是 SQL Server 2008。使用 SQL Server 2005 或更高版本,您根本不需要游标来做您想做的事情。

    select top 10 left(cast(row_number() over(order by ContactName) as varchar)+ '      ', 6) + ContactName
        from Customers
    

    【讨论】:

    • 谢谢,我开始想知道为什么每个人都在告诉她如何修复光标,而不是为什么她根本不应该使用它!不使用游标的原因以及如何避免它们:wiki.lessthandot.com/index.php/Cursors_and_How_to_Avoid_Them
    • @HLGEM,该示例可能已被删减以更好地说明问题。在任何情况下,光标都有自己的位置。 SQL Server 社区教条地回避它们到无知的地步。
    • @Peter,我们避开它们是因为它们通常表现不佳,而且它们的代码几乎总是比您可以使用的基于集合的逻辑更复杂。当有更好的技术可用时学习使用性能不佳的技术是愚蠢的,它也涉及更简单的代码。她所说的一切都不需要光标。如果她在做更复杂的事情,那么她可以这么说。
    • @HLGEM:向我展示一个比游标更方便或更高效的非集合迭代结构。例如:您需要为集合 y 中的每一行调用一次过程 x,获取输出参数值并记录异常。你用什么作为循环结构?创建一个带有标识列的表变量并循环它?这并不比静态游标好,而且更啰嗦。你所保留的只是基于集合的纯度。他们有他们的位置。无知使他们的使用效率低下。
    • 好吧,除了系统 procs,我永远不会考虑为一组值一次调用一个 procs,我编写基于集合的 procs 而不是单个行 procs,现在您可以使用表变量作为输入变量没有记录器编写插入一条记录的proc的借口。
    【解决方案3】:

    看看你是如何复制打印逻辑的?这是一个指向哪里出了问题的指针。你的循环应该是这样的:

    FETCH NEXT INTO @working_variables
    WHILE @@FETCH_STATUS = 0
        -- process @working_variables
        FETCH NEXT INTO @working_variables
    

    唯一重复的代码应该是 FETCH NEXT 本身 - 你现在拥有它的方式,最后一个 FETCH 发生,但你 PRINTWHILE 之前的一行可以退出。

    【讨论】:

      【解决方案4】:

      记录集末尾的 FETCH 将 @@FETCH_STATUS 设置为非 0。

      FETCH NEXT 命令应该是 WHILE BLOCK 中的最后一行。

      USE Northwind
      GO
      
      DECLARE myCursor CURSOR 
      FOR SELECT TOP(10) ContactName FROM Customers
      DECLARE @RowNo int,@ContactName nvarchar(30)
      SET @RowNo=0
      OPEN myCursor
      FETCH NEXT FROM myCursor INTO @ContactName
      WHILE @@FETCH_STATUS=0
        BEGIN
      
              SET @RowNo=@RowNo+1
              SET @ContactName=''
              PRINT + LEFT(CAST(@rowNo as varchar) + '      ',6)+'  '+ @ContactName
              FETCH NEXT FROM myCursor INTO @ContactName
        END
      CLOSE myCursor
      DEALLOCATE myCursor
      

      【讨论】:

        【解决方案5】:

        这是一个不相上下的错误。这是一种更好的方法来遍历游标,减少代码重复:

        USE Northwind
        GO
        
        DECLARE myCursor CURSOR 
        FOR SELECT TOP(10) ContactName FROM Customers
        DECLARE @RowNo int,@ContactName nvarchar(30)
        SET @RowNo=0 -- initialize counters at zero, increment after the fetch/break
        OPEN myCursor
        WHILE 1=1 BEGIN -- start an infinite loop
          FETCH NEXT FROM myCursor INTO @ContactName
          IF @@FETCH_STATUS <> 0 BREAK
          SET @RowNo=@RowNo+1
          PRINT  LEFT(CAST(@rowNo as varchar) + '      ',6)+'  '+ @ContactName
        END
        CLOSE myCursor
        DEALLOCATE myCursor
        

        对于额外的点,使用游标变量并声明 w/ FAST_FORWARD 和 TYPE_WARNING,或 STATIC 用于小型数据集。例如:

        DECLARE @cursor CURSOR
        SET @cursor = CURSOR FAST_FORWARD TYPE_WARNING FOR
          SELECT TOP (10) ContactName FROM Customers
        OPEN @cursor 
        ......
        CLOSE @cursor
        DEALLOCATE @cursor
        

        CLOSE 和 DEALLOCATE 不是绝对必要的,因为游标变量将在批处理结束时超出范围。但是,它仍然是一种很好的形式,因为您可能会在稍后添加更多代码,并且您应该尽早释放资源。

        如果请求的类型与您的 SELECT 语句不兼容,则 TYPE_WARNING 会告诉您 SQL Server 何时将请求的游标类型 (FAST_FORWARD) 隐式转换为另一种类型(通常是 STATIC)。

        【讨论】:

        • LOL downvote 没有解释。光标仇恨?如果某处出现技术错误,请告知。
        • 看起来像在特定时间得到一个驱动下投票之前给出的所有 4ish 答案。在这里,有一个 +1。
        猜你喜欢
        • 2022-01-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-08-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多