SQL Server 2017特定LIKE条件查询慢,替换值后查询快的问题求助
排查SQL Server LIKE查询性能差异的原因及解决办法
先来说说为什么NAME LIKE '%ibm%'和NAME LIKE '%services%'会有这么大的性能差距,再给你对应的解决思路:
性能差异的核心原因
- 数据分布与选择性差异:这是最常见的诱因。大概率
%ibm%匹配到的行数远多于%services%——比如前者返回几万行,后者只返回几百行。行数差异会直接拉高JOIN、UNION(去重需排序)的计算成本;如果SQL Server对%ibm%的行数预估不准(哪怕补了统计信息,采样率不足或数据更新后未同步统计信息也会出现),就会选择低效的执行计划(比如用嵌套循环处理海量数据,而非更适合大数据集的哈希连接)。 - 统计信息准确性问题:你提到补了缺失的统计信息,但可能统计信息的采样率太低,或者没有覆盖
NAME列的特定模式分布。SQL Server依赖统计信息预估行数,如果预估数据和实际差几个数量级,执行计划就会彻底跑偏——比如误以为%ibm%只返回几百行,选了嵌套循环,结果实际返回几万行,性能直接崩盘。 - UNION的额外成本:
UNION会自动去重,需要对结果集做排序操作。如果%ibm%的结果集很大,排序的IO和CPU成本会飙升,而%services%结果集小,排序成本几乎可以忽略。
具体解决办法
- 先验证数据分布:跑两个简单的计数查询,确认行数差异:
如果行数差了2个数量级以上,那基本就是数据量导致的性能差异。SELECT COUNT(*) AS IBM_Match_Count FROM YourTable WHERE NAME LIKE '%ibm%'; SELECT COUNT(*) AS Services_Match_Count FROM YourTable WHERE NAME LIKE '%services%'; - 全扫描更新统计信息:普通统计信息更新可能用默认采样率,对分布不均的数据不够准确,直接跑全扫描更新:
这能让SQL Server拿到最准确的行数预估,生成更合理的执行计划。UPDATE STATISTICS [YourTableName] WITH FULLSCAN; -- 多表场景逐个更新 UPDATE STATISTICS [Table1] WITH FULLSCAN; UPDATE STATISTICS [Table2] WITH FULLSCAN; - 用全文索引替换LIKE:前后通配的LIKE本来就没法利用普通索引的键查找,对于大数据集,全文索引是更高效的方案:
- 给
NAME列创建全文索引; - 把查询改成
CONTAINS(NAME, 'ibm')(匹配包含ibm的任意字符串),或者CONTAINS(NAME, '"*ibm*"')(精确子串匹配,注意全文的通配符规则)。
全文索引的搜索效率远高于LIKE,尤其是匹配行数较多时。
- 给
- 评估并创建筛选索引视图:Database Tuning Advisor建议的筛选索引视图,应该是针对
%ibm%场景预先计算了JOIN后的结果集。这些视图相当于把你常用的查询逻辑(JOIN+过滤)预先物化,查询时直接读视图就能拿到结果,避免每次都做复杂的JOIN操作。不过要注意,物化视图会增加基表的维护成本(更新基表时视图也要同步更新),如果你的数据不是频繁更新,这个方案性价比很高。 - 对比执行计划找瓶颈:把两个查询的执行计划保存下来对比,重点看:
- 连接运算符:
%ibm%是不是用了嵌套循环?如果是,改成哈希连接可能会更好(可以用查询提示OPTION(HASH JOIN)临时测试); - 排序操作:是不是因为UNION去重导致的昂贵排序?如果业务场景允许不去重,直接把
UNION改成UNION ALL,能省掉排序的巨大成本; - 扫描类型:是不是做了全表扫描?可以考虑创建包含
NAME和JOIN关联列的覆盖索引,哪怕是索引扫描,也比全表扫描的IO成本低。
- 连接运算符:
- 复查索引碎片:你说重建了索引,但可以再确认下索引的碎片率:
如果碎片率还是很高,可能重建时的参数有问题,比如用了SELECT name, avg_fragmentation_in_percent FROM sys.dm_db_index_physical_stats(DB_ID(), OBJECT_ID('YourTable'), NULL, NULL, 'DETAILED');REBUILD WITH (ONLINE=ON)但没指定合适的填充因子。
内容的提问来源于stack exchange,提问作者Data Engineer




