【问题标题】:MS SQL Server - When is a CURSOR good?MS SQL Server - CURSOR 什么时候好?
【发布时间】:2010-12-01 13:39:36
【问题描述】:

很多时候我写过存储过程等。我一开始使用 CURSOR,后来发现我的过程存在一些性能问题。

我读到的每一篇文章都说 CURSORS 很糟糕,会导致不必要的锁定等,性能测试也证明了这一点。

我的问题是你什么时候使用 CURSOR 以及它们在什么情况下有用或有用?

如果没有用,他们为什么要为 SQL 制作这么糟糕的控制结构/类型?

【问题讨论】:

  • @any:谁将此问题标记为“完全重复”,您会发布原始问题的链接吗?然后我将投票关闭“完全重复”。但在那之前,我发现这个问题是信息性的。

标签: sql sql-server tsql database-cursor


【解决方案1】:

通常应避免使用它们,但该功能的存在是有原因的,并且有时会使用它们。我想说我见过的 90% 以上的游标是不需要的。如果您将它们用于 CRUD 操作,则几乎总是可以以基于集合的方式重做。我经常看到人们为此使用游标,因为他们不知道如何在更新或删除中使用连接,或者他们可以在插入中使用选择语句而不是值子句。当人们认为他们需要它们进行稍微复杂的处理而实际上可以通过 case 语句轻松处理时,这是另一种不必要的用途。

游标有时会更快地计算诸如运行总计之类的东西。

游标对于存储过程的多次执行也很方便,该过程设置为一次只处理一个输入值。我不使用此功能来运行用户存储的过程(除非我知道我会遇到非常小的数据集),但是当需要针对多个表运行系统过程时,它对于数据库管理员来说非常方便。

如果您在 SQl 中创建电子邮件(不是最好的地方,但在他们这样做的某些系统中)并且不希望电子邮件的所有受众看到列表中的其他人或者您想要要使用有关收件人的信息个性化每封电子邮件,光标是要走的路。

如果整个基于集合的插入/更新/删除需要太长时间并锁定表,则游标或循环也可用于处理批量记录。这是游标和基于集合的解决方案之间的一种混合,通常是生产系统进行大更改的最佳解决方案。

【讨论】:

  • +1 给出了游标存在的充分理由。太多人认为基于集合的逻辑以某种方式 (a) 涵盖了所有可能的场景 (b) 总是比基于游标的解决方案性能更高,并且 (c) 总是更易于维护/可读。虽然我认为大部分工作最好通过基于集合的方法完成,但没有什么是 100%。
  • FWIW,如果您使用的是 SQL Server 2005/2008,我认为您可能可以使用公用表表达式来实现运行总计。
  • SQL Server 有隐式游标语法吗? Explicit 是通常的声明,带有 OPEN、FETCH、CLOSE 方法...
  • 我是唯一一个完全熟悉连接但不知道如何使用光标的人吗?
  • @recursive,从一开始就避免了光标,我在同一条船上。现在我知道如何使用它们了,我想知道它们的用途。
【解决方案2】:

我曾经问过 SQL Server 团队的一个人,如果您可以添加一项功能,让每个人都可以更好地使用产品,那会是什么?

他的回答是“添加?嗯,我会带走一个。如果您摆脱游标,您将迫使全世界的程序员开始以基于 SET 的方式思考事物,这将是您所见过的全球数据库性能最大的提升。'

然而,就我而言,我倾向于看到一种模式,似乎有很多程序编码人员使用游标,因为他们需要能够一次执行一个元素的操作,而错过了老式的 WHILE 循环概念。没有光标开销的相同基本思想。仍然不像基于 SET 的东西那样快速/有效,但 90% 的时间当有人声称“我不能基于这个集合,我必须使用游标”时,我可以让他们通过 while 循环来做到这一点。

【讨论】:

  • 它们被严重滥用,这可能是那个人所指的,但问题是在哪里使用它们。在某些情况下,它们确实占有一席之地。
  • 通过“没有游标开销的相同基本思想”,您是在暗示 while 循环没有与游标相同的开销吗?
  • 使用 WHILE 循环一次执行一个元素的操作而不是使用游标一次执行一个元素的操作不会将操作更改为基于集合的操作。
  • 我也想和那个人谈谈。我考虑基于集合的方法;花了几个小时试图写它们;搜索网络告诉我:“没有基于设置的方式,使用光标”;然后我访问这样的网站以了解何时使用游标,并且只阅读以下内容:“除非你不能,否则使用基于集合”。因此,也许他们应该开始设计一种在没有(100%)循环需要的情况下更易读/写的语言,而不是责怪程序编码人员使用即使他们无法基于集合实现解决 10% 的功能。实现更快的解释循环也可以解决问题。 :(
【解决方案3】:

这是一个相当固执己见的人的文章,他给出了不使用游标的理由以及一些关于它们是如何形成的答案:There Must be 15 Ways to Lose Your Cursors

【讨论】:

    【解决方案4】:

    我正在研究的 SQL Server 2008 的 MCTS 准备手册建议在 T-SQL 中需要 CURSOR 的任何地方使用外部 CLR 代码,尤其是现在 SQL Server 2008 支持自定义聚合函数。

    5 年前,我与他们合作开发了广泛的报告功能,但我认为我现在无法为他们提出一个好的用例。 CLR 聚合和函数的执行方式与内置聚合函数类似。

    【讨论】:

    • 这根本不是我的经验。调用 CLR 方法似乎有其自身的开销,并且如果您在循环中调用这样的方法,该方法以足够大的量运行,事情往往会变慢。你最近过得怎么样?
    • 到目前为止相当不错,尽管我承认我一直在使用简单的功能。您是在专门讨论聚合吗?
    • 如果IsInvariantToOrder 被实现而不是保留供将来使用,CLR 聚合将更有用。一些性能数据支持 CLR 与 Cursors are here
    【解决方案5】:

    我会使用它们的唯一时间是当光标内所做的任何事情都必须一次完成一项并且光标内所做的任何事情都需要很长时间以致光标的开销逐渐变得微不足道时.

    例如数据库备份、完整性检查、索引重建。简而言之,管理任务。

    【讨论】:

      【解决方案6】:

      天哪,我怎么忘记了 Group By?我采用了您在下面看到的基于光标的查询,并将其替换为后面的查询。现在我得到了一个结果集,所以在 php 中使用 sqlsrv_next_result() 没有问题。

      DECLARE @thisday datetime;
      
      DECLARE daycursor CURSOR FOR
      SELECT DISTINCT DATEADD(day, 0, DATEDIFF(day, 0, TimeCollected)) as thisday
      FROM computerusedata
      
      OPEN daycursor;
      FETCH NEXT FROM daycursor
      INTO @thisday;
      WHILE @@FETCH_STATUS = 0
          BEGIN
          select distinct left(ComputerName,5) as CompGroup,DATEADD(day, 0, DATEDIFF(day, 0, TimeCollected)) as day
          FROM computerusedata
          where DATEADD(day, 0, DATEDIFF(day, 0, TimeCollected)) = @thisday
          order by CompGroup;
          FETCH NEXT FROM daycursor;
          END;
      CLOSE daycursor;
      DEALLOCATE daycursor;";
      
      
      select DATEADD(day, 0, DATEDIFF(day, 0, TimeCollected)) as day,left(ComputerName,5) as CompGroup
      from ComputerUseData
      group by DATEADD(day, 0, DATEDIFF(day, 0, TimeCollected)),left(ComputerName,5)
      order by day,CompGroup
      

      【讨论】:

        【解决方案7】:

        我通常不使用游标,但当我这样做时,它必须是我在本地运行的“一次性”查询或日常工作。您希望避免让生产代码调用会频繁调用的游标,例如响应 Web 请求。

        【讨论】:

          【解决方案8】:

          在以下情况下,游标很有用:1)您需要做一些无法通过集合操作完成的事情,或者 2)通过从应用程序层进行迭代调用来做同样的工作没有意义。或者有时您有一个必须保留在数据库层上的过程,而您根本无法返回到应用程序层中途迭代某些结果集。

          我会提出一个建议,即人们使用游标变量而不是普通游标,因为您可以避免围绕普通游标的游标分配/解除分配问题。使用普通游标,如果您不取消分配它们,它们会持续存在,这可能是内存泄漏的来源。基于变量的游标不是这样(即 DECLARE @cursor CURSOR)。

          底线是,尽可能避免使用它们,如果不能,尽量少用且明智地使用它们。

          【讨论】:

          • 您在说 (nr1):如果您失败,请尝试比飞机更快地到达目的地。这可能会占用我的整个生命......
          • @Jan 这与我所说的(9 年前)相差甚远。事实上,我什至不知道你的意思。请再读一遍。
          • 如果你不明白我的意思,你怎么能知道它和你说的不一样呢?完全对不起这个评论,我只是对 SQL 很生气并继续阅读:“除非你不能,否则使用基于集合的方法”。那是某种递归首字母缩写词,例如 GNU。我需要测试我的一生才能做到这一点,然后当我死的时候可以安全地说“对我来说这是不可能的,我可以在这里使用光标”。
          • @Jan 很抱歉在您发帖澄清后没有回复此评论。你最初的问题很公平,我的评论是仓促发表的。无论如何。 (1) SQL 中的某些操作不能在集合操作中完成。 (2) 有些可以作为集合操作完成,但由于数据集的大小,不应该那样做,而是在分块集合操作中完成。 (3) 有些操作无论如何都不能以集合的方式完成,即使是那些,如果你不想涉及到应用层,也只有一些条件需要游标。快速了解差异是我们的工作。
          【解决方案9】:

          您使用游标单独重建或重新组织表索引
          除非有办法将ALTER INDEX... 作为基于集合的操作来运行。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2010-12-19
            • 1970-01-01
            • 2012-04-05
            • 1970-01-01
            • 2011-12-18
            • 1970-01-01
            • 2010-10-17
            • 2021-06-08
            相关资源
            最近更新 更多