如何將逗號分隔的字串透過T-SQL回傳成一組DataSet呢。
例如輸入 Sandy,Marry,Dofi 一組字串時,回傳下列三筆資料 。
Sandy
Marry
Dofi
CREATE FUNCTION dbo.ufn_SplitToTable ( @InputString nvarchar(4000) ) RETURNS @tblReturn TABLE (COL1 nvarchar(60)) AS BEGIN DECLARE @CIndex smallint WHILE (@InputString<>'') BEGIN SET @CIndex=CHARINDEX(',',@InputString) IF @CIndex=0 SET @CIndex=LEN(@InputString)+1 --透過substring函數取得第一個字串,並輸入資料表變數中 INSERT INTO @tblReturn (COL1) VALUES (SUBSTRING(@InputString,1,@CIndex-1)) IF @CIndex=LEN(@InputString)+1 BREAK SET @InputString=SUBSTRING(@InputString,@CIndex+1,LEN(@InputString)-@CIndex) END RETURN END GO
最後使用下列語法測試下
Select * From dbo.ufn_SplitToTable('Sandy,Lisa,Dofi')
執行結果如下:
以我的設計經驗來說,我通常都會將這個自訂函數拿來模擬多重選單的功能。以下列這個預存程序為例,假設我要查詢客戶編號為 ALFKI 與 ANATR 這兩筆資料。我的T-SQL語法應該是
SELECT * FROM Northwind.dbo.customers
WHERE CUSTOMERID IN (‘ALFKI’ , ’ANATR’)
但如果改成預存程序呢?WHERE子句應該如何處理呢?其實只要將上述的自訂函數拿來稍微應用一下即可。參考下列指令碼:
Create proc sp_QueryCustomers @STR nvarchar(max) AS SELECT CustomerID,CompanyName,ContactName,Phone FROM dbo.Customers WHERE CustomerID IN (select * from dbo.ufn_SplitToTable(@STR)) GO --使用下列語法執行 DECLARE @STR NVARCHAR(MAX) SET @STR='ALFKI,ANATR' EXEC sp_QueryCustomers @STR
應該不難發現,ufn_SplitToTable自訂函數神奇地將字串切割成資料集了。因此在CustomerID in(參數)這裡可以將符合的字串挑回來。執行結果如下圖所示。
最後,在示範一個稍微複雜的字串處理。這次需要處理的字串是"22,科技類;26,旅遊類;27,進修類",然後我們希望回傳的資料集如下:
欄位1 欄位2
-------------------
22 科技類
26 旅遊類
27 進修類
CREATE FUNCTION [dbo].[fnSplitList](@List Nvarchar(MAX)) --取出的字串長相需是 ======= 22,科技類;26,旅遊類;27,進修類 RETURNS @ReturnTable TABLE(Column_code nvarchar(30),Column_Desc nvarchar(60)) AS BEGIN DECLARE @Index int,@IndexCol int DECLARE @NewText nvarchar(max),@ColumnCode nvarchar(max),@ColumnDesc nvarchar(max) IF @List = null RETURN SET @Index = CHARINDEX(';', @List) WHILE NOT(@Index = 0) BEGIN --取出 22,科技類 SET @NewText = RTRIM(LTRIM(LEFT(@List, @Index - 1))) --切割成 <22> 和 <科技類> 兩部分 SET @IndexCol=CHARINDEX(',',@NewText) SET @ColumnCode = RTRIM(LTRIM(LEFT(@NewText, @IndexCol - 1))) SET @ColumnDesc = RIGHT(@NewText,LEN(@NewText)-@IndexCol) --插入紀錄 INSERT INTO @ReturnTable(Column_Code,Column_Desc) VALUES(@ColumnCode,@ColumnDesc) --取出前一段字串後剩餘的字串 SET @List = RIGHT(@List, LEN(@List) - @Index) SET @Index = CHARINDEX(';', @List) END --切割成 <26> 和 <旅遊類> 兩部分 SET @IndexCol=CHARINDEX(',',@List) SET @ColumnCode = RTRIM(LTRIM(LEFT(@List, @IndexCol - 1))) SET @ColumnDesc = RIGHT(@List,LEN(@List)-@IndexCol) --插入紀錄 INSERT INTO @ReturnTable(Column_Code,Column_Desc) VALUES(@ColumnCode,@ColumnDesc) RETURN END
最後,使用下列語法測試回傳結果
DECLARE @STR NVARCHAR(MAX) SET @STR=N'22,科技類;26,旅遊類;27,進修類' SELECT * FROM [dbo].[fnSplitList](@STR)
成功地將一組字串組成虛擬資料表,如下圖所示
以上程式碼適用於SQL Server 2000以上版本。SQL 2005 則可參考下列程式碼達到 split 的效果。
create function Split ( @StringToSplit varchar(2048), @Separator varchar(128)) returns table as return with indices as ( select 0 S, 1 E union all select E, charindex(@Separator, @StringToSplit, E) + len(@Separator) from indices where E > S ) select substring(@StringToSplit,S, case when E > len(@Separator) then e-s-len(@Separator) else len(@StringToSplit) - s + 1 end) String ,S StartIndex from indices where S >0 GO --simple split comma delimited string example select * from Split('aaa,b,cccc,dd,e,ffff,g' , ',') --when the separator is not found, no problems either select * from Split('abcdefgh' , ',') --empty values are returned too, the list would not be complete without them (for example when handling input file lines) select * from Split(',,a,,b,c,,,d' , ',') --skipping the empty values would simply be using a criterium select * from Split(',,a,,b,c,,,d' , ',') where len(String) > 0 --of course, splitting with other (and longer) strings than a comma is supported too select * from Split('aaa->bb->cccc->d' , '->') where len(String) > 0 --just a final example with some query options combined select rtrim(ltrim(String)) from Split('from abc from abb from cdd from addd' , 'from') where ltrim(String) like 'a%'
留言列表