2

传统观点认为,基于集合的表处理应始终优于 RBAR - 特别是当表变大和/或您需要更新许多行时。

但这总是成立吗?我经历过很多情况——在不同的硬件上——基于集合的处理显示时间消耗呈指数增长,而将相同的工作负载分成更小的块会产生线性增长。

我认为被证明完全错误会很有趣——如果我遗漏了一些明显的东西——或者如果没有,那么知道什么时候分担工作量是值得的,这将是非常好的。随后确定哪些指标有助于决定使用哪种方法。我个人希望以下组件很有趣:

  • 工作量大小
  • 日志文件的大小和增长
  • 内存量
  • 磁盘系统的速度

任何其他?CPU/CPU 核心数?

示例 1:我有一个 1200 万行的表,我必须使用另一个表中的数据更新每行中的一个或两个字段。如果我在一个简单的 UPDATE 中执行此操作,我的测试箱需要大约 30 分钟。但是如果我把它分成十二块,我会在大约 24 分钟内完成 - 即:

WHERE <key> BETWEEN 0 AND 1000000
WHERE <key> BETWEEN 1000000 AND 2000000
...

示例 2:是一个 200+ 百万行的表,还需要对几乎所有行进行多次计算。如果一个做全套,我的盒子会运行三天,甚至不会完成。如果我编写一个简单的 C# 来执行完全相同的 SQL,但附加了 WHERE 子句以一次将事务大小限制为 100k 行,它将在大约 14 小时内完成。

记录一下:我的结果来自相同的数据库,基于相同的物理硬件,更新了统计信息,索引没有变化,简单的恢复模型等。

不,我还没有尝试过“真正的”RBAR,尽管我可能应该尝试过——尽管这只是看看真正需要多长时间。

4

1 回答 1

3

不,没有规定基于集合总是更快。我们有游标是有原因的(不要误以为 while 循环或其他类型的循环实际上与游标完全不同)。Itzik Ben-Gan 已经展示了一些游标更好的案例,特别是对于运行总计问题。在某些情况下,您描述了您尝试更新 1200 万行的情况,并且由于内存限制、日志使用或其他原因,SQL 无法将其作为单个操作处理而不必溢出到 tempdb 或解决由于没有足够快地获得更优化的计划,提前终止的次优计划。

cursors 名声不好的原因之一是人们很懒,只会说:

DECLARE c CURSOR FOR SELECT ...

当他们几乎总是应该说:

DECLARE c CURSOR 
    LOCAL FORWARD_ONLY STATIC READ_ONLY 
    FOR SELECT ...

这是因为这些额外的关键字由于各种原因使光标更有效。根据文档,您会认为其中一些选项是多余的,但在我的测试中并非如此。有关更多详细信息,请参阅我的这篇博文和SQL Server MVP Hugo Kornelis 的这篇博文。

综上所述,在大多数情况下,您最好的选择是基于集合的(或者至少是基于大块的集合,如上所述)。但是对于一次性管理任务(我希望您的 1200 万行更新是这样),有时只编写游标比花费大量精力构建产生适当计划的最佳查询更容易/更有效。对于将在应用程序范围内作为正常操作运行很多的查询,那些值得更多努力尝试优化为基于集合的查询(请记住,您最终可能仍会使用游标)。

于 2011-09-03T17:17:41.213 回答