Sql server 为什么在SQL Server中使用游标被认为是不好的做法?

Sql server 为什么在SQL Server中使用游标被认为是不好的做法?,sql-server,sql-server-2005,cursor,Sql Server,Sql Server 2005,Cursor,早在SQL7时代,我就知道一些性能原因,但在SQLServer2005中是否仍然存在相同的问题?如果我在一个存储过程中有一个要单独操作的结果集,那么游标仍然是一个错误的选择吗?如果是,原因是什么?我认为游标的名声不好,因为SQL新手发现了它们,并认为“嘿,for循环!我知道如何使用它们!”然后他们继续在所有事情上使用它们 如果你将它们用于它们的设计目的,我就找不到缺点。SQL是一种基于集合的语言,这是它最擅长的 我认为游标仍然是一个糟糕的选择,除非你对它们有足够的了解,在有限的情况下证明它们的使

早在SQL7时代,我就知道一些性能原因,但在SQLServer2005中是否仍然存在相同的问题?如果我在一个存储过程中有一个要单独操作的结果集,那么游标仍然是一个错误的选择吗?如果是,原因是什么?

我认为游标的名声不好,因为SQL新手发现了它们,并认为“嘿,for循环!我知道如何使用它们!”然后他们继续在所有事情上使用它们


如果你将它们用于它们的设计目的,我就找不到缺点。

SQL是一种基于集合的语言,这是它最擅长的

我认为游标仍然是一个糟糕的选择,除非你对它们有足够的了解,在有限的情况下证明它们的使用是合理的

我不喜欢光标的另一个原因是清晰度。光标块太难看了,很难清晰有效地使用


话虽如此,有些情况下光标确实是最好的——它们通常不是初学者想要使用它们的情况。

我认为,基本问题是数据库是为基于集合的操作而设计和调整的——选择、更新,根据数据中的关系,在一个快速步骤中删除大量数据

另一方面,内存中的软件是为单独的操作而设计的,因此在一组数据上循环,并可能连续地对每个项目执行不同的操作是它最擅长的


循环不是数据库或存储体系结构的设计目标,即使在SQL Server 2005中,如果将基本数据集拉入自定义程序并在内存中进行循环,也无法获得接近您所获得的性能,使用尽可能轻量级的数据对象/结构。

因为游标占用内存并创建锁

您真正做的是试图将基于集合的技术强制转换为非基于集合的功能。而且,平心而论,我应该指出,游标确实有用处,但它们不受欢迎,因为许多不习惯使用基于集合的解决方案的人使用游标,而不是计算基于集合的解决方案

但是,当您打开光标时,基本上是将这些行加载到内存中并锁定它们,从而创建潜在的块。然后,当您在游标中循环时,您正在对其他表进行更改,并且仍然保持游标的所有内存和锁处于打开状态

所有这些都有可能给其他用户带来性能问题


因此,一般来说,不允许使用光标。特别是当这是解决问题的第一个解决方案时。

游标确实有其位置,但我认为主要是因为当单个select语句足以提供结果的聚合和过滤时,经常使用游标


避免使用游标可以使SQL Server更充分地优化查询的性能,这在大型系统中非常重要。

有时,您需要执行的处理的性质需要使用游标,但出于性能原因,如果可能,最好使用基于集的逻辑编写操作

我不认为使用游标是“不好的做法”,但它们确实会在服务器上消耗更多的资源(比同等的基于集合的方法),而且通常情况下它们是不必要的。考虑到这一点,我的建议是在使用游标之前考虑其他选项。 有几种类型的游标(仅向前、静态、键集、动态)。每一个都有不同的性能特征和相关的开销。确保您的操作使用了正确的光标类型。“仅向前”是默认设置

使用游标的一个参数是当您需要处理和更新单个行时,特别是对于没有好的唯一键的数据集。在这种情况下,您可以在声明游标时使用FORUPDATE子句,并使用UPDATE。。。其中电流为


请注意,“服务器端”游标过去很流行(来自ODBC和OLE DB),但ADO.NET不支持它们,AFAIK也永远不支持它们。

很少有案例证明使用游标是合理的。在几乎没有任何情况下,它的性能会优于基于集合的关系查询。有时程序员更容易根据循环进行思考,但使用集合逻辑(例如,更新表中的大量行)将导致解决方案不仅SQL代码少很多行,而且运行速度更快,通常快几个数量级


即使是SQLServer2005中的快进游标也无法与基于集合的查询竞争。与基于集合的操作相比,性能下降的图表通常开始看起来像一个n^2操作,随着数据集变得非常大,它往往更线性。

以上关于SQL是基于集合的环境的评论都是正确的。但是,有时逐行操作是有用的。考虑元数据和动态SQL.

的组合。 作为一个非常简单的例子,假设我在一个表中有100多条记录,这些记录定义了我要复制/截断/任何内容的表的名称。哪一个最好?硬编码SQL以执行我需要的操作?或者迭代此结果集并使用动态SQL(sp_executesql)执行操作

使用基于集合的SQL无法实现上述目标

那么,使用游标还是while循环(伪游标)

只要使用正确的选项,SQL游标就可以:

“不敏感”将创建结果集的临时副本(使您不必为伪光标自己执行此操作)

只读将确保基础结果集上没有锁。基础结果集中的更改将反映在后续的获取中(就像从伪游标获取TOP 1一样)

快进将创建一个优化的只读向前光标

阅读有关可用的o
DECLARE @commandname NVARCHAR(1000) = '';

SELECT @commandname += 'truncate table ' + tablename + '; ';
FROM tableNames;

EXEC sp_executesql @commandname;
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
SELECT * FROM @temptable WHERE Id=@counter 
SELECT TOP 1 * FROM @temptable WHERE Id>@lastId
var items = new List<Item>();
foreach(int oneId in itemIds)
{
    items.Add(dataAccess.GetItemById(oneId);
}
var items = dataAccess.GetItemsByIds(itemIds);