4

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

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

这就是我的想法:

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

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

  2. 追问:游标总是表现更差吗?

    • 编辑:@Martin 展示了一个很好的案例,其中 Cursors 相当显着地胜过基于集合的操作。我怀疑这不会是你经常做的那种事情在你求助于某种 OLAP / 数据仓库类型的解决方案之前),但尽管如此,这似乎是一个你真的不能没有的情况一个光标。
    • 参考 TPC 基准表明游标可能比人们普遍认为的更具竞争力。
    • 参考 自 Sql-Server 2005 以来游标的内存使用优化
  3. 有没有你能想到的问题,游标比基于集合的操作更适合解决?

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

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

4

4 回答 4

4

直接回答你的问题:

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

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

对于我真正想说的:

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

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

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

于 2011-07-29T18:44:12.570 回答
3

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

Itzik Ben Gan 在这里做了一些比较

图形

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

于 2011-07-29T22:44:55.120 回答
2

Since I've seen people manage to re-implement cursors (in all there varied forms) using other TSQL constructs (usually involving at least one while loop), there's nothing that cursors can achieve that can't be done using other constructs.

That's not to say that the re-implementations aren't equally as inefficient as the cursors that were avoided by not including the word "cursor" in that solution. Some people seem to purely hate the word, not the mechanics.

One place I've successfully argued to keep cursors was for a data transfer/transform between two different databases (we were dealing with clients here). Whilst we could have implemented this transfer in a set based manner (indeed, we previously had), there was problematic data that could cause issues for a few clients. In a set based solution, we had either to:

  • Continue the transfer, excluding failed client data at each table, leaving those clients partially transferred, or,
  • abort the entire batch

Whereas, by making the unit of transfer the individual client (using a cursor to select each client), we could make each client's transfer between the systems either work fully or be entirely rolled back (i.e. place each transfer in its own transaction)

I can't think of any situations where I've wanted to use a cursor below the "top level" of such transfers though (e.g. selecting which client to transfer next)

于 2011-07-29T19:10:44.667 回答
2

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

于 2011-07-29T19:25:41.310 回答