SQLServer實現split分割字符串到列


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

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

?
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

其原理還是比較簡單的,一看便知。調用該函數返回的結果是:

?
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 和 UNPIVOT

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

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

發現與上面期望的效果完全一致了!

但是這只是針對一行數據做split,如果是查詢結果有多行都要分割怎么辦呢?

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

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畢竟不夠完美,這樣的函數系統提供能夠最好,自己實現的話遇到太多瓶頸,比如函數不支持動態語句,不能將查詢結果傳入過程等等。

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM