SQLServer實現split分割字符串到列

網上已有人實現sqlserver的split函數可將字符串分割成行,可是咱們習慣了split返回數組或者列表,所以這裏對其作一些改動,最終實現也許不盡如意,可是也能解決一些問題。web

先貼上某大牛寫的split函數(來自:Split function in SQL Server to break Comma separated strings,注意我這裏將其命名爲splitl):sql

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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#

?
1
SELECT  FROM  dbo.splitl( 'a#b#c#d' , '#' )

然而我但願獲得的結果是:數組

?
1
SELECT  'a'  a, 'b'  b, 'c'  c, 'd'  d

這就要用到sqlserver行轉列的技巧,網上有不少方法能夠參照。下面真正的split「過程」來了:函數

?
1
2
3
4
5
6
7
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 和 UNPIVOTsqlserver

注意這個過程調用了splitl函數,是在其基礎上開發的。咱們再來看看執行結果:spa

?
1
EXEC  dbo.split  'a#b#c#d' , '#'

發現與上面指望的效果徹底一致了!.net

可是這只是針對一行數據作split,若是是查詢結果有多行都要分割怎麼辦呢?code

我沒有找到辦法,由於sqlserver查詢語句中不能嵌套過程,只能調用函數,而函數返回的結果集不能是多行。server

but..世上無難事,只要寫過程:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
-- 刪除結果表
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畢竟不夠完美,這樣的函數系統提供可以最好,本身實現的話遇到太多瓶頸,好比函數不支持動態語句,不能將查詢結果傳入過程等等。

至於實際應用,將上面這個栗子創建臨時數據表的部分替換成要查詢的真實表列便可,最後結果以下所示:

相關文章
相關標籤/搜索