【SQL】 借助遊標來實現文本的分列與合並
有時我們會遇到需要把表中個別字段拆分成多條數據或是把多條數據合並到一起的情況。一般的編程語言都有函數“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,因為我們不需要那個逗號
代碼與實現效果如下:
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】 借助遊標來實現文本的分列與合並