享受生活,享受学习

导航

SQL拆分字段

拆分字段与合并字段是字符串处理常见的两个问题,下面将针对实例进行分析求解。
问题:將字符串拆分成記錄集
原表Table1(id int identity(1,1),name varchar(100))
id           name
-------------------
1            'a,b,c'
2            'e,f'

希望得到结果:
id    name
-----------
1     'a'
1     'b'
1     'c'
2     'e'
2     'f'

一、首先我们利用最原始的方法进行求解,即通过逐条循环来实现。
create table #a(id int identity(1,1),name varchar(100))
insert #a(name)
select 'a,b,c'
union all select 'd,e,f'
union all select 'g,h'
1.利用游标
DECLARE @id int,@name varchar(100)
create table #b(id int,name varchar(100))
DECLARE a_cursor CURSOR FOR
SELECT id, name
FROM #a
ORDER BY id
 
OPEN a_cursor
 
FETCH NEXT FROM a_cursor
INTO @id, @name

WHILE @@FETCH_STATUS = 0
BEGIN
  while charindex(',',@name)>0
    begin
 insert #b(id,name)
 select @id,left(@name,charindex(',',@name)-1)
 set @name=right(@name,len(@name)-charindex(',',@name))
    end
  insert #b(id,name)
  select @id,@name
  FETCH NEXT FROM a_cursor
  INTO @id, @name
end
CLOSE a_cursor
DEALLOCATE a_cursor

select *from #b
drop table #b
2.利用临时表代替游标
DECLARE @id int,@name varchar(100),@count int
create table #tmp(no int identity(1,1),id int,name varchar(100))
create table #b(id int,name varchar(100))
insert #tmp(id,name)
select * from #a
set @count=@@rowcount
while @count>0
begin
  select @id=id,@name=name from #tmp where no=@count
  while charindex(',',@name)>0
    begin
 insert #b(id,name)
 select @id,left(@name,charindex(',',@name)-1)
 set @name=right(@name,len(@name)-charindex(',',@name))
    end
  insert #b(id,name)
  select @id,@name
  set @count=@count-1
end

select * from #b order by id,name
drop table #b,#tmp
上述两种方法都两次利用了循环,清晰易懂,但代码长而且速度慢。

二、利用replace函数与动态语句结合。
-- test:
create table #a(id int identity(1,1),name varchar(100))
create table #b(id int,name varchar(100))
insert #a(name)
select 'a,b,c'
union all select 'd,e,f'
union all select 'g,h'
go
declare @sql varchar(8000)
set @sql = ''
select @sql = @sql + ' insert into #b select ' + cast(id as varchar) + ',''' + replace(name,',',''' union all select ' + cast(id as varchar) + ',''') + '''' from #a
--print @sql
exec (@sql)
go
select * from #b
go
drop table #a,#b

解析:此段代码很简洁,但使用了比较复杂的动态语句与代替函数的使用技巧,给程序的解读带来一定的困难,而且受字段串长度的限制,
一旦超过8000个字符就不能满足要求。

三、利用substring函数计算出每一段分割符的上下位置,从而达到分离字段的目的。
此方法可以处理较大数据量的要求而且速度较快,但此方法比较巧妙,难以联想到。
create table #a(id int identity(1,1),name varchar(100))

insert #a(name)
select 'a,b,c'
union all select 'd,e,f'
union all select 'g,h'

select a.id,name=substring(a.name,b.id,charindex(',',a.name+',',b.id)-b.id)
from #a a join(
 select top 100 id=a.id+b.id+1
 from(
  select id=0 union all select 1
  union all select id=2 union all select 3
  union all select id=4 union all select 5
  union all select id=6 union all select 7
  union all select id=8 union all select 9
 ) a,(
  select id=0 union all select 10
  union all select id=20 union all select 30
  union all select id=40 union all select 50
  union all select id=60 union all select 70
  union all select id=80 union all select 90
 ) b
)b on substring(','+a.name,b.id,1)=','

drop table #a
综合起来,上述三种方法相比:
第一种通俗易懂,符合一般人的编写思想,但效率不高。
第二种方法语句短小精悍,较率也较高,但由于受字符串长度限制,通用性不好,难以处理大批量的数据。
第三种方法弥补了第二种方法的不足,但同时加进了程序的复杂度,不是很容易想得到,想得通。

四、 Ntext字段的拆分

if   exists   (select   *   from   dbo.sysobjects   where   id   =   object_id(N'[dbo].[f_GetStr]')   and   xtype   in   (N'FN',   N'IF',   N'TF'))  
  drop   function   [dbo].[f_GetStr]  
  GO  
   
  /*--得到字符串列表指定位置的字符  
   
  可以自定义字符串列表的分隔符  
  如果取数位置超出的范围,返回空字符串  
   
  --邹建   2005.04(引用请保留此信息)--*/  
   
  /*--调用示例  
  --测试数据  
  declare   @UserItem   table(UserItemID   int,CateName   varchar(50),OtherDemo   text)  
  insert   @UserItem  
  select   1,'张三','50&黄石市小河镇&0762-2262626'   union   all  
  select   2,'李四','35&广州市&020-2262626'   union   all  
  select   3,'博士','25&青岛&0456-2262626'   union   all  
  select   4,'学士','25&北京&010-2262626'  
   
  --分拆  
  select   UserItemID,CateName  
  ,年龄=dbo.f_GetStr(OtherDemo,1)  
  ,地址=dbo.f_GetStr(OtherDemo,2)  
  ,联系电话=dbo.f_GetStr(OtherDemo,3)  
  from   @UserItem  
   
  /*--结果  
   
  UserItemID     CateName           年龄                 地址                     联系电话  
  -----------   ------------   ----------   -------------   ---------------  
  1                       张三                   50                   黄石市小河镇       0762-2262626  
  2                       李四                   35                   广州市                   020-2262626  
  3                       博士                   25                   青岛                       0456-2262626  
  4                       学士                   25                   北京                       010-2262626  
   
  (所影响的行数为   4   行)  
  --*/  
  --*/  
  CREATE   FUNCTION   f_GetStr(  
  @s   ntext,             --字符串列表  
  @pos   int               --要获取的数据信息段  
  )RETURNS   nvarchar(4000)  
  AS  
  BEGIN  
  IF   @s   IS   NULL   RETURN(NULL)  
  DECLARE   @s1   nvarchar(4000),@i   int,@j   int  
  SELECT   @i=1,@j=PATINDEX('%&%',@s)  
  WHILE   @pos>1   AND   @j>0  
  SELECT   @pos=@pos-1,  
  @j=PATINDEX('%&%',SUBSTRING(@s,@i,4000))  
  ,@i=@i+@j  
  RETURN(SUBSTRING(@s,@i,PATINDEX('%&%',SUBSTRING(@s,@i,4000)+'&')-1))  
  END  
  GO  


posted on 2007-09-28 14:24  徘徊中的海鸟  阅读(2300)  评论(0编辑  收藏  举报