导读:本期聚焦于小伙伴创作的《如何在SQL存储过程中实现复杂的字符串拆分并利用STRING_SPLIT函数》,敬请观看详情。把逗号分隔的订单编号批量写入关联表时,直接循环截取既慢又难维护。SQL Server 2016起内置的STRING_SPLIT函数可在集合层面一次性拆串,避免游标开销。本文说明在存储过程里如何调用该函数处理多分隔符、空值及排序问题,并对比传统WHILE截取写法在十万行数据下的执行差异,给出参数化防注入与结果去重实战示例,帮助后端人员把字符串解析逻辑下沉到数据库层,减少应用端代码复杂度与网络往返。

在编写SQL Server后端逻辑时,经常会遇到前端传来越来越长的逗号分隔字符串,需要在存储过程中将其拆成多行再关联业务表。过去我们习惯用WHILE循环配合CHARINDEX截取,但这种方式不仅代码冗长,而且在数据量上升时性能急剧下降。借助内置的STRING_SPLIT函数,可以用纯集合操作完成拆分,让存储过程更简洁也更高效。

如何在SQL存储过程中实现复杂的字符串拆分并利用STRING_SPLIT函数

一、STRING_SPLIT函数的基本用法

STRING_SPLIT是SQL Server 2016(兼容级别130及以上)引入的表值函数,它接收两个参数:待拆分的字符串和分隔符,返回一列名为value的结果集。与自定义拆分函数相比,它由数据库引擎原生实现,执行计划更优。

下面示例演示如何将一串产品编号拆成多行,并直接插入临时表供后续使用。注意分隔符当前仅支持单个字符,这是使用该函数的首要限制。

DECLARE @ids NVARCHAR(MAX) = '1001,1002,1003,1004';
SELECT value AS product_id
INTO #tmp_products
FROM STRING_SPLIT(@ids, ',');

SELECT * FROM #tmp_products;

上述代码在存储过程中可改写为接收@input_str参数,从而让调用方动态传入字符串。由于返回的是表,我们可以直接用INNER JOIN替代早期写法中的游标逐行处理。

需要提醒的是,STRING_SPLIT不保证返回行的顺序。如果业务要求按原字符串中出现顺序处理,需要额外借助排序键,后文会给出兼容写法。

二、在存储过程中封装拆分逻辑

实际项目中,我们往往要把拆分结果和主表关联,过滤出存在的记录。下面的存储过程接收用户选中的部门编号串,返回这些部门下的活跃员工。

使用STRING_SPLIT后,原本几十行的循环截取代码缩减为一条JOIN语句。同时,由于输入是参数化变量,不存在拼接SQL带来的注入风险,比在应用端拼IN语句更安全。

CREATE PROCEDURE dbo.GetActiveEmployeesByDepts
    @dept_ids NVARCHAR(MAX)
AS
BEGIN
    SET NOCOUNT ON;
    SELECT e.emp_id, e.emp_name, e.dept_id
    FROM dbo.employee e
    INNER JOIN STRING_SPLIT(@dept_ids, ',') s
        ON e.dept_id = TRY_CAST(s.value AS INT)
    WHERE e.status = 1;
END;

这里用TRY_CAST避免字符串里混入非数字导致整条语句报错。如果前端可能传入空白项,STRING_SPLIT会产生空字符串行,可在JOIN前用WHERE过滤,或者改用下面的CTE写法统一清洗。

在复杂存储过程里,建议先把拆分结果存入临时表并建索引,再参与多表关联。当拆分出的行数超过几千时,这种写法比内联JOIN更稳定,能显著减少重复拆分计算。

三、处理多分隔符与去重需求

STRING_SPLIT只支持单字符分隔符。如果历史数据混用了逗号、分号和竖线,需要先用REPLACE归一化。此外,用户重复勾选会造成重复编号,应当去重。

下面的示例先将多种符号统一为逗号,再嵌套调用STRING_SPLIT并配合DISTINCT得到干净编号列表,最后关联权限表。

