介绍
最近在更新应用程序时,我收到以下错误消息:
Msg 5074 Level 16 State 1 Line 1 The statistics 'BreakingStuff' is dependent on column 'TaskText'. Msg 4922 Level 16 State 9 Line 1 ALTER TABLE ALTER COLUMN TaskText failed because one or more objects access this column.
基于分析的错误消息示例
这是针对表中的列手动创建的统计信息。实际上,有多个手动创建的统计数据引用了相关列。
解决方法
删除表的统计信息,运行更新,然后重新创建统计信息后,一切都运行良好。
分析
我首先对 db<>fiddle 进行了一个简单的模拟,看看是否可以重现该问题。经过一番摆弄(双关语),我发现只有当我减小列的大小时才会触发错误消息。
例如varchar(50)
-->varchar (40)
这是db<>fiddle。
我在 Microsoft Learn 上找到的唯一警告是:
-
当现有数据转换为新类型时,修改已包含数据的列的数据类型可能会导致数据永久丢失。此外,依赖于已修改列的代码和应用程序可能会失败。其中包括查询、视图、存储过程、用户定义函数和客户端应用程序。请注意,这些故障会级联。例如,调用依赖于已修改列的用户定义函数的存储过程可能会失败。在对列进行任何更改之前,请仔细考虑。
参考: 修改列(Microsoft Learn | SQL)
我知道减少列大小可能会导致数据丢失,但事实并非如此,因为数据都短于varchar(40)
.
问题
- 为什么由于手动创建的统计信息而减少列的大小会导致错误?
- 为什么增加列的大小不会导致手动创建的统计信息出现错误?
SQL Server 2022 在创建统计信息时有一个新选项,可以根据文档自动执行此操作(重点是我的):
如果不首先删除统计信息对象,则无法修改附加了手动创建的统计信息的列的属性 - 这是为了确保统计信息对象准确反映列的内容。当检测到手动创建的统计列时,SQL Server 会返回一条错误消息,指出“ALTER TABLE ALTER COLUMN 失败,因为一个或多个对象访问此列”。
我在这里写了一篇关于它的博客文章
有趣的是,如果 SQL Server 在列上自动创建了统计对象,并且您随后修改了该列,则不会收到此类错误。 SQL Server 以静默方式删除统计对象,并修改列。在执行需要统计对象的查询之前,不会自动重新创建自动创建的统计对象。
增加列的大小是仅元数据操作,实际上不会更改任何现有数据,因此 stats 对象仍然有效。
减小列的大小可能意味着值被截断,这可能会使统计数据无效。