字符串拆分为列表(函数)
今天试著自己实现了将字符串拆分为列表的功能,中间也有找一些资料,但我知道光看是没有用的,必须要自己试著实现印象才会深刻,必须要在实现过程中遇到问题然后解决问题,反复折腾过才算真正懂了!
拆分字符串这种数据处理功能性的代码,写成函数比较合适。
我写的第一个版本(有局限):
/*
思路:遍历字符串,保存被分隔字符,遇到分隔符后播入结果表,再重新开始保存新的被分隔字符
*/
if object_id('dbo.f_MySplit_1','TF') is not null
drop function dbo.f_MySplit_1
go
create function dbo.f_MySplit_1
(
@str varchar(1000), -- 待拆分字符串
@delimiter varchar(5) -- 分隔符(仅限一个字符)
)
returns @split table(item varchar(100))
as
begin
set @str = @str + @delimiter -- 便于统一处理
declare @len int, @fo int, @tmp varchar(100)
select @tmp = '',@len = len(@str + 'x') - 1, @fo = 1
while @fo @delimiter)
begin
set @tmp = @tmp + substring(@str,@fo,1)
end
else
begin
insert into @split values(@tmp)
set @tmp = ''
end
set @fo = @fo + 1
end
return
end
-- 测试
select * from testdb.dbo.MySplit_1('1,t5655565,9tt,10,a,bgfr,c',',')
item
----------
1
t5655565
9tt
10
a
bgfr
c
(7 row(s) affected)
期本功能是实现了,可以进行常规的拆分,不过在多试验了几个用例之后发现,它有相当的局限性,甚至可以说是bug
1、仅支持单个分隔符
2、没考虑 分隔符 为 null 或 空字符 的情况( 这种情况下,每一个字符被分隔为一列)
于是又做了更新,下面是第二个版本,基本上算是通用了:
/*
思路:定位前后分隔符位置,提取前后分隔之间的字符串
*/
if object_id('dbo.f_MySplit_2','TF') is not null
drop function dbo.f_MySplit_2
go
create function dbo.f_MySplit_2
(
@str varchar(1000), -- 待拆分字符串
@split varchar(100) = '' -- 分隔符
)
returns @result table(item varchar(100))
as
begin
set @str = isnull(@split,'') + @str + isnull(@split,'')
declare
@start_index int, -- 子串搜索开始点
@split_len int, -- 分隔符长度
@fore_location int, -- 前一个分隔符的位置
@back_location int -- 后一个分隔符的位置
select
@start_index = 1,
@split_len = len(@split + 'x') - 1, -- len()不包含尾随空格
@fore_location = 1,
@back_location = 1
if len(isnull(@split,'') + 'x') - 1 = 0 -- 若 @split 为 null 或 ''
begin
declare @fo int
set @fo = 1
while @fo <= len(@str)
begin
insert into @result
select substring(@str,@fo,1)
set @fo = @fo + 1
end
end
else
while (@start_index + @split_len - 1) < len(@str)
begin
set @fore_location = charindex(@split,@str,@start_index)
set @back_location = charindex(@split,@str,@start_index + @split_len)
insert into @result
select substring(@str,
@fore_location + @split_len, -- 取子串开始点
@back_location - @fore_location - @split_len) -- 子串长度
set @start_index = @back_location
end
return
end
-- 测试
select * from f_MySplit_2('I,@,like,@,coding',',@,')
select * from f_MySplit_2('1,2,3,4','')
select * from f_MySplit_2('1,2,3,4',null)
select * from f_MySplit_2('1,2,3,4',default)
均能得到预期结果
上面代码中两个细节说明一下:
1、set @str = isnull(@split,’’) + @str + isnull(@split,’’) ,防止 @split 为空的情况,null 与任何字符串相加的结果都为 null
2、@split_len = len(@split + ‘x’) - 1, 为什么不直接len((@split)呢,因为len(@string)默认不计算尾随的空格,这样处理一下后就变向支持了。
另一版本,相对好理解一点:
CREATE FUNCTION f_Split_V2
(
@str VARCHAR(500),
@delimiter VARCHAR(10)
)
RETURNS @result TABLE(items VARCHAR(100))
AS
BEGIN
DECLARE @index INT;
IF ISNULL(@delimiter+'x','x')='x' --当分隔符号为''或NULL(这是一个技巧)
BEGIN
WHILE LEN(@str+'x') > 1
BEGIN
INSERT INTO @result VALUES(SUBSTRING(@str,1,1));
SET @str = RIGHT(@str,LEN(@str+'x')-2);
END
RETURN;
END
SET @index = CHARINDEX(@delimiter,@str);
WHILE @index <> 0
BEGIN
INSERT INTO @result VALUES(LEFT(@str,@index-1));
--SET @str = RIGHT(@str,LEN(@str+'x')-LEN(@delimiter+'x')-@index+1);
SET @str = STUFF(@str,1,@index+LEN(@delimiter)-1,'')
SET @index = CHARINDEX(@delimiter,@str);
END
INSERT INTO @result VALUES(@str);
RETURN;
END
后来在网上看到了另一种奇特的方法,除了不支持分隔为空或null外,其余都支持,只是不好写为函数,可以考虑用存储过程,真的很简洁很强大。
与大家共享一下:
-- 巧用 union 替换分隔符,拆分字符串为列表
declare @str varchar(500), @split varchar(100), @sql varchar(800)
select @str = '小明呵呵又变帅呵呵了',@split = '呵呵'
set @sql = 'select ''' + replace(@str,@split,''' union all select ''') + ''''
--print @sql
exec(@sql)
------
小明
又变帅
了
(3 row(s) affected)
感谢大神们的奇思妙想,感谢互联网!!!