传统观念认为,基于集合的表处理应始终优先于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,虽然我可能应该 - 尽管它只会看到真正需要多长时间。
答案 0 :(得分:3)
不,没有规则基于集合总是更快。我们有游标是有原因的(并且不要误以为while循环或其他类型的循环确实与游标完全不同)。 Itzik Ben-Gan已经展示了一些游标更好的情况,特别是对于运行总计问题。还有一些情况你描述了你试图更新1200万行的地方,并且由于内存限制,日志使用或其他原因,SQL对于单个操作来说处理太多而不必溢出到tempdb,或者解决由于未能更快地获得更优化的计划,因此提前终止的次优计划。
游标得到一个糟糕说唱的原因之一是人们很懒,只是说:
DECLARE c CURSOR FOR SELECT ...
当他们几乎总是应该说:
DECLARE c CURSOR
LOCAL FORWARD_ONLY STATIC READ_ONLY
FOR SELECT ...
这是因为这些额外的关键字因各种原因使光标更有效。根据文档,您可能会认为其中一些选项是多余的,但在我的测试中并非如此。有关详细信息,请参阅我的this blog post以及来自其他SQL Server MVP Hugo Kornelis的this blog post。
所有人都说过,在大多数情况下,你最好的选择是基于设置(或至少如上所述,基于大块的设置)。但对于一次性管理任务(我希望你的1200万行更新),有时候编写游标比花费大量精力构建产生适当计划的最佳查询更容易/更有效。对于将在应用程序范围内作为正常操作运行的查询,那些尝试优化为基于集合的更多努力(请记住,您可能仍然以光标结束)。