SqlServer-分词

字符串根据词性拆分分词

在工作中有的时候需要对人工填写的地址与数据库的地址进行匹配,有的时候需要对地址进行拆分,将地址规整。但多数情况下,人工填写是不规范的,因此一个根据词性拆分的方法,以便在拆分时能起到辅助作用。
如:南塘浜路11-15号 (虚拟)

序号分词词性位置
1南塘浜路cn1
211num5
3-symbol7
415num8
5cn10

通过拆分,可以获得“-”前后的数字和文本,这样也便于将字符串规整为连续型地址,如:南塘浜路11号,南塘浜路12号…南塘浜路15号等。注:本文仅介绍拆分成分词的方法。

1/思路

(拿中文文本举例)如下表所示,对每个字符的词性进行判断,如果该字符不是中文(则不算其位置),用字符原来的位置-判断字符为中文词性后的位置,得出来的结果就可以将字符串归类并拼接成分词。

字符原顺序中文字符再次排序分词归类
111(=1-1+1)
221(=2-2+1)
331(=3-3+1)
441(=4-4+1)
15--
16--
-7--
18--
59--
1056(=10-5+1)

2/创建表值函数-生成序列表

  • 给字符串生成序列表,这样就获得了字符串原来的位置
create function [dbo].[fn_serial_numer]
(@n int)
returns table as
return
with t1 as(select 1 n union all select 1)
,t2 as(select 1 n from t1,t1 a,t1 b,t1 c)
,t3 as(select 1 n from t2,t2 a,t2 b,t2 c)
,t4 as(select 1 n from t3,t3 a)
select top(@n) row_number()over(order by(select 1)) n from t4 
order by n 
select substring('南塘浜路11-15号',n,1)ch/*单个字符*/,n /*原始顺序*/
from fn_serial_numer(len('南塘浜路11-15号'))
chn
1
2
3
4
15
16
-7
18
59
10

3/创建表值函数-字符串根据词性拆分成分词

create function  [dbo].[fn_split_by_property]
(
@str varchar(max)
)

returns  @table table(
pkid int/*序号*/
,keys varchar(max)/*拆分后的分词*/
,property varchar(32)/*词性*/
,indexs int/*位置*/)
as
begin 

--新建一个临时表
declare @tmptable table(pkid int,keys varchar(max),property varchar(32),indexs int)

/**1
分词归类
*/
;with t0 as (
		--将字符串拆分成一行行单个字符
		select substring(@str,n,1)ch/*单个字符*/,n /*原始顺序*/
		from fn_serial_numer(len(@str))
		)
,t1 as(/*只取中文字符*/
select ch/*单个字符*/
,n/*原始顺序*/
,id=row_number()over(order by n)/*这里对只取中文字符字符排序*/
,rid=n-row_number()over(order by n)+1 
,'cn' property /*词性*/
from t0 
where unicode(ch) between 19968 and 40869 /*判断是否是中文字符*/
)
,t2 as(/*只取数字*/
select ch,n
,id=row_number()over(order by n)
,rid=n-row_number()over(order by n)+1
,'num' property
from t0 
where ch like '%[0-9]%'
)
,t3 as(/*只取英文*/
select ch,n
,id=row_number()over(order by n)
,rid=n-row_number()over(order by n)+1
,'en' property
from t0 
where ch like '%[a-zA-Z]%'
)
,t4 as(/*除中文、数字、英文之外的字符串*/
select ch,n
,id=row_number()over(order by n)
,rid=n-row_number()over(order by n)+1
,'symbol' property
from t0  a
where not exists(select * from t1 where a.ch=ch)
and  not exists(select * from t2 where a.ch=ch)
and  not exists(select * from t3 where a.ch=ch)
)
,tmp as (
select* from t1 union 
select* from t2 union
select* from t3 union
select* from t4
)
/**
通过归类和词性,合并成一个个分词,但是顺序会打乱
如:南塘浜路  11  -   15    号 
*/
insert into @tmptable
        ( pkid, keys, property,indexs )
select pkid,keys,property,indexs
from(
		select row_number()over(order by (select 0))pkid/*随机生成序列,方便循环*/
		,rid
		,keys=(select  ''+ch from tmp b where b.rid=a.rid and b.property=a.property order by b.n for xml path('')) 
		,a.property /*词性*/
		,0 indexs /*关键字符串在原字符串的位置*/
		from tmp a 
		group by rid,a.property
		)tmp



/**2/获取分词在字符串中的位置
--循环,获得每个分词在原字符串中的位置,
--每次循环后,将上述取的位置在原字符串中替换成 ' '
--这样即使字符串中相同的分词,也会得到不同的位置
*/
declare @i int ,@str_tmp varchar(max),@keys varchar(max),@index int
set @i =1
set @str_tmp=@str
set @keys=''
set @index=1

while @i<=(select max(pkid) from @tmptable )
begin
set @keys=(select keys from @tmptable where pkid=@i)
set @index=charindex(@keys,@str_tmp)

update @tmptable
set indexs=@index
where pkid=@i

set @str_tmp=stuff (@str_tmp, @index, len(@keys), {fn repeat(' ',len(@keys))})
set @i=@i+1
end 



insert @table
        ( pkid,keys, property,  indexs )
select dense_rank()over(order by indexs) pkid
,keys,property,indexs 
from @tmptable

return

end 
select*from dbo.fn_split_by_property('南塘浜路9009号,南塘浜路9999号')
序号分词词性位置
1南塘浜路cn1
29009num5
3cn9
4,symbol10
5南塘浜路cn11
69999num15
7cn19
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jackie_Mina

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值
>