我正在尝试将具有5k大小的一行的目标表也更新为5k的行。
由于是一行,因此很容易知道行的实际大小:
select *
from sys.dm_db_index_physical_stats(DB_ID('RODS_HSD_ES'),
OBJECT_ID(N'TBL_BM_HSD_SUBJECT_AN_148_REPRO'), NULL, NULL, 'DETAILED')
自创建以来,表未更改。没有看到任何原因导致它失败。有想法吗?
我正在尝试将具有5k大小的一行的目标表也更新为5k的行。
由于是一行,因此很容易知道行的实际大小:
select *
from sys.dm_db_index_physical_stats(DB_ID('RODS_HSD_ES'),
OBJECT_ID(N'TBL_BM_HSD_SUBJECT_AN_148_REPRO'), NULL, NULL, 'DETAILED')
自创建以来,表未更改。没有看到任何原因导致它失败。有想法吗?
Answers:
该问题与您正在更新群集密钥的事实有关,并且目标表恰好具有分区方案1。当要求SQL Server更新群集密钥的任何组件时,它必须执行UPDATE
和和DELETE
或混合更新,其中一些行就地更新,而某些行不行。
如果从目标表中删除聚簇索引,则会看到更新有效。
错误消息虽然可能有些误导,但由于在更新过程中产生的行大小超过了最大长度,因此是准确的。
我建议您考虑将表的结构更改为:
VARCHAR(MAX)
用于所有这些列。如果您实际上在一列中不需要2GB的字符,为什么要用这种方式定义该列?将列定义为实际会遇到的最大大小。V_MAX_xxx
,V_64_xxx
和V_512_xxx
列等。为了简化您的再现,您可能需要消除光标,并且仅执行以下DML操作:
UPDATE dbo.TBL_BM_HSD_SUBJECT_AN_148_REPRO_TARGET
SET [sampletime] = '2015-12-29 01:11:26.687';
上面的列是群集键和分区键的组件之一(更新其他CI键列可以正常工作)。
有了聚集索引后,会出现以下错误:
消息511,第16级,状态1,第1行
无法创建大小为8287的行,该行大于允许的最大行大小为8060。
该语句已终止。
如果没有聚集索引,该语句将成功。
1有趣的是,如果我们从repro中消除了分区,即使已建立聚集索引,我们也会发现更新成功。
由于与我在回答上一个问题时所解释的原因大致相似,因此更新失败。
在这种情况下,因为你有可能更新多个行,其中一个键列一个的唯一索引的改变*,SQL服务器建立了一个计划,其中包括拆分,排序和折叠运营商,以避免中间唯一键冲突(参见本文的详细信息) 。
这样引入的Sort运算符遇到宽度超过限制的中间行(包括内部开销),因此引发错误。向OPTION (ROBUST PLAN)
更新查询添加提示表明这是不可避免的:
消息8619,级别16,状态2,行681
查询处理器无法生成查询计划,因为需要工作表,并且其最小行大小超过了最大允许的8060字节。为何需要工作表的典型原因是查询中的GROUP BY或ORDER BY子句。在没有“健壮计划”提示的情况下重新提交查询。
简短地了解一下源/目标数据之间的关系就不清楚了,但是如果可以保证每个更新操作最多影响一行,则可以通过TOP (1)
在update语句中添加来避免使用Split / Sort / Collapse :
UPDATE TOP (1) [TBL_BM_HSD_SUBJECT_AN_148_REPRO_TARGET]
SET ...
不过,这有点骇人听闻。理想情况下,更新语句的构造和索引应为优化器提供足够的信息,以便可以看到最多更新一行。特别是,最佳实践是编写确定性的更新语句。
鉴于设计的奇特之处和问题的不明确性,我什至不打算尝试破译数据关系,或者是实现详细信息所必需的查询和索引更改。
*正如Martin Smith在评论中指出的那样,在这种特殊情况下,如果不对表进行分区,这将不是问题。如果更新在每一行中将键设置为相同的确定性值,则不需要拆分/排序/折叠,除非该表也已在该键上分区。因此,此查询的另一种解决方案是不在sampletime上对表进行分区。