很多时候,当我编写存储过程等时,我首先使用 CURSOR,后来发现我的过程存在一些性能问题。
我读到的每一篇文章都说 CURSORS 很糟糕,会导致不必要的锁定等,性能测试也证明了这一点。
我的问题是您何时使用 CURSOR 以及在什么情况下它们有用或有用?
如果没有用,他们为什么要为 SQL 制作如此糟糕的控制结构/类型?
很多时候,当我编写存储过程等时,我首先使用 CURSOR,后来发现我的过程存在一些性能问题。
我读到的每一篇文章都说 CURSORS 很糟糕,会导致不必要的锁定等,性能测试也证明了这一点。
我的问题是您何时使用 CURSOR 以及在什么情况下它们有用或有用?
如果没有用,他们为什么要为 SQL 制作如此糟糕的控制结构/类型?
通常要避免使用它们,但是该功能的存在是有原因的,并且有时会使用它们。我会说我见过的 90% 以上的游标是不需要的。如果您将它们用于 CRUD 操作,则几乎总是可以以基于集合的方式重做。我经常看到人们为此使用游标,因为他们不知道如何在更新或删除中使用连接,或者他们可以在插入中使用选择语句而不是值子句。当人们认为他们需要它们进行稍微更复杂的处理时,这是另一种不必要的用途,实际上可以通过 case 语句轻松处理。
光标有时会更快地计算诸如运行总计之类的东西。
游标对于设置为一次只处理一个输入值的存储过程的多次执行也很方便。我不使用此功能来运行用户存储的过程(除非我知道我会遇到非常小的数据集),但是当需要针对多个表运行系统过程时,它对于数据库管理员来说非常方便。
如果您在 SQl 中创建电子邮件(不是最好的地方,但在他们这样做的某些系统中)并且不希望电子邮件的整个受众看到列表中的其他人,或者您想要个性化每个人带有收件人信息的电子邮件,光标是要走的路。
如果整个基于集合的插入/更新/删除将花费太长时间并锁定表,则游标或循环也可用于处理批量记录。这是游标和基于集合的解决方案之间的一种混合,通常是对生产系统进行较大更改的最佳解决方案。
有一次我问 SQL Server 团队的一个人,如果你可以添加一个功能,让每个人都更好的产品,那会是什么?
他的回答是“添加?嗯,我会带走一个。如果您摆脱光标,您将迫使全世界的程序员开始以基于 SET 的方式思考事物,这将是您所见过的全球数据库性能最大的提升。
然而,就我而言,我倾向于看到一种模式,似乎有很多程序编码人员使用游标,因为他们需要能够一次执行一个元素的操作,而错过了老式的 WHILE 循环概念。没有光标开销的相同基本思想。仍然不像基于 SET 的东西那样快速/有效,但 90% 的时间当有人声称“我不能基于这个集合,我必须使用游标”时,我可以让他们用 while 循环来做。
这是一个相当固执己见的人的文章,他给出了不使用光标的原因以及它们是如何形成的一些答案:必须有 15 种方法来丢失光标。
我正在研究的 SQL Server 2008 的 MCTS 准备手册建议在 T-SQL 中需要 CURSOR 的任何地方使用外部 CLR 代码,尤其是现在 SQL Server 2008 支持自定义聚合函数。
5 年前,我与他们合作开发广泛的报告功能,但我认为我现在无法为他们提出一个好的用例。CLR 聚合和函数的执行方式与内置聚合函数类似。
只有当在光标内完成的任何事情都必须一次完成一项并且在光标内完成的任何事情都需要很长时间以致光标的开销变得微不足道时,我才会使用它们。
例如数据库备份、完整性检查、索引重建。简而言之,管理任务。
OMG,我怎么忘记了 Group By?我采用了您在下面看到的基于光标的查询,并将其替换为后面的查询。现在我得到了一个结果集,所以在 php.ini 中使用 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
我通常不使用游标,但当我这样做时,它必须是我在本地运行的“一次性”查询或日常工作。您希望避免让生产代码调用会频繁调用的游标,例如响应 Web 请求。
游标在以下情况下很有用: 1) 您需要做一些无法通过集合操作完成的事情,或者 2) 通过从应用程序层进行迭代调用来做同样的工作没有意义。或者有时您有一个必须保留在数据库层上的过程,而您根本无法返回到应用程序层中途迭代某些结果集。
不过,我要提出的一项建议是人们使用游标变量而不是普通游标,因为您可以避免围绕普通游标的游标分配/解除分配问题。使用普通游标,如果您不取消分配它们,它们会持续存在,这可能是内存泄漏的来源。基于变量的游标(即 DECLARE @cursor CURSOR)并非如此。
底线是,尽可能避免使用它们,如果不能,尽量少用且明智地使用它们。
您可以使用游标单独重建或重新组织表索引
,除非有一种方法可以将ALTER INDEX...作为基于集合的操作运行。