<p>“丢失的索引引用”并非真实错误,而是优化器基于执行计划生成的临时建议;sys.dm_db_missingindex* 视图记录不持久,重启或清缓存即消失,且建议需人工评估业务场景、过滤低效项、校验列类型与长度,并严禁自动建索引。</p>

“丢失的索引引用”不是数据库实际存在的错误状态,SQL Server 不会报这个错误,也没有对应的系统视图或 DMV 跟踪它——你真正遇到的,是查询执行计划里出现 MissingIndexes 节点,或是 sys.dm_db_missing_index_* 视图里有记录,但这些只是优化器的“建议”,不是“损坏”或“丢失”的事实。
为什么不能用存储过程“修复”缺失索引
缺失索引建议(sys.dm_db_missing_index_details 等)本质是查询优化器在生成执行计划时做的“事后推演”:如果当时有某个索引,这次查询本可以少扫几万行。它不表示索引真的“丢了”,也不表示数据库一致性出问题了。
- 这些建议不会持久化到重启后,SQL Server 服务重启、查询缓存清空、或执行
DBCC FREEPROCCACHE都会让它们消失 - 同一张表可能被多个查询建议不同组合的索引,比如一个查
WHERE a=1 AND b>10,另一个查WHERE b>10 AND c=5,直接照搬会建一堆重叠低效索引 -
user_seeks和avg_user_impact是估算值,没经过真实执行验证;有些“高分建议”对应的是每月跑一次的报表,建了反而拖慢写入 - 存储过程无法自动判断“这个建议该不该建”——它缺业务语境:这个表是不是高频写入?这个查询是不是只在凌晨ETL里跑?这个列是不是经常 NULL?
如何在存储过程中安全提取并过滤缺失索引建议
你可以用存储过程定期捞取建议,但必须加人工可读的过滤逻辑,避免盲目生成 DDL。关键不是“修复”,而是“降噪+聚焦”。
- 只看过去 7 天内有
user_seeks + user_scans >= 10的建议,排除偶发性扫描 - 跳过
database_id <> DB_ID()的记录(防止跨库误操作) - 排除包含
xml、geography、hierarchyid等不支持作为索引键的列类型(需 JOINsys.columns+sys.types校验) - 对
equality_columns和inequality_columns做长度截断保护:SUBSTRING(..., 1, 128),避免生成超长索引名导致CREATE INDEX报错 Msg 193 - 强制要求
improvement_measure > 1000(按官方公式计算),筛掉那些“提升 0.3% 却要多占 2GB 空间”的伪需求
生成 CREATE INDEX 语句时必须绕开的坑
即使你决定建索引,也不能直接把 DMV 返回的字符串拼起来就 EXEC——很多隐式陷阱会导致建索引失败或效果相反。
-
included_columns里如果有varchar(max)或text类型,CREATE INDEX会直接报错 Msg 1919,得提前过滤掉 - 如果
equality_columns包含计算列,必须确认该列已标记为PERSISTED,否则建索引失败 - 不要忽略文件组:默认建在 PRIMARY,但生产库通常有专用索引文件组(如
INDEX_FG),需手动替换语句中的ON [PRIMARY] - 对大表(行数 > 1000 万),加
WITH (SORT_IN_TEMPDB = ON, MAXDOP = 2),避免日志暴涨或锁死主文件组 - 永远别在存储过程中自动执行
CREATE INDEX—— 至少先输出到临时表或表变量,让 DBA 审核后再手工运行
最常被忽略的一点:缺失索引建议从不告诉你“删哪个旧索引”。而现实中,建一个新索引前,往往得先删掉三个重复或未使用的旧索引。这事没法靠 DMV 自动判断,只能结合 sys.dm_db_index_usage_stats 里的 user_seeks = 0 和 last_user_seek 时间戳人工比对。

















