为什么在SQL Server中使用游标被认为是不好的做法?
我早在SQL 7天内就已经知道一些性能原因,但是在SQL Server 2005中是否仍然存在相同的问题?如果在存储过程中有一个要单独处理的结果集,则游标仍然是一个错误的选择吗?如果是这样,为什么?
解决方案
回答
我认为游标的名字很不好,因为SQL新手发现了游标,并认为"嘿,for循环!我知道如何使用它们!"然后他们继续使用它们进行所有操作。
如果我们将它们用于设计目的,那我就找不到错。
回答
SQL是一种基于集合的语言,这才是最出色的。
我认为,除非我们对游标有足够的了解以证明在有限的情况下使用游标是合理的,否则游标仍然是一个错误的选择。
我不喜欢游标的另一个原因是清晰度。游标块非常丑陋,很难以清晰有效的方式使用它。
综上所述,在某些情况下游标确实是最好的-刚开始的初学者通常不希望使用它们。
回答
我认为,基本问题是数据库是针对基于集合的操作而设计和调整的,即基于数据关系在一个快速步骤中选择,更新和删除大量数据。
另一方面,内存中软件是针对单个操作而设计的,因此,最好的方法是循环访问一组数据并可能对每个项目依次执行不同的操作。
循环不是数据库或者存储体系结构设计的目的,即使在SQL Server 2005中,如果将基本数据集放入自定义程序中并在内存中进行循环,也无法获得接近于性能。 ,使用尽可能轻量的数据对象/结构。
回答
因为游标会占用内存并创建锁。
我们真正要做的是尝试将基于集合的技术强制为非基于集合的功能。而且,公平地说,我应该指出游标确实有其用途,但是它们并不受欢迎,因为许多不习惯使用基于集合的解决方案的人使用游标而不是弄清楚基于集合的解决方案。
但是,当我们打开游标时,基本上是在将这些行加载到内存中并锁定它们,从而创建潜在的块。然后,当我们遍历游标时,我们正在对其他表进行更改,并且仍保持游标的所有内存和锁处于打开状态。
所有这些都有可能导致其他用户的性能问题。
因此,作为一般规则,光标会皱眉。特别是如果这是解决问题的第一个解决方案。
回答
游标确实有其位置,但是我认为这主要是因为通常在单个select语句足以提供结果的汇总和过滤时使用它们。
避免使用游标,SQL Server可以更充分地优化查询性能,这在大型系统中非常重要。
回答
有时,我们需要执行的处理的性质要求使用游标,尽管出于性能方面的考虑,如果可能的话,最好使用基于集合的逻辑来编写操作。
我不会将使用游标称为"坏习惯",但是游标确实会消耗服务器上的更多资源(比基于等价的基于集合的方法更多),并且经常会不必要。鉴于此,我的建议是在诉诸游标之前考虑其他选项。
游标有几种类型(仅向前,静态,键集,动态)。每个都有不同的性能特征和相关的开销。确保为操作使用正确的光标类型。仅转发是默认设置。
使用游标的一个参数是何时需要处理和更新单个行,尤其是对于没有良好唯一键的数据集。在这种情况下,可以在声明游标时使用FOR UPDATE子句,并使用UPDATE ... WHERE CURRENT OF处理更新。
请注意,"服务器端"游标曾经很流行(从ODBC和OLE DB开始),但是ADO.NET不支持它们,而AFAIK则永远不会。
回答
在极少数情况下使用游标是合理的。几乎在任何情况下,它都不会胜过基于集合的关系型查询。有时,程序员更容易从循环的角度进行思考,但是使用集合逻辑(例如更新表中的大量行)将导致解决方案不仅减少了很多SQL代码行,但是运行速度要快得多,通常要快几个数量级。
甚至Sql Server 2005中的快进游标也无法与基于集合的查询竞争。与基于集合的性能相比,性能下降的图形通常看起来像是n ^ 2运算,随着数据集的增长,线性下降趋于线性化。
回答
以上关于SQL是基于集合的环境的评论都是正确的。但是,有时逐行操作很有用。考虑元数据和动态SQL的组合。
作为一个非常简单的示例,假设我在一个表中有100多个记录,这些记录定义了我想复制/截断/以任何方式表示的表的名称。哪个最好?对SQL进行硬编码以执行我需要做的事情?还是遍历此结果集并使用动态SQL(sp_executesql)执行操作?
使用基于集合的SQL无法实现上述目标。
那么,要使用游标还是while循环(伪光标)?
只要使用正确的选项,SQL游标就可以了:
INSENSITIVE将为结果集制作一个临时副本(免除了我们必须自己为伪游标执行此操作的麻烦)。
READ_ONLY将确保没有对基础结果集持有任何锁。基础结果集中的更改将反映在后续的访存中(就像从伪光标中获得TOP 1一样)。
FAST_FORWARD将创建一个优化的只读只读游标。
在将所有游标视为邪恶之前,请阅读有关可用选项的信息。