为什么人们如此讨厌SQL游标?

为什么人们如此讨厌SQL游标?,sql,cursor,Sql,Cursor,我可以理解,由于开销和不便,想要避免使用光标,但似乎有一种严重的光标恐惧症,人们正在竭尽全力避免使用光标 例如,有一个问题询问如何使用游标做一些明显琐碎的事情,并且建议使用带有递归自定义函数的公共表表达式(CTE)递归查询来接受答案,尽管这将可处理的行数限制为32(由于sql server中的递归函数调用限制)。这对我来说是一个可怕的系统寿命解决方案,更不用说为了避免使用简单的游标而付出的巨大努力了 这种疯狂仇恨的原因是什么?是否有一些“著名权威”发布了针对游标的fatwa?是不是有什么说不出的

我可以理解,由于开销和不便,想要避免使用光标,但似乎有一种严重的光标恐惧症,人们正在竭尽全力避免使用光标

例如,有一个问题询问如何使用游标做一些明显琐碎的事情,并且建议使用带有递归自定义函数的公共表表达式(CTE)递归查询来接受答案,尽管这将可处理的行数限制为32(由于sql server中的递归函数调用限制)。这对我来说是一个可怕的系统寿命解决方案,更不用说为了避免使用简单的游标而付出的巨大努力了

这种疯狂仇恨的原因是什么?是否有一些“著名权威”发布了针对游标的fatwa?是不是有什么说不出的邪恶潜伏在诅咒者的心中,腐蚀了孩子们的道德或其他什么

维基问题,对答案比代表更感兴趣

相关信息:


编辑:让我更精确一点:我理解不应该使用游标来代替正常的关系操作;这是不需要动脑筋的。我不明白的是,人们会不厌其烦地避免使用光标,就像他们有cooties或其他东西一样,即使光标是一种更简单和/或更有效的解决方案。让我困惑的是非理性的仇恨,而不是明显的技术效率。

一般来说,因为在关系数据库上,使用游标的代码的性能比基于集合的操作要差一个数量级

游标使人们过度地将程序思维应用于基于集合的环境

而且他们的速度很慢

发件人:

请注意,游标是 访问SQL内部数据的最慢方式 服务器。应仅在以下情况下使用 您确实需要一次访问一行 时间我能想到的唯一原因 这就是调用存储过程 每一排。在我发现的 游标超过三十次 比基于集合的备选方案慢


在基于集合的操作更好的地方,SQL新手往往会使用游标。特别是当人们在学习传统编程语言之后学习SQL时,“迭代这些记录”的心态往往会导致人们不恰当地使用游标

最严肃的SQL书籍包括一章禁止使用游标;写得好的说明清楚地表明,游标有其位置,但不应用于基于集合的操作


显然,在某些情况下,游标是正确的选择,或者至少是正确的选择

当使用游标方法时,优化器通常无法使用关系代数来转换问题。通常,游标是解决问题的好方法,但SQL是一种声明性语言,数据库中有很多信息,从约束到统计信息和索引,这意味着优化器有很多解决问题的选项,而光标几乎明确地指示了解决方案。

您可以发布光标示例或问题的链接吗?可能还有比递归CTE更好的方法


除其他注释外,如果游标使用不当(通常情况下),会导致不必要的页/行锁定。

上面有一个答案说“游标是访问SQL Server内部数据的最慢方式……游标的速度比基于集合的备选方案慢30多倍。”

这种说法在许多情况下可能是正确的,但作为一种笼统的说法,它是有问题的。例如,在需要执行更新或删除操作的情况下,我很好地使用了游标,这些操作会影响到接收恒定生产读取的大型表的许多行。运行一次更新一行的存储过程最终会比基于集合的操作快,因为基于集合的操作与读取操作冲突,并最终导致可怕的锁定问题(在极端情况下,可能会完全杀死生产系统)

在没有其他数据库活动的情况下,基于集合的操作通常更快。在生产系统中,这取决于。

游标的“开销”只是API的一部分。游标是RDBMS的一部分如何在引擎盖下工作的。通常
CREATE TABLE
INSERT
都有
SELECT
语句,而实现就是明显的内部游标实现

使用更高级的“基于集合的运算符”将光标结果捆绑到单个结果集中,这意味着来回的API更少

游标早于提供一流集合的现代语言。旧的C、COBOL、Fortran等必须一次处理一行,因为没有可以广泛使用的“集合”概念。Java、C#、Python等都有一流的列表结构来包含结果集

缓慢的问题


在某些圈子里,关系连接是个谜,人们会编写嵌套的游标,而不是简单的连接。我见过真正史诗般的嵌套循环操作,它们被写成了很多很多的游标。击败RDBMS优化。而且跑得很慢

简单的SQL重写将嵌套的游标循环替换为联接,一个单一的、扁平的游标循环可以使程序在100%的时间内运行。[他们认为我是优化之神。我所做的只是用连接替换嵌套循环。仍然使用游标。]

这种混乱常常导致对光标的起诉。然而,问题不在于光标,而是光标的滥用

尺寸问题

对于真正的epic结果集(即,将表转储到文件),游标是必不可少的。基于集合的操作无法在内存中将真正大的结果集具体化为单个集合

SELECT * INTO #temp FROM master..spt_values DECLARE @startTime DATETIME BEGIN TRAN SELECT @startTime = GETDATE() UPDATE #temp SET number = 0 select DATEDIFF(ms, @startTime, GETDATE()) ROLLBACK BEGIN TRAN DECLARE @name VARCHAR DECLARE tempCursor CURSOR FOR SELECT name FROM #temp OPEN tempCursor FETCH NEXT FROM tempCursor INTO @name SELECT @startTime = GETDATE() WHILE @@FETCH_STATUS = 0 BEGIN UPDATE #temp SET number = 0 WHERE NAME = @name FETCH NEXT FROM tempCursor INTO @name END select DATEDIFF(ms, @startTime, GETDATE()) CLOSE tempCursor DEALLOCATE tempCursor ROLLBACK DROP TABLE #temp
  for x in (select ....) loop
    --do something 
  end loop;