網上已有人實現sqlserver的split函數可將字符串分割成行,可是咱們習慣了split返回數組或者列表,所以這裏對其作一些改動,最終實現也許不盡如意,可是也能解決一些問題。 web
先貼上某大牛寫的split函數(來自:Split function in SQL Server to break Comma separated strings,注意我這裏將其命名爲splitl): sql
ALTER FUNCTION dbo.splitl ( @String VARCHAR(MAX), @Delimiter VARCHAR(MAX) ) RETURNS @temptable TABLE (items VARCHAR(MAX)) AS BEGIN DECLARE @idx INT=1 DECLARE @slice VARCHAR(MAX) IF LEN(@String) < 1 OR LEN(ISNULL(@String,'')) = 0 RETURN WHILE @idx != 0 BEGIN SET @idx = CHARINDEX(@Delimiter,@String) IF @idx != 0 SET @slice = LEFT(@String,@idx - 1) ELSE SET @slice = @String IF LEN(@slice) > 0 INSERT INTO @temptable(items) VALUES(@slice) SET @String = RIGHT (@String, LEN(@String) - @idx) IF LEN(@String) = 0 BREAK END RETURN END
其原理仍是比較簡單的,一看便知。調用該函數返回的結果是: c#
SELECT * FROM dbo.splitl('a#b#c#d','#')
然而我但願獲得的結果是: 數組
SELECT 'a' a,'b' b,'c' c,'d' d
這就要用到sqlserver行轉列的技巧,網上有不少方法能夠參照。下面真正的split「過程」來了: 函數
ALTER PROC [dbo].[split] @strs VARCHAR(MAX),@delimiter VARCHAR(MAX) AS SELECT items,id=IDENTITY(INT,1,1) INTO #ccc FROM dbo.splitl(@strs,@delimiter) DECLARE @str VARCHAR(MAX)='',@SQL VARCHAR(MAX)='' SELECT @str = @str + ',' + '[' + CONVERT(VARCHAR(MAX),id) + ']' FROM #ccc SET @SQL = 'SELECT * FROM #ccc PIVOT(MAX(items) FOR id IN(' + SUBSTRING(@str,2,LEN(@str)) + ')) b' EXEC (@SQL) DROP TABLE #ccc
該過程當中使用了pivot語法,參見:使用 PIVOT 和 UNPIVOT sqlserver
注意這個過程調用了splitl函數,是在其基礎上開發的。咱們再來看看執行結果: spa
EXEC dbo.split 'a#b#c#d','#'
發現與上面指望的效果徹底一致了! code
可是這只是針對一行數據作split,若是是查詢結果有多行都要分割怎麼辦呢? server
我沒有找到辦法,由於sqlserver查詢語句中不能嵌套過程,只能調用函數,而函數返回的結果集不能是多行。 開發
but..世上無難事,只要寫過程:
-- 刪除結果表 IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id=object_id(N'test_result') AND OBJECTPROPERTY(id, N'IsUserTable')=1) DROP TABLE test_result -- 創建數據表 CREATE TABLE #tmp ( id INT NOT NULL IDENTITY(0,1), str VARCHAR(MAX) ) INSERT INTO #tmp SELECT 'a#b#c#d' UNION SELECT 'f#g#h' -- 生成結果表 DECLARE @maxc INT=(SELECT MAX(LEN(str)-LEN(REPLACE(str,'#','')))+1 FROM #tmp) DECLARE @sql0 VARCHAR(MAX)='CREATE TABLE test_result (' DECLARE @x INT=0 WHILE @x<@maxc BEGIN SET @sql0 = @sql0 + 'a' + CONVERT(VARCHAR(MAX),@x) + ' VARCHAR(MAX),' SET @x=@x+1 END SET @sql0 = SUBSTRING(@sql0,0,LEN(@sql0)) + ')' EXEC (@sql0) -- 遍歷數據表 DECLARE @i INT=0 WHILE @i<(SELECT COUNT(1) FROM #tmp) BEGIN DECLARE @strs VARCHAR(MAX)=(SELECT str FROM #tmp WHERE id=@i) DECLARE @cols INT=(SELECT LEN(@strs)-LEN(REPLACE(@strs,'#','')))+1 DECLARE @y INT=0 DECLARE @sql1 VARCHAR(MAX)='INSERT INTO test_result(' WHILE @y<@cols BEGIN SET @sql1 = @sql1 + 'a' + CONVERT(VARCHAR(MAX),@y) + ',' SET @y=@y+1 END -- -- 分割字符串 SET @sql1 = SUBSTRING(@sql1,0,LEN(@sql1)) + ') EXEC split "' + @strs + '","#"' EXEC (@sql1) SET @i=@i+1 END SELECT * FROM test_result
暫時就到此爲止八~sqlserver畢竟不夠完美,這樣的函數系統提供可以最好,本身實現的話遇到太多瓶頸,好比函數不支持動態語句,不能將查詢結果傳入過程等等。
至於實際應用,將上面這個栗子創建臨時數據表的部分替換成要查詢的真實表列便可,最後結果以下所示: