1. 程式人生 > >【SQL】 借助遊標來實現文本的分列與合並

【SQL】 借助遊標來實現文本的分列與合並

ack 沒有 成了 數據類型 close server ins http nodes

有時我們會遇到需要把表中個別字段拆分成多條數據或是把多條數據合並到一起的情況。一般的編程語言都有函數“split”和“join”來實現,而SQL中既沒有這些函數也沒有類似數組和列表這類方便保存成組數據的數據類型,一些對於字符串的處理功能實現起來比較麻煩。直到SQL Server 2016才新增了string_split函數,專門用來拆分字符串但在此之前的版本,我們只能通過其他方式來實現這些功能。


一、文本分列的實現


現有測試數據表如下圖,“學科”和“成績”都是多條數據通過逗號連接的。如果想把他們拆分出多條數據,讓每門學科和成績都對應一條記錄,自然需要把“學科”與“成績”兩列數據中按照逗號作為分隔符進行拆分。

技術分享圖片

  • 利用xml對字符串進行拆分

在網上搜索的比較簡便和常用的方法是利用xml對字符串進行拆分,在此不再細說,代碼如下:

1 declare @x xml,@str nvarchar(200)
2 set @str=aaa,bbb,ccc
3 SELECT @x = CONVERT(xml,
4             <v> + REPLACE(@str, ,, </v><v>) + </v>)
5 SELECT N.v.value(., varchar(100))
6 FROM @x.nodes(/v) N(v)

  • 利用遊標配合函數substring
    來實現表中文本的分列

思路:通過建立遊標逐條獲取表內信息,並對此進行加工,使用函數substring分段讀取“學科”和“成績”,將結果插入到另外一個新建立的表“test_split”中。

實現方法:建立一個新表"test_split",通過建立遊標逐條獲取表“test”內信息,針對從遊標中獲取的每一條記錄,進行如下的操作。因為“學科”與“成績”都是通過逗號進行連接的一組數據,所以分別用函數charindex定位到“學科”與“成績”中逗號第一次出現的位置,隨後使用函數substring截取從開頭也就是索引號1的位置到逗號第一次出現位置通過charindex獲取到的索引號減去1,因為我們不需要那個逗號

)的部分,將獲取到的新字符串作為新表“test_split”中的“學科”與“成績”連同“姓名”一起插入到新表“test_split”中。再將原字符串從開頭到第一個逗號的部分刪除作為新的字符串循環重復前面的操作,直到字符串中沒有逗號為止。這樣我們就把原表“test”中原本的一條數據根據“學科”與“成績”的內容拆分成了多條數據。

代碼與實現效果如下:

 1 if exists(select name from sys.tables where name=test_split)
 2 drop table test_split
 3 CREATE TABLE  test_split(姓名 nvarchar(50),學科 nvarchar(200),成績 nvarchar(200))
 4 go
 5 
 6 DECLARE MyCursor CURSOR
 7 for select * FROM dbo.test
 8 open MyCursor
 9 DECLARE @姓名 nvarchar(50),@學科 nvarchar(200),@成績 nvarchar(200),@學科Temp nvarchar(200),@成績Temp nvarchar(200)
10 declare @getindex1 int,@getindex2 int
11 FETCH NEXT FROM MyCursor INTO @姓名,@學科,@成績 
12 WHILE @@FETCH_STATUS =0 
13 BEGIN 
14     set  @getindex1=charindex(,,@學科)
15     set  @getindex2=charindex(,,@成績)
16     while(@getindex1<>0)   
17     begin
18         set @學科Temp=substring(@學科,1,@getIndex1-1)
19         set @成績Temp=substring(@成績,1,@getIndex2-1)
20         insert into test_split values (@姓名,@學科Temp,@成績Temp)
21         set @學科=stuff(@學科,1,@getindex1,‘‘)
22         set @成績=stuff(@成績,1,@getindex2,‘‘)
23         set    @getindex1=charindex(,,@學科) 
24         set    @getindex2=charindex(,,@成績)  
25     end
26     insert into test_split values (@姓名,@學科,@成績)
27     FETCH NEXT FROM MyCursor INTO @姓名,@學科,@成績
28 END 
29 
30 CLOSE MyCursor 
31 DEALLOCATE MyCursor 

技術分享圖片

原表“test"中的四條記錄給拆分成了對應的9條記錄。

二、文本合並的實現


與文本的分列對應的,可以采用同樣的方法來實現文本的合並。下面我們來嘗試將之前生成的表“test_split”重新合並成新表“test_join”,實現字段“學科”與“成績”的合並。

思路:通過建立遊標逐條獲取表內信息,設置一個變量“@姓名Temp”,將每次獲取到的數據中的“姓名”與該變量進行對比,相同的就將其中的“學科”與“成績”進行合並。

實現方法:建立一個新表“test_join”,同時建立一個遊標來讀取"test_split"中的數據(表中數據需要按照“姓名”進行排序),將每次遊標獲取到的數據中的“姓名”與“@姓名Temp”對比,如果相同就把“學科”與“成績”累加到變量“@學科”與“@成績”中,並使用逗號分隔;如果不同,就將變量值插入到新建立的表“test_join”中,同時使用新獲取到的“姓名”、“學科“、”成績“更新變量”@姓名”、“@學科”、“@成績”。

代碼及實現效果如下:

 1 if exists(select name from sys.tables where name=test_join)
 2 drop table test_join
 3 CREATE TABLE  test_join(姓名 nvarchar(50),學科 nvarchar(200),成績 nvarchar(200));
 4 go
 5 
 6 DECLARE MyCursor CURSOR
 7 for select 姓名,學科,成績 FROM dbo.test_split
 8 open MyCursor
 9 DECLARE @姓名 nvarchar(50),@學科 nvarchar(200),@成績 nvarchar(200)
10 declare @姓名Temp nvarchar(50),@學科Temp nvarchar(200),@成績Temp nvarchar(200)
11 set @姓名Temp = ‘‘
12 FETCH NEXT FROM MyCursor INTO @姓名,@學科,@成績 
13 
14 WHILE @@FETCH_STATUS =0 
15 BEGIN 
16     if @姓名 <> @姓名Temp
17         begin
18             insert into test_join values (@姓名Temp,@學科Temp,@成績Temp)
19             set @姓名Temp=@姓名
20             set @學科Temp=@學科
21             set @成績Temp=@成績
22         end
23     else
24         begin
25             set @學科Temp=@學科Temp+,+@學科
26             set @成績Temp=@成績Temp+,+@成績
27         end
28     FETCH NEXT FROM MyCursor INTO @姓名,@學科,@成績
29 END 
30 if @姓名 = @姓名Temp
31     insert into test_join values (@姓名Temp,@學科Temp,@成績Temp)
32 
33 CLOSE MyCursor 
34 DEALLOCATE MyCursor 

技術分享圖片

從最終的運行結果可以看到,這裏還存在一個小問題,就是因為一開始把變量“@姓名”初始化成空值,所以在新表"test_join"中會產生一條無用記錄,但為了代碼中循環體的條件判斷,沒有太好的消除辦法。

【SQL】 借助遊標來實現文本的分列與合並