string_split在存储过程中需谨慎使用:2016–2019版必须用row_number()显式编号保序,须过滤空值并trim清洗,避免直接嵌套在where中导致性能崩溃;2022+支持ordinal参数可简化处理。

STRING_SPLIT 能用,但在存储过程中直接套用大概率出错——顺序不可靠、空值不处理、嵌套调用性能崩,不是写完就能跑通的。
STRING_SPLIT 在存储过程中必须显式编号才敢用
SQL Server 2016–2019 的 STRING_SPLIT 返回结果无序,哪怕输入是 'a,b,c',查询可能返回 c,a,b。若你在存储过程里靠位置 JOIN 或取第 2 个元素,不加序号等于埋雷。
- SQL Server 2022+:直接用
STRING_SPLIT(@str, ',', 1),ordinal列自动带 1-based 顺序 - SQL Server 2016–2019:必须包装
ROW_NUMBER() OVER (ORDER BY (SELECT NULL)),且不能写ORDER BY value(那是按值排序,不是按出现顺序) - 更稳妥做法:先插入临时表,再编号——避免并发下
(SELECT NULL)仍可能错序
连续分隔符和首尾空格会悄悄破坏逻辑
STRING_SPLIT 是纯机械切分:遇到 ' a ,,b ' 会返回四行 —— ' a '、''、''、'b '。这些空格和空串在 IN 子句或 JOIN 中极易导致漏匹配或转换失败。
- 过滤空值:
WHERE TRIM(value) != '' - 统一清洗:
TRIM(value)替换原value字段 - 转数字前务必检查:
CAST(TRIM(value) AS INT)否则空串或空格串直接报错Conversion failed
在 WHERE 或 JOIN 中直接调用 STRING_SPLIT 是性能杀手
把 STRING_SPLIT 塞进 WHERE id IN (SELECT value FROM STRING_SPLIT(@ids, ',')) 这类语句里,SQL Server 很难生成高效执行计划。函数不可内联,优化器无法下推谓词,常退化为嵌套循环 + 表扫描,尤其当 @ids 超过几十个值时,响应时间陡增。
- 推荐做法:先把拆分结果存入临时表,再建索引
SELECT TRIM(value) AS val INTO #split_ids FROM STRING_SPLIT(@ids,',') WHERE TRIM(value) != '';CREATE INDEX IX_val ON #split_ids(val); - 更优方案:应用层改用表值参数(TVP),服务端可走索引查找,避免字符串拼接和重复拆分
- 绝对避免:在大表的
WHERE条件中反复调用STRING_SPLIT,尤其是作为子查询嵌套在循环逻辑里
什么时候不该用 STRING_SPLIT,而该写自定义函数
只有两个真实场景值得放弃 STRING_SPLIT:
- SQL Server 版本低于 2016(
STRING_SPLIT根本不存在) - 必须严格保序且需返回位置索引(例如“取第 3 个逗号前的内容”),又无法接受外部程序预处理
其余情况,手写递归 CTE 或 XML 拆分反而埋坑:MAXRECURSION 限制、特殊字符(、<code>&)需手动转义、自定义函数在高并发下易成瓶颈。真正麻烦的从来不是“怎么拆”,而是拆完之后怎么保序、保空、保性能——这些细节藏在 TRIM、ROW_NUMBER() 和临时表里,而不是函数名里。










