網上已有人實現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畢竟不夠完美,這樣的函數系統提供可以最好,本身實現的話遇到太多瓶頸,好比函數不支持動態語句,不能將查詢結果傳入過程等等。
至於實際應用,將上面這個栗子創建臨時數據表的部分替換成要查詢的真實表列便可,最後結果以下所示: