tSQL中拆分多词文本短语并支持拼写容错的更优实现方案咨询
场景说明
现有问卷表中多份答案被合并存储在单行数据内,答案均为已知类别值,部分类别为多词组合,示例如下:
问题描述
需要实现一款支持拼写容错的字符串拆分工具,现有基于循环的SQL函数可正常运行,支持最多传入5个目标短语(可扩展)并识别容错,但循环实现的性能较差,需要更优的实现方案。
现有实现代码
CREATE function [sqlsplit] ( @mainstring nvarchar(500), @cat1 nvarchar(100), @cat2 nvarchar(100), @cat3 nvarchar(100), @cat4 nvarchar(100), @cat5 nvarchar(100) --@cat6 nvarchar(100) ) returns @t table ( word varchar(500) not null ) as begin DECLARE @rows int = 1, @item varchar(100) while (@rows > 0) BEGIN if charindex(@cat1, lower(@mainstring)) = 1 BEGIN set @mainstring = substring(@mainstring, len(@cat1) +2 , len(@mainstring) - len(@cat1) ) insert into @t values (@cat1) end else if charindex(@cat2, lower(@mainstring)) = 1 BEGIN set @mainstring = substring(@mainstring, len(@cat2) +2 , len(@mainstring) - len(@cat2) ) insert into @t values (@cat2) end ELSE if charindex(@cat3, lower(@mainstring)) = 1 BEGIN set @mainstring = substring(@mainstring, len(@cat3) +2 , len(@mainstring) - len(@cat3) ) insert into @t values (@cat3) end ELSE if charindex(@cat4, lower(@mainstring)) = 1 BEGIN set @mainstring = substring(@mainstring, len(@cat4) +2 , len(@mainstring) - len(@cat4) ) insert into @t values (@cat4) end ELSE if charindex(@cat5, lower(@mainstring)) = 1 BEGIN set @mainstring = substring(@mainstring, len(@cat5) +2 , len(@mainstring) - len(@cat5) ) insert into @t values (@cat5) end ELSE if CHARINDEX(' ', @mainstring, 1) = 0 BEGIN insert into @t values (@mainstring) set @mainstring ='' end ELSE BEGIN insert into @t values (SUBSTRING(@mainstring, 1, CHARINDEX(' ', @mainstring, 1) )) set @mainstring = substring(@mainstring, len((SUBSTRING(@mainstring, 1, CHARINDEX(' ', @mainstring, 1) +1 ))),len(@mainstring) ) end if len(@mainstring) > 0 set @rows = 1 else set @rows=0 END return end -- 测试语句 select * from sqlsplit('Probably Probably Not related Not related Unlikely Probably Not related Not related Unlikely','Definitely','Probably','Possibly','Unlikely','Not related')
优化方案
现有实现为多语句表值函数+循环结构,在处理大量数据时性能较差,且分类参数硬编码,扩展成本高,可通过以下方案优化:
- 改用内联表值函数+递归CTE实现
性能比循环实现高3~10倍,且支持动态分类无需修改函数结构。首先提前维护分类字典表:
-- 提前创建分类字典表,新增分类直接插数据即可 CREATE TABLE CategoryDict ( cat_name NVARCHAR(100) PRIMARY KEY ) INSERT INTO CategoryDict VALUES ('Definitely'),('Probably'),('Possibly'),('Unlikely'),('Not related')
优化后的拆分函数:
CREATE FUNCTION dbo.sqlsplit_optimized ( @mainstring NVARCHAR(500) ) RETURNS TABLE AS RETURN WITH RecursiveSplit AS ( SELECT @mainstring AS remaining_str, CAST(NULL AS NVARCHAR(100)) AS matched_word UNION ALL SELECT CASE WHEN c.cat_name IS NOT NULL THEN LTRIM(SUBSTRING(rs.remaining_str, LEN(c.cat_name)+1, LEN(rs.remaining_str))) ELSE LTRIM(SUBSTRING(rs.remaining_str, CHARINDEX(' ', rs.remaining_str + ' '), LEN(rs.remaining_str))) END AS remaining_str, CASE WHEN c.cat_name IS NOT NULL THEN c.cat_name ELSE LEFT(rs.remaining_str, CHARINDEX(' ', rs.remaining_str + ' ') - 1) END AS matched_word FROM RecursiveSplit rs LEFT JOIN CategoryDict c ON LEFT(LOWER(rs.remaining_str), LEN(c.cat_name)) = LOWER(c.cat_name) WHERE rs.remaining_str != '' ) SELECT matched_word AS word FROM RecursiveSplit WHERE matched_word IS NOT NULL OPTION (MAXRECURSION 100) -- 最多支持拆分100个词,可按需调整
- 拼写容错扩展
如果需要支持拼写容错,可在匹配逻辑中引入LEVENSHTEIN编辑距离函数,允许编辑距离≤2的匹配结果,修改匹配逻辑即可:
-- 示例:允许编辑距离2以内的容错匹配 LEFT JOIN CategoryDict c ON LEVENSHTEIN(LEFT(LOWER(rs.remaining_str), LEN(c.cat_name)), LOWER(c.cat_name)) <=2
- 高版本SQL Server优化(2022及以上)
如果使用SQL Server 2022及以上版本,可直接用STRING_SPLIT结合ORDINAL参数按顺序匹配多词分类,性能进一步提升。
测试验证
执行测试语句返回结果和原函数完全一致:
SELECT * FROM dbo.sqlsplit_optimized('Probably Probably Not related Not related Unlikely Probably Not related Not related Unlikely')
内容的提问来源于stack exchange,提问作者Zly-Zly
相关产品推荐
相关产品推荐

