【问题标题】:When are TSQL Cursors the best or only option?什么时候 SQL 游标是最好的或唯一的选择?
【发布时间】:2011-10-16 04:34:54
【问题描述】:

我最近有关于在 TSQL 中使用游标的争论......

首先,我不是辩论中的啦啦队长。但是每次有人说 cursor 时,总会有一些傻瓜(或 50 个)用强制性的'光标是邪恶的'口头禅猛扑过去。我知道 SQL-Server 针对基于集合的操作进行了优化,也许游标确实是邪恶的化身,但如果我想在这背后加上一些客观的想法......

这就是我的想法:

  1. 游标和集合操作之间的唯一区别是性能之一吗?

    编辑:有一个很好的例子,它不仅仅是性能问题 - 例如为一个 id 列表反复运行单个批处理,或者执行逐行存储在表字段中的实际 SQL 文本。

  2. 跟进:游标总是表现更差吗?

    • 编辑:@Martin 展示了一个很好的案例,即 Cursors 相当显着地胜过基于集合的操作。我怀疑这不会是你经常做的事情(在你求助于某种 OLAP / 数据仓库类型的解决方案之前),但尽管如此,这似乎是一个案例没有光标你真的活不下去。
    • reference to TPC benchmarks 建议光标可能比人们普遍认为的更具竞争力。
    • reference to memory-usage optimizations 用于游标自 Sql-Server 2005 起
  3. 你能想到游标比基于集合的操作更适合解决的问题吗?

    • 编辑:基于集合的操作实际上不能 Execute 存储过程等(参见上面第 1 项的编辑)。
    • 编辑:在聚合大型数据集时,基于集合的操作比逐行操作要慢得多。

  • Article from MSDN 解释他们的观点 最常见的问题,人们求助于游标(和一些 解释更有效的基于集合的技术。)
  • 微软在2008 Transact SQL Reference on MSDN 中(含糊地)说:“......有时最好一次处理一行结果”,但没有给出任何关于他们所指案例的示例到。

大多数情况下,如果/当我对各种应用程序进行任何重大升级时,我会在我的旧代码中将游标转换为基于集合的操作,只要能从中获得一些东西。 (我在很多时候倾向于懒惰而不是纯粹——也就是说,如果它没有坏,就不要修理它。)

【问题讨论】:

  • "如果没有损坏,请不要修复它。" - 我认为您的目标是“如果它没有坏,就不要修复它”:-)
  • @Damien -- 好吧,我已经把它弄坏了。 :-)
  • 到目前为止,我有 4 个非常有见地的答案,考虑到它们都是有效的,我只想将得票最多的一个标记为答案。我在我最初的问题中总结了答案,我希望人们在看到一些被遗漏的东西时仍然会为此做出贡献。例如——我仍然想测试的一件事是在一个字段中聚合字符串(即创建一个分隔列表),以及游标在大型数据集上是否优于基于集合的解决方案,例如链接中的合并解决方案上面的问题 3(文章来自 MSDN)。无论如何,谢谢。

标签: tsql stored-procedures database-cursor set-based


【解决方案1】:

直接回答您的问题:

我还没有遇到过设置操作无法完成游标可能完成的操作的情况。但是,在某些情况下,使用游标将大型集合问题分解为更易于管理的块证明了对于代码可维护性、日志记录、事务控制等目的的更好解决方案。但我怀疑是否有任何硬性规则可以告诉您哪些类型的需求会导致一种解决方案或另一种解决方案——单个数据库和需求实在是太多样化了。

也就是说,我完全同意您的“如果它没有损坏,就不要修复它”的方法。通过重构过程代码来为运行良好的过程设置操作几乎没有什么收获。但是,首先寻求基于集合的解决方案是一个很好的经验法则,只有在必要时才使用过程代码。直觉?如果您使用游标的次数超过 20%,则说明您做错了。

我真正想说的是:

当我采访程序员时,我总是向他们提出几个中等复杂的 SQL 问题,并请他们解释他们将如何解决这些问题。这些是我知道可以通过集合操作解决的问题,我专门寻找能够在没有程序方法(即光标)的情况下解决这些问题的候选人。

