use*_*004 16 sql t-sql sql-server
假设您有一个包含大约500万条记录的表和一个nvarchar(max)填充了大量文本数据的列.您希望以尽可能最快的方式将此列设置为NULLif SomeOtherColumn = 1.
蛮力UPDATE在这里不能很好地工作,因为它将创建大型隐式事务并永远占用.
一次进行小批量50K记录的更新有效,但在32核/ 64GB服务器上完成仍需要47个小时.
有没有办法更快地完成此更新?是否有任何神奇的查询提示/表格选项牺牲其他东西(如并发)以换取速度?
注意:创建临时表或临时列不是一个选项,因为此nvarchar(max)列涉及大量数据,因此占用大量空间!
PS:是的,SomeOtherColumn已经编入索引.
从我能看到的一切看起来,你的问题与索引无关.
关键似乎是你的nvarchar(max)字段包含"大量"数据.考虑一下SQL为了执行此更新必须执行的操作.
由于您要更新的列可能超过8000个字符,因此它会在页外存储,这意味着当它非NULL时读取此列需要额外的工作.
当您运行一批50000次更新时,SQL必须将其置于隐式事务中,以便在出现任何问题时可以回滚.为了回滚,它必须在事务日志中存储列的原始值.
假设(为简单起见)每列平均包含10,000个字节的数据,这意味着50,000行将包含大约500MB的数据,这些数据必须临时存储(在简单恢复模式下)或永久存储(在完全恢复模式下).
无法禁用日志,因为它会危及数据库的完整性.
我在我的狗慢桌面上运行了一个快速测试,运行甚至10,000的批次变得非常慢,但是将大小减少到1000行,这意味着临时日志大小大约10MB,工作得很好.
我加载了一个包含350,000行的表,并标记了50,000个用于更新的表.这在大约4分钟内完成,并且由于它线性扩展,你应该能够在我的1台2GB台式机上大约6小时内在我的狗慢桌面上更新你的整个5Million行,所以我希望你的强大服务器支持更好的东西通过SAN或其他东西.
您可能希望将update语句作为select运行,仅选择主键和大nvarchar列,并确保它以您期望的速度运行.
当然瓶颈可能是其他用户在服务器上锁定存储或内存上的内容或争用,但由于您没有提及其他用户,我将假设您在单用户模式下拥有数据库.
作为优化,您应确保事务日志位于与数据不同的物理磁盘/磁盘组上,以最大限度地缩短查找时间.