CREATE PROCEDURE dbo.LoadRightsByCodes
    @mixed_str NVARCHAR(MAX)
AS
BEGIN
    SET NOCOUNT ON;
    DECLARE @normalized NVARCHAR(MAX) = REPLACE(REPLACE(@mixed_str, ';', ','), '|', ',');
    SELECT DISTINCT TRIM(s.value) AS right_code
    INTO #rights
    FROM STRING_SPLIT(@normalized, ',') s
    WHERE LEN(TRIM(s.value)) > 0;

    SELECT r.right_code, r.right_name
    FROM dbo.right_def r
    INNER JOIN #rights t ON r.right_code = t.right_code;
END;

TRIM函数用于清除首尾空格,LEN过滤空项。如果数据库兼容级别低于130,则需改用传统的XML或数字表拆分,但本文聚焦原生函数方案。

对于必须保序的场景,可先对原始串用序号标记,例如用OPENJSON配合自定义数组,或是在应用端传结构化JSON而非纯字符串,这样比强行给STRING_SPLIT加序更高效。

四、与传统WHILE截取写法对比

在没有STRING_SPLIT的年代,常用WHILE加CHARINDEX循环截取。下面给出典型旧写法,以便理解性能差异根源。

该写法每循环一次就要对剩余字符串做SUBSTRING和CHARINDEX,时间复杂度接近O(n^2),且产生大量批处理日志。在十万级字符串拆分测试中,旧写法常超过两秒,而STRING_SPLIT多在两百毫秒内完成。

DECLARE @s NVARCHAR(MAX) = '1,2,3,4,5,6,7,8,9,10';
DECLARE @pos INT, @item NVARCHAR(50);
CREATE TABLE #old_split(val NVARCHAR(50));
WHILE LEN(@s) > 0
BEGIN
    SET @pos = CHARINDEX(',', @s);
    IF @pos = 0
    BEGIN
        INSERT INTO #old_split VALUES(@s);
        BREAK;
    END
    SET @item = LEFT(@s, @pos - 1);
    INSERT INTO #old_split VALUES(@item);
    SET @s = STUFF(@s, 1, @pos, '');
END
SELECT * FROM #old_split;

从维护角度看,旧写法需要十行以上且容易在边界条件(如末尾无逗号)出错;新写法一行搞定,逻辑错误概率大幅降低。

综合来看,在支持STRING_SPLIT的环境里,存储过程的字符串拆分应优先采用该函数,仅在需多字符分隔或严格保序时考虑辅助处理或升级到SQL Server 2022的STRING_SPLIT有序特性(若有启用)。

五、实战注意事项与小结

在存储过程使用STRING_SPLIT时,应注意传入参数长度用NVARCHAR(MAX)以防截断;关联前尽量清洗空项;对拆分结果建临时表索引可进一步优化多表JOIN。

此外,若前端使用Entity Framework等ORM,可直接调用该存储过程并传字符串,避免先在前端拆成列表再发多次查询,减少网络往返。将拆分逻辑留在数据库层,既清晰又利于统一权限校验。

-- 综合示例:按逗号拆分订单号并标记是否存在
CREATE PROCEDURE dbo.CheckOrderExists
    @order_csv NVARCHAR(MAX)
AS
BEGIN
    SET NOCOUNT ON;
    SELECT
        s.value AS order_no,
        CASE WHEN o.order_id IS NOT NULL THEN 1 ELSE 0 END AS is_exist
    FROM STRING_SPLIT(@order_csv, ',') s
    LEFT JOIN dbo.orders o ON o.order_no = TRIM(s.value);
END;

通过上述示例可以看到,STRING_SPLIT让存储过程内的字符串拆分从繁琐脚本变为声明式查询。掌握它的限制与配套清洗技巧,就能在多数后台批量处理场景中写出高性能、易读且安全的SQL代码。

SQL存储过程STRING_SPLIT字符串拆分修改时间:2026-08-01 23:57:34

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。