使用游标的优点和缺点(在SQL服务器中)

时间:2012-02-16 22:14:40

标签: sql-server cursor

我在这里问了一个问题Using cursor in OLTP databases (SQL server)

人们回应说不应该使用游标。

我觉得游标是非常强大的工具,意在使用(我不认为Microsoft支持错误的开发人员的游标)。假设你有一个表,其中一行中的列的值取决于值的值上一行中的同一列。如果是一次性后端进程,您认为使用游标是否是可接受的选择?

在我的脑海中,我可以想到几个场景,我觉得使用游标不应该感到羞耻。如果你们有其他感觉,请告诉我。

1>一次性后端处理,用于清除在几分钟内完成执行的错误数据。 2>在很长一段时间内运行一次的批处理(类似于一年一次)。 如果在上述场景中,其他进程没有明显的压力,那么花费额外的时间编写代码来避免使用游标是不合理的?换句话说,在某些情况下,开发人员的时间比对其他任何事情几乎没有影响的流程的执行更重要。

在我看来,这些是您应该认真尝试避免使用游标的一些场景。 1>从网站调用的存储过程可以经常被调用。 2>每天运行多次并占用大量资源的SQL作业。

我认为,如果不分析手头的任务并实际权衡其他选择,那么做出像“游标不应该被使用”这样的一般性陈述是非常肤浅的。

请告诉我你的想法。

5 个答案:

答案 0 :(得分:11)

有几种情况下游标实际上比基于集合的等价物更好。总是会想到运行总计 - 查找Itzik的话(并忽略任何涉及SQL Server 2012的内容,它会增加新的窗口函数,在这种情况下为游标提供运行)。

人们对游标的一个重大问题是它们执行缓慢,它们使用临时存储等。这部分是因为默认语法是具有各种低效默认选项的全局游标。下次你用光标做一些事情,不需要像UPDATE...WHERE CURRENT OF这样的事情(我已经能够避免我的整个职业生涯),通过比较这两个语法选项,给它一个公平的动摇:

DECLARE c CURSOR 
    FOR <SELECT QUERY>;

DECLARE c CURSOR 
    LOCAL STATIC READ_ONLY FORWARD_ONLY
    FOR <SELECT QUERY>;

实际上,第一个版本表示未记录的存储过程sp_MSforeachdb中的错误,如果任何数据库的状态在执行期间发生更改,则会使其跳过数据库。我随后编写了自己的存储过程版本(请参阅herehere),它们都修复了错误(只需使用上面语法的后一版本)并添加了几个参数来控制哪些数据库会被选中。

很多人认为方法不是游标,因为它没有说DECLARE CURSOR。我见过有人认为while循环比游标(which I hope I've dispelled here)快,或者使用FOR XML PATH执行组连接并不执行隐藏游标操作。在很多情况下看这个计划都会显示出真相。

在很多情况下,使用基于集合的游标更合适。但是有很多有效的用例,其中基于集合的等价物编写起来要复杂得多,优化器可以为两者或两者生成计划(例如,在循环表中更新统计信息的维护任务,为结果中的每个值调用存储过程,等等。对于许多大型多表查询也是如此,其中计划对于优化器来说太过怪异。在这些情况下,最好先将一些中间结果转储到临时结构中。对于游标的某些基于集合的等价物(如运行总计)也是如此。我还写过另一种方式,人们几乎总是本能地想到使用while循环/光标,并且有clever set-based alternatives that are much better

更新2013-07-25

只是想添加一些关于游标的其他博客文章,如果你必须使用哪些选项,并使用基于集合的查询而不是循环来生成集:

Best Approaches for Running Totals - Updated for SQL Server 2012

What impact can different cursor options have?

生成没有循环的集合或序列:[Part 1] [Part 2] [Part 3]

答案 1 :(得分:6)

SQL Server中游标的问题在于引擎是基于集内部的,不像其他DBMS,如Oracle,它们是基于游标的内部。这意味着在SQL Server中创建游标时,需要创建临时存储,并且需要将基于集合的结果集复制到临时游标存储。您可以看到为什么这样做会非常昂贵,更不用说您可能在光标本身上执行的任何逐行处理。底线是基于集合的处理更有效率,并且通常使用CTE或临时表可以更好地完成基于光标的操作。

话虽如此,有些情况下光标可能是可接受的,正如您所说的一次性操作。我能想到的最常见的用途是在维护计划中,您可能会迭代执行各种维护任务的服务器上的所有数据库。只要你限制你的使用并且没有围绕RBAR(逐行激动行)处理设计整个应用程序,你应该没问题。

答案 2 :(得分:3)

一般来说,游标是一件坏事。但是在某些情况下使用光标更实际,而在某些情况下使用光标更快。一个很好的例子是光标通过联系表发送基于某些标准的电子邮件。 (如果从DBMS发送电子邮件是一个好主意,不要打开问题 - 让我们假设它是针对手头的问题。)没有办法编写基于集合的。你可以使用一些技巧来提出一个基于集合的解决方案来生成动态SQL,但是不存在真正的基于集合的解决方案。

但是,可以使用自联接完成涉及前一行的计算。这通常仍然比光标快。

在所有情况下,您需要平衡开发更快解决方案所需的工作量。如果没有人关心,如果您在1分钟或1小时内处理运行,请使用最快速完成工作的内容。如果您循环遍历随着时间增长的数据集(如[orders]表),请尽可能远离游标。如果您不确定,请执行性能测试,将游标基础与基于集合的解决方案在几种截然不同的数据大小上进行比较。

答案 3 :(得分:0)

对于像动态SQL旋转这样的东西,它们是必需的,但是你应该尽可能避免使用它们。

答案 4 :(得分:0)

由于性能缓慢,我一直不喜欢游标。但是,我发现我并没有完全理解不同类型的游标,在某些情况下,游标是一个可行的解决方案。

如果您遇到的业务问题只能通过一次处理一行来解决,那么光标就是合适的。

因此,要使用光标提高性能,请更改正在使用的光标类型。我不知道的是,如果你没有指定你声明的光标类型,默认情况下会得到动态乐观类型,这是性能最慢的类型,因为它在引擎盖下做了很多工作。但是,通过将游标声明为不同的类型,例如静态游标,它具有非常好的性能。

有关更全面的解释,请参阅这些文章:

The Truth About Cursors: Part I

The Truth About Cursors: Part II

The Truth About Cursors: Part III

我认为对游标最大的反对意见是性能,但是,在基于集合的方法中没有布置任务可能排在第二位。第三是任务的可读性和布局,因为它们通常没有很多有用的评论。

SQL Server经过优化,可以运行基于集合的方法。您编写查询以返回结果数据集,例如表上的连接,但SQL Server执行引擎确定要使用的连接:合并连接,嵌套循环连接或散列连接。 SQL Server根据参与列中的参与列,数据量,索引结构和值集确定最佳连接算法。因此,使用基于集合的方法通常是性能优于程序游标方法的最佳方法。