2012-05-08 72 views
2

假設我們有如下表:如何在一列分割字符串值,並返回結果表

id name member 
1 jacky a;b;c 
2 jason e 
3 kate i;j;k 
4 alex null 

現在我想用SQL或T-SQL來如下表返回:

1 jacky a 
1 jacky b 
1 jacky c 
2 jason e 
3 kate i 
...... 

如何做到這一點? 我正在使用MSSQL,MYSQL和Oracle數據庫。

+0

Unpivot是你在MSSQL和oracle尋找的命令 – xQbert

+0

@xQbert - 我不認爲unpivot具有拆分功能,但我可能是錯的... –

回答

3

這是最短可讀字符串到行分離器一個可以設計,並且可能是更快了。

選擇純CTE而不是功能的使用案例,例如當您不允許在數據庫上創建函數時:-)

通過函數創建行生成器(可以通過循環或通過CTE實現)仍然需要使用橫向連接(DB2和Sybase擁有此功能功能,使用LATERAL關鍵字;在SQL Server中,這與CROSS APPLY和OUTER APPLY類似)最終將由函數生成的拆分行連接到主表。

純粹的CTE方法可能比函數方法更快。速度指標在於分析,雖然,只是檢查的這相對於其他解決方案的執行計劃,如果這確實是快:

with Pieces(theId, pn, start, stop) AS 
(
     SELECT id, 1, 1, charindex(';', member) 
     from tbl 

     UNION ALL 

     SELECT id, pn + 1, stop + 1, charindex(';', member, stop + 1) 
     from tbl 
     join pieces on pieces.theId = tbl.id 
     WHERE stop > 0 
) 
select 

     t.id, t.name, 

     word = 
     substring(t.member, p.start,    
      case WHEN stop > 0 THEN p.stop - p.start 
      ELSE 512 
      END) 

from tbl t 
join pieces p on p.theId = t.id 
order by t.id, p.pn 

輸出:

ID NAME WORD 
1 jacky a 
1 jacky b 
1 jacky c 
2 jason e 
3 kate i 
3 kate j 
3 kate k 
4 alex (null) 

基本邏輯這裏來源:T-SQL: Opposite to string concatenation - how to split string into multiple records

現場測試:http://www.sqlfiddle.com/#!3/2355d/1

2

那麼......讓我先向你介紹一下Adam Machanic,他教會了我一個Numbers表。他還使用這個Numbers表格編寫了一個非常快速的分割函數。

http://sqlblog.com/blogs/adam_machanic/archive/2006/07/12/splitting-a-string-of-unlimited-length.aspx

您實現返回一個表拆分功能後,您就可以加入反對它,並得到你想要的結果。

+0

我瞭解dbo.SplitString('東西,別的東西等等等等等等',',')函數。但是如何用這個函數得到結果表。 – DerekY

+0

@ user838204如果您使用了某個功能,則應使用CROSS APPLY或OUTER APPLY。這種格式:'SELECT * FROM tbl OUTER APPLY(select * FROM dbo.splitter(tbl.member))' –

1
IF OBJECT_ID('dbo.Users') IS NOT NULL 
    DROP TABLE dbo.Users; 

CREATE TABLE dbo.Users 
(
    id INT IDENTITY NOT NULL PRIMARY KEY, 
    name VARCHAR(50) NOT NULL, 
    member VARCHAR(1000) 
) 
GO 

INSERT INTO dbo.Users(name, member) VALUES 
    ('jacky', 'a;b;c'), 
    ('jason', 'e'), 
    ('kate', 'i;j;k'), 
    ('alex', NULL); 
GO 

DECLARE @spliter CHAR(1) = ';'; 
WITH Base AS 
(
    SELECT 1 AS n 
    UNION ALL 
    SELECT n + 1 
    FROM Base 
    WHERE n < CEILING(SQRT(1000)) --generate numbers from 1 to 1000, you may change it to a larger value depending on the member column's length. 
) 
, Nums AS --Numbers Common Table Expression, if your database version doesn't support it, just create a physical table. 
(
    SELECT ROW_NUMBER() OVER(ORDER BY (SELECT 0)) AS n 
    FROM Base AS B1 CROSS JOIN Base AS B2 
) 
SELECT id, 
     SUBSTRING(member, n, CHARINDEX(@spliter, member + @spliter, n) - n) AS element 
FROM dbo.Users 
    JOIN Nums 
    ON n <= DATALENGTH(member) + 1 
    AND SUBSTRING(@spliter + member, n, 1) = @spliter 
ORDER BY id 
OPTION (MAXRECURSION 0); --Nums CTE is generated recursively, we don't want to limit recursion count. 
相關問題