string_split在sql server 2016中可用但需数据库兼容级别≥130;使用时须过滤空/空白值、显式编号保序、拆分后落临时表建索引以保障性能与正确性。

STRING_SPLIT 在 SQL Server 2016 存储过程中能用,但必须先确认兼容级别
直接执行 STRING_SPLIT 报错 “Invalid object name 'STRING_SPLIT'”,大概率是数据库兼容级别低于 130。SQL Server 2016 虽然自带该函数,但默认新建数据库可能仍设为 120(对应 SQL Server 2014)。
检查当前级别:SELECT COMPATIBILITY_LEVEL FROM sys.databases WHERE name = DB_NAME()
升级需显式执行(注意:影响查询计划缓存,建议在维护窗口操作):ALTER DATABASE CURRENT SET COMPATIBILITY_LEVEL = 130
- 不能只改实例级设置,必须对目标数据库单独设
- 升级后无需重启服务,但已有执行计划会失效,首次调用可能稍慢
- 如果数据库被其他应用强依赖旧行为(比如用到了已弃用的语法),升级前务必在测试库验证
空输入、空格、重复分隔符会导致意外空行或转换失败
STRING_SPLIT 是纯机械切分,不清洗、不跳过、不合并。传入 '1,, 2 , ' 会返回四行:'1'、''、' 2 '、' '——后两行带空格,中间一行为空字符串。
常见崩溃点:CAST(s.value AS INT) 遇到空串或纯空格直接报错 “Conversion failed”WHERE t.id IN (SELECT value FROM STRING_SPLIT(@csv, ',')) 中,只要有一行是空,整个 IN 判断就变成空集,逻辑静默失效
- 必须加
WHERE TRIM(s.value) != ''过滤空/空白项 - 所有后续类型转换前,先用
TRY_CAST(... AS INT)或ISNUMERIC(TRIM(s.value)) = 1做兜底 - 不要依赖
value = '1'精确匹配,而要用TRIM(s.value) = '1'
顺序不可靠,不能靠位置取值或隐式 JOIN
SQL Server 2016–2019 的 STRING_SPLIT 不保证返回顺序。输入 'a,b,c',结果可能是 c、a、b。若你在存储过程里写 SELECT TOP 1 value FROM STRING_SPLIT(@s, ',') 想取第一个,实际拿到的可能是任意一个。
需要保序时,唯一可靠做法是显式编号:SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS pos, TRIM(value) AS val FROM STRING_SPLIT(@csv, ',') WHERE TRIM(value) != ''
- 别写
ORDER BY value——那是按字典序排,不是按原始位置 -
ORDER BY (SELECT NULL)是惯用写法,但并发高时仍有极小概率错序;更稳的是先插入临时表再编号 - 如果业务真依赖“第 N 个值”,比如解析
'姓名,电话,邮箱',建议提前在应用层拆好再传参数,别在存储过程里靠位置硬猜
性能容易崩,别在 WHERE 子句里嵌套调用
把 STRING_SPLIT 直接塞进 WHERE id IN (SELECT value FROM STRING_SPLIT(@ids, ',')),优化器无法估计返回行数,常生成嵌套循环 + 全表扫描,几十个 ID 就明显变慢,上千个基本卡死。
正确路径是“拆分 → 清洗 → 落临时表 → 建索引 → 关联”:SELECT TRIM(value) AS val INTO #split_ids FROM STRING_SPLIT(@ids, ',') WHERE TRIM(value) != '';CREATE INDEX IX_val ON #split_ids(val);
- 临时表有统计信息,JOIN 或 EXISTS 时优化器能选哈希连接
- 避免在循环体里反复调用
STRING_SPLIT,尤其当它嵌在游标或 WHILE 里 - 如果调用频繁且参数稳定,考虑改用表值参数(TVP),客户端传 DataTable,SQL Server 可走索引查找
WHERE TRIM(...) != '' 和一行 CREATE INDEX,都能避开 80% 的线上故障。










