谈及 SQL Server 2019 中的字符串函数,首先需要纠正一个常见误解:该版本并未新增任何字符串函数。CHARINDEX、TRIM、CONCAT_WS 等函数早在 2017 甚至更早的版本中就已存在。影响查询效率的核心因素并非函数的新旧,而是索引策略与执行模式的协同配合——这一点经常被开发者忽略。

CHARINDEX 能否利用索引?不能,但可通过间接方式实现索引加速
直接使用 WHERE CHARINDEX('abc', Column) > 0 会导致全表扫描,原因很明确:函数作用于列上,破坏了索引的有序性。不过,通过持久化计算列可以巧妙绕过这一限制。关键要点包括:
- 必须使用
PERSISTED关键字将计算结果物理存储,否则无法创建索引。 - 计算列的表达式应保持简单,例如
CHARINDEX('张', CustomerName)是允许的,但CHARINDEX(UPPER(@keyword), UPPER(CustomerName))则不行——其中混入了变量和函数嵌套,优化器无法处理。 - 过滤索引的谓词需明确指定,比如使用
WHERE CustomerNameContains > 0,不建议写成>= 1,因为 SQL Server 优化器对> 0的识别更为稳定。 - 还需注意一个易错点:如果原列允许 NULL,那么
CHARINDEX对 NULL 输入会返回 NULL 而非 0,导致计算列值为 NULL,从而整行被过滤索引排除——设计时需提前考虑。
用 TRIM 替换 LTRIM+RTRIM,但避免在 WHERE 子句中直接使用
TRIM 语法比 LTRIM(RTRIM(...)) 更简洁,但若将其放入 WHERE TRIM(Name) = 'John',同样会导致索引失效——这与 RTRIM(LTRIM(Name)) = 'John' 的危险程度完全一致。正确做法主要有两种:
- 在 ETL 或应用层提前清洗数据后再写入,或者创建计算列
NameClean AS TRIM(Name) PERSISTED,然后对该列建立索引。 TRIM(' ' FROM Column)与TRIM(Column)行为相同,但显式指定字符可以让你拥有更多控制权——例如,当你只想去除连字符'-'时,二者差异就显现出来了。- 性能方面两者没有本质区别,
TRIM本质上是语法糖,底层调用的依然是相同的字符串处理逻辑。
批模式执行(Batch Mode)或可隐性加速字符串运算
SQL Server 2019 在行存储表上启用了“行存储上的批模式”(Batch Mode on Rowstore),这对涉及字符串函数的大批量过滤查询具有隐性收益。具体而言:
- 当查询包含大表扫描 +
CHARINDEX或SUBSTRING时,若满足条件(例如内存充足、统计信息较新,且查询计划选择了批模式),CPU 向量化处理可使字符串查找速度提升 2–5 倍。 - 该模式无需显式启用,但可通过
SELECT * FROM sys.dm_exec_query_stats qs CROSS APPLY sys.dm_exec_query_plan(qs.plan_handle)查看执行计划 XML 中是否包含BatchMode="on"。 - 注意:批模式不会改变函数语义,也无法修复索引失效问题,它只是让“不得不扫描”的场景运行得更快一些。
归根结底,真正限制性能的从来不是函数名称是否新颖,而是你是否成功将字符串操作从 WHERE 谓词中“剥离”——要么提前物化,要么交由全文索引或列存储处理。函数只是工具,索引与执行模式才是性能的杠杆支点。
