假设我们有下表:
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 数据库。
假设我们有下表:
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 数据库。
这是可以设计的最短且可读的字符串到行拆分器,并且也可以更快。
选择纯 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:与字符串连接相反 - 如何将字符串拆分为多个记录
嗯...首先让我向您介绍亚当·马哈尼奇(Adam Machanic),他教我有关数字表的知识。他还使用这个数字表编写了一个非常快速的拆分函数。
http://dataeducation.com/counting-occurrences-of-a-substring-within-a-string/
实现返回表的拆分函数后,您可以加入它并获得所需的结果。
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.