我在SQL 7天后就知道了一些性能原因,但SQL Server 2005中是否仍然存在相同的问题?如果我在存储过程中有一个我想单独操作的结果集,那么游标仍然是一个糟糕的选择吗?如果是这样,为什么?
因为游标占用内存并创建锁.
您真正在做的是尝试将基于集合的技术强制转换为基于非集合的功能.并且,公平地说,我应该指出游标确实有用,但是它们不受欢迎,因为许多不习惯使用基于集合的解决方案的人使用游标而不是找出基于集合的解决方案.
但是,当您打开游标时,您基本上将这些行加载到内存中并锁定它们,从而创建潜在的块.然后,当您循环光标时,您正在更改其他表并仍然保持光标的所有内存和锁定打开.
所有这些都有可能导致其他用户的性能问题.
因此,作为一般规则,游标不受欢迎.特别是如果这是解决问题的第一个解决方案.
以上关于SQL是基于集合的环境的评论都是正确的.但是,有时候逐行操作很有用.考虑元数据和dynamic-sql的组合.
作为一个非常简单的例子,假设我在表中有100多条记录,用于定义要复制/截断/删除的表的名称.哪个最好?硬编码SQL来做我需要的东西?或者遍历此结果集并使用dynamic-SQL(sp_executesql)来执行操作?
使用基于集合的SQL无法实现上述目标.
那么,使用游标还是while循环(伪游标)?
只要您使用正确的选项,SQL游标就可以了:
INSENSITIVE将为您的结果集创建一个临时副本(使您不必自己为伪游标执行此操作).
READ_ONLY将确保底层结果集上没有锁定.基础结果集的更改将反映在后续提取中(与从伪游标获取TOP 1相同).
FAST_FORWARD将创建一个优化的只进,只读游标.
在将所有游标统治为邪恶之前,请阅读可用选项.
我每次需要游标时都会使用游标.
我创建了一个带有标识列的表变量.
插入我需要处理的所有数据.
然后使用计数器变量创建一个while块,并使用select语句从table变量中选择我想要的数据,其中identity列与计数器匹配.
这种方式我不会锁定任何东西,并使用更少的内存和它的安全,我不会丢失任何内存损坏或类似的东西.
并且块代码易于查看和处理.
这是一个简单的例子:
DECLARE @TAB TABLE(ID INT IDENTITY, COLUMN1 VARCHAR(10), COLUMN2 VARCHAR(10)) DECLARE @COUNT INT, @MAX INT, @CONCAT VARCHAR(MAX), @COLUMN1 VARCHAR(10), @COLUMN2 VARCHAR(10) SET @COUNT = 1 INSERT INTO @TAB VALUES('TE1S', 'TE21') INSERT INTO @TAB VALUES('TE1S', 'TE22') INSERT INTO @TAB VALUES('TE1S', 'TE23') INSERT INTO @TAB VALUES('TE1S', 'TE24') INSERT INTO @TAB VALUES('TE1S', 'TE25') SELECT @MAX = @@IDENTITY WHILE @COUNT <= @MAX BEGIN SELECT @COLUMN1 = COLUMN1, @COLUMN2 = COLUMN2 FROM @TAB WHERE ID = @COUNT IF @CONCAT IS NULL BEGIN SET @CONCAT = '' END ELSE BEGIN SET @CONCAT = @CONCAT + ',' END SET @CONCAT = @CONCAT + @COLUMN1 + @COLUMN2 SET @COUNT = @COUNT + 1 END SELECT @CONCAT
我认为游标的名字很糟糕,因为SQL新手发现它们并且认为"嘿,因为循环!我知道如何使用它们!" 然后他们继续将它们用于一切.
如果你将它们用于它们的设计目标,我就不会错.
SQL是一种基于集合的语言 - 这是它最擅长的.
我认为游标仍然是一个糟糕的选择,除非你对它们有足够的了解,以证明它们在有限的情况下使用它们.
我不喜欢游标的另一个原因是清晰度.光标块太丑了,很难以清晰有效的方式使用.
所有这一切已经表示,目前有某些情况下,光标真的是最好的-他们只是通常不是初学者想使用它们的情况.