2012-05-08 30 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. 
相关问题