STRING_SPLIT是SQL Server 2016+首选字符串拆分方案,无需自定义函数或递归CTE,但返回无序、无序号的value列,仅支持单字符分隔符,且需配合CROSS APPLY使用。

SQL Server 2016+ 直接用 STRING_SPLIT 最省事
只要数据库版本 ≥ 2016,STRING_SPLIT 就是首选方案——它不需建函数、不写递归、不拼 XML,一行就能把字符串转成结果集。但要注意它返回的是无序的 value 列,且不带序号。
-
STRING_SPLIT必须配合CROSS APPLY使用,不能直接在SELECT后裸调 - 分隔符只能是单字符(比如
','或'|'),传入多字符会报错Invalid parameter 2: delimiter - 拆出的
value是NVARCHAR(4000)类型,若原始字符串含超长项,会被截断(SQL Server 2022 起支持NVARCHAR(MAX)输出) - 连续分隔符(如
'a,,b')会产生空字符串行,需加WHERE value != ''过滤
在存储过程中安全调用 STRING_SPLIT
直接把参数传给 STRING_SPLIT 很常见,但容易忽略 NULL 和空字符串处理。尤其当调用方传入 NULL 时,STRING_SPLIT(NULL, ',') 返回空结果集,不会报错,但后续逻辑可能漏数据。
- 务必在
CROSS APPLY前检查输入参数:IF @input IS NULL OR LTRIM(@input) = '' RETURN - 如果需要保留原始顺序(比如第1个是主分类、第2个是子分类),必须额外加序号:用
ROW_NUMBER() OVER (ORDER BY (SELECT NULL)),但注意这不能保证和原字符串位置一致——STRING_SPLIT本身不保证顺序 - 想严格按位置取值(如“取第3个元素”),别依赖
STRING_SPLIT+TOP 3,应改用SUBSTRING_INDEX思路或自定义函数
为什么不用 STRING_SPLIT 也能实现,但通常不该这么做
有人为了“可控性”坚持手写递归 CTE 或 XML 拆分,其实只在两个场景真有必要:
- SQL Server 版本 STRING_SPLIT 根本不存在)
- 必须严格保序且需返回位置索引(例如要区分“第一个逗号前”和“第二个逗号后”),而你又无法接受外部程序处理
其余情况,手写方案反而埋坑:递归 CTE 容易触发 MAXRECURSION 限制;XML 方案对特殊字符(、<code>&)需手动转义,否则解析失败;自定义函数在高并发下可能成为性能瓶颈。
实际存储过程片段示例
以下是一个带防御性检查、去空、并关联原记录 ID 的典型写法:
CREATE PROCEDURE dbo.SplitTags
@TagList NVARCHAR(MAX)
AS
BEGIN
IF @TagList IS NULL OR LTRIM(@TagList) = ''
RETURN;
SELECT
ID = ROW_NUMBER() OVER (ORDER BY (SELECT NULL)),
Tag = TRIM(s.value)
FROM STRING_SPLIT(@TagList, ',') s
WHERE s.value <> '';
END
重点看三处:TRIM 去首尾空格、WHERE s.value '' 过滤空项、ROW_NUMBER() 补序号——这些都不是 STRING_SPLIT 自带的,得自己补全。
最常被忽略的是:哪怕只拆一个字符串,STRING_SPLIT 返回的仍是表结果集,不能直接赋值给标量变量;若需取首个值,必须用 SELECT TOP 1 @first = value FROM STRING_SPLIT(...),且要处理无结果时变量保持旧值的问题。

















