使用 SET @Variable = Field= @Variable + 1 在堆上的意外更新结果,用聚集索引修复

Iro*_*fin 5 sql-server clustered-index heap update

我只是想了解为什么会发生这种情况,而我的 Google 搜索失败了。我们使用的是 SQL Server 2016 SP1。

这是这种情况:供应商表通过跟踪每个表的当前值来管理 ID。如果您正在执行插入操作,则可以调用一个函数来返回一个 ID 块。

因此,我们通过使用从真实表中选择来设置临时表select into(我们正在克隆一组数据,以使用不同的属性集重新插入)。

然后我们调用该函数并获取记录数的新 id(它只返回最大 ID,因此我们进行一些数学运算以获取下一个 id)。

然后我们更新表格:

update #temp set @nextId = Id = @nextId + 1
Run Code Online (Sandbox Code Playgroud)

期望它会为每条记录递增 1 并设置 id。

相反,为每 4 条记录设置相同的 ID,然后它会递增,接下来的 4 条会得到下一个 id,等等。为什么每 4 条记录?什么地方出了错?

结果

更有趣的是,如果我们在表上放置一个聚集索引,一切都会正常进行。

我确定这与表是堆有关……但不知道为什么。

链接到计划

Pau*_*ite 14

文档UPDATE声明说,(强调):

可以在 UPDATE 语句中使用变量名来显示受影响的旧值和新值,但这应该仅在 UPDATE 语句影响单个记录时使用

有些人试图通过对其使用施加更大的约束,使这种“古怪的更新”技术在多行上可靠地工作。事实仍然是,这依赖于观察到的效果和未记录的行为,所以你不应该期望它在一般情况下工作,或者在未来继续“工作”。

在没有重现脚本或执行计划的情况下,我无法确切说出您的情况“出了什么问题”,但最终这并不重要。如果被迫猜测,我会说您的更新运行在 DOP 4 并且四个线程同时读取相同的变量值。

建议您改用可靠的解决方案,例如ROW_NUMBER( docs )、IDENTITY functionsequence

  • 天啊,谢谢大家。我多虑了我的谷歌搜索,应该从一开始就开始。这是有道理的,您已经死了 4 个线程。我将计划添加到原始问题以供将来参考。 (2认同)