不是,因为我相信在这两种方法中都存在本质上好的或更高性能的东西——不同的情况会产生不同的结果。而是因为,根据我的经验,程序员要么了解基于集合的操作的概念,要么不了解。如果不这样做,他们将花费太多时间来为问题开发复杂的程序解决方案,而这些问题可以通过基于集合的操作更快、更简单地解决。

相反,获得基于集合的操作的程序员几乎从来没有遇到过实现程序解决方案的问题,而事实上,这是绝对必要的。

【讨论】:

  • @Michael-- 有没有机会获得这些问题的链接?为了个人后代?
  • 我口头给出,但很乐意为您写下来。给我几天时间,我会把它们打出来然后贴在某个地方。 :)
  • 回复:您真正想说的 -- 实际上,这是一个非常好的观察结果 -- 在与我所在机构的其他项目分析师合作时,我在某种程度上注意到了这一点。
  • +1 -- 谢谢 -- 另外,我同意经验法则:在使用游标之前寻找基于集合的解决方案。
  • 给你打勾...如果你想看看它们,t-clausen 和 damien 实际上提出了两个具体案例,你确实没有基于集合的解决方案,而 martin想出了一个性能是一个非常严重的问题。但无论如何,我认为您的回答在更广泛的层面上确实很有见地,并且肯定提供了很多好的智慧/建议。谢谢。
【解决方案2】:

Running Totals 是一个经典案例,当行数变大时,游标可以执行基于集合的操作,因为尽管游标的固定成本较高,但所需的工作量呈线性增长,而不是像基于集合的“三角连接”那样呈指数增长接近。

Itzik Ben Gan does some comparisons here

Denali 对 OVER 子句有更完整的支持,但这应该会使这种使用变得多余。

【讨论】:

【解决方案3】:

自从我看到人们设法使用其他 TSQL 构造(通常涉及至少一个 while 循环)重新实现游标(以各种形式),没有什么是游标可以实现的不能 使用其他结构来完成。

这并不是说重新实现的效率不如通过在该解决方案中不包含“光标”一词而避免的光标一样低效。有些人似乎完全讨厌这个词,而不是机制。

我成功证明保留游标的一个地方是用于两个不同数据库之间的数据传输/转换(我们在这里与客户打交道)。虽然我们本可以以基于集合的方式实现此传输(实际上,我们以前有过),但存在可能导致一些客户出现问题的有问题的数据。在基于集合的解决方案中,我们必须:

  • 继续传输,排除每个表中的失败客户端数据,让这些客户端部分传输,或者,
  • 中止整个批次

然而,通过将传输单元设置为单个客户端(使用光标选择每个客户端),我们可以使每个客户端在系统之间的传输要么完全工作,要么完全回滚(即,将每个传输放在自己的事务中)

我想不出任何我想在此类转移的“顶级”下方使用光标的情况(例如,选择下一个转移的客户)

【讨论】:

  • +1 为示例,但是 OP 将游标与基于集合的操作进行比较,因此谈论以循环形式重新实现游标并不真正相关。
  • @Davy8 - 问题 3 的原始形式询问您是否可以用游标做一些其他方式无法完成的事情,因此是前两段。
  • 除非它刚刚出现在编辑历史记录中(这是可能的,我注意到非常快速的编辑有时不会出现)看起来问题 3 总是关于游标与基于集合的, 只是从问什么是可能的变成了问游标有没有更好的解决方法。
  • +1 -- 谢谢。这是一个很好的案例(类似于@t-clausen.dk)——这让我现在修改了这个问题:-) 以区分“数据集”和“执行语句集”。
【解决方案4】:

通常在构建动态 sql 时,必须使用游标。想象一个脚本,它在数据库中的所有表中搜索不同字段中的相同值。最好的解决方案是光标。问题出在哪里How to use EXEC or sp_executeSQL without looping in this case? 如果有人能在没有光标的情况下更好地解决这个问题,我会印象深刻。

【讨论】:

  • 同意。动态 SQL 完全不同。
  • +1 -- 谢谢。这是一个很好的案例(类似于@Damien_The_Unbeliever 所说的)——这让我现在修改了这个问题:-) 以区分“数据集”和“执行语句集”
猜你喜欢
  • 1970-01-01
  • 2021-11-06
  • 1970-01-01
  • 1970-01-01
  • 2011-03-27
  • 1970-01-01
  • 2010-11-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多