将逗号分隔的字符串转换为单独的行
Posted
技术标签:
【中文标题】将逗号分隔的字符串转换为单独的行【英文标题】:Turning a Comma Separated string into individual rows 【发布时间】:2011-07-26 11:55:09 【问题描述】:我有一个这样的 SQL 表:
| SomeID | OtherID | Data
+----------------+-------------+-------------------
| abcdef-..... | cdef123-... | 18,20,22
| abcdef-..... | 4554a24-... | 17,19
| 987654-..... | 12324a2-... | 13,19,20
是否有一个查询,我可以在其中执行类似 SELECT OtherID, SplitData WHERE SomeID = 'abcdef-.......'
的查询,该查询返回单个行,如下所示:
| OtherID | SplitData
+-------------+-------------------
| cdef123-... | 18
| cdef123-... | 20
| cdef123-... | 22
| 4554a24-... | 17
| 4554a24-... | 19
基本上将逗号处的数据拆分为单独的行?
我知道将comma-separated
字符串存储到关系数据库中听起来很愚蠢,但消费者应用程序中的正常用例确实很有帮助。
我不想在应用程序中进行拆分,因为我需要分页,所以我想在重构整个应用程序之前探索选项。
它是SQL Server 2008
(非 R2)。
【问题讨论】:
另见:periscopedata.com/blog/… 【参考方案1】:您可以使用 SQL Server 中出色的递归函数:
示例表:
CREATE TABLE Testdata
(
SomeID INT,
OtherID INT,
String VARCHAR(MAX)
);
INSERT Testdata SELECT 1, 9, '18,20,22';
INSERT Testdata SELECT 2, 8, '17,19';
INSERT Testdata SELECT 3, 7, '13,19,20';
INSERT Testdata SELECT 4, 6, '';
INSERT Testdata SELECT 9, 11, '1,2,3,4';
查询
WITH tmp(SomeID, OtherID, DataItem, String) AS
(
SELECT
SomeID,
OtherID,
LEFT(String, CHARINDEX(',', String + ',') - 1),
STUFF(String, 1, CHARINDEX(',', String + ','), '')
FROM Testdata
UNION all
SELECT
SomeID,
OtherID,
LEFT(String, CHARINDEX(',', String + ',') - 1),
STUFF(String, 1, CHARINDEX(',', String + ','), '')
FROM tmp
WHERE
String > ''
)
SELECT
SomeID,
OtherID,
DataItem
FROM tmp
ORDER BY SomeID;
-- OPTION (maxrecursion 0)
-- normally recursion is limited to 100. If you know you have very long
-- strings, uncomment the option
输出
SomeID | OtherID | DataItem
--------+---------+----------
1 | 9 | 18
1 | 9 | 20
1 | 9 | 22
2 | 8 | 17
2 | 8 | 19
3 | 7 | 13
3 | 7 | 19
3 | 7 | 20
4 | 6 |
9 | 11 | 1
9 | 11 | 2
9 | 11 | 3
9 | 11 | 4
【讨论】:
如果将列Data
的数据类型从varchar(max)
更改为varchar(4000)
,则代码不起作用,例如create table Testdata(SomeID int, OtherID int, Data varchar(4000))
?
@NickW 这可能是因为 UNION ALL 之前和之后的部分从 LEFT 函数返回不同的类型。就我个人而言,我不明白为什么一旦达到 4000 就不会跳到 MAX...
对于一大组值,这可能会超出 CTE 的递归限制。
@dsz 那是你使用OPTION (maxrecursion 0)
LEFT 函数可能需要 CAST 才能工作....例如 LEFT(CAST(Data AS VARCHAR(MAX))....【参考方案2】:
;WITH tmp(SomeID, OtherID, DataItem, Data) as (
SELECT SomeID, OtherID, LEFT(Data, CHARINDEX(',',Data+',')-1),
STUFF(Data, 1, CHARINDEX(',',Data+','), '')
FROM Testdata
WHERE Data > ''
)
SELECT SomeID, OtherID, Data
FROM tmp
ORDER BY SomeID
只对上面的查询进行微小的修改...
【讨论】:
您能否简要解释一下这是对已接受答案中版本的改进? 没有联合所有...更少的代码。既然是用union all而不是union,不应该是性能上的区别吗? 这并没有返回它应该有的所有行。我不确定数据需要全部联合,但您的解决方案返回的行数与原始表相同。 (这里的问题是递归部分被省略了……) 不给我预期的输出只在单独的行中给出第一条记录【参考方案3】:检查一下
SELECT A.OtherID,
Split.a.value('.', 'VARCHAR(100)') AS Data
FROM
(
SELECT OtherID,
CAST ('<M>' + REPLACE(Data, ',', '</M><M>') + '</M>' AS XML) AS Data
FROM Table1
) AS A CROSS APPLY Data.nodes ('/M') AS Split(a);
【讨论】:
使用这种方法时,您必须确保没有任何值包含非法 XML 的内容 这很棒。请问,如果我希望新列仅显示拆分字符串中的第一个字符,我将如何重写? 我必须告诉你,这种方法是“lovingl”(感受到爱了吗?),称为“XML 拆分器方法”,几乎与 While 循环或递归 CTE 一样慢。我强烈建议您始终避免使用它。请改用 DelimitedSplit8K。除了 2016 年的 Split_String() 函数或编写良好的 CLR 之外,它把所有东西都吹走了。【参考方案4】:DECLARE @id_list VARCHAR(MAX) = '1234,23,56,576,1231,567,122,87876,57553,1216';
DECLARE @table TABLE ( id VARCHAR(50) );
DECLARE @x INT = 0;
DECLARE @firstcomma INT = 0;
DECLARE @nextcomma INT = 0;
SET @x = LEN(@id_list) - LEN(REPLACE(@id_list, ',', '')) + 1; -- number of ids in id_list
WHILE @x > 0
BEGIN
SET @nextcomma = CASE WHEN CHARINDEX(',', @id_list, @firstcomma + 1) = 0
THEN LEN(@id_list) + 1
ELSE CHARINDEX(',', @id_list, @firstcomma + 1)
END;
INSERT INTO @table
VALUES ( SUBSTRING(@id_list, @firstcomma + 1, (@nextcomma - @firstcomma) - 1) );
SET @firstcomma = CHARINDEX(',', @id_list, @firstcomma + 1);
SET @x = @x - 1;
END;
SELECT *
FROM @table;
【讨论】:
这是少数几种适用于 Azure SQL 数据仓库中有限 SQL 支持的方法之一。【参考方案5】:截至 2016 年 2 月 - 请参阅 TALLY 表示例 - 从 2014 年 2 月开始,很可能会超过我的以下 TVF。保留下面的原始帖子以供后代使用:
在上面的例子中,我喜欢的重复代码太多了。而且我不喜欢 CTE 和 XML 的性能。此外,显式的Id
以便特定于订单的消费者可以指定ORDER BY
子句。
CREATE FUNCTION dbo.Split
(
@Line nvarchar(MAX),
@SplitOn nvarchar(5) = ','
)
RETURNS @RtnValue table
(
Id INT NOT NULL IDENTITY(1,1) PRIMARY KEY CLUSTERED,
Data nvarchar(100) NOT NULL
)
AS
BEGIN
IF @Line IS NULL RETURN;
DECLARE @split_on_len INT = LEN(@SplitOn);
DECLARE @start_at INT = 1;
DECLARE @end_at INT;
DECLARE @data_len INT;
WHILE 1=1
BEGIN
SET @end_at = CHARINDEX(@SplitOn,@Line,@start_at);
SET @data_len = CASE @end_at WHEN 0 THEN LEN(@Line) ELSE @end_at-@start_at END;
INSERT INTO @RtnValue (data) VALUES( SUBSTRING(@Line,@start_at,@data_len) );
IF @end_at = 0 BREAK;
SET @start_at = @end_at + @split_on_len;
END;
RETURN;
END;
【讨论】:
【参考方案6】:select t.OtherID,x.Kod
from testData t
cross apply (select Code from dbo.Split(t.Data,',') ) x
【讨论】:
完全符合我的要求,并且比许多其他示例更易于阅读(前提是数据库中已经有一个用于分隔字符串拆分的函数)。作为以前不熟悉CROSS APPLY
的人,这有点用处!
我无法理解这部分(从 dbo.Split(t.Data,',') 中选择代码)? dbo.Split 是一个存在的表,并且 Code 是拆分表中的列?我在此页面的任何地方都找不到这些表或值的列表?
我的工作代码是:select t.OtherID, x.* from testData t cross apply (select item as Data from dbo.Split(t.Data,',') ) x
【参考方案7】:
在使用这种方法时,您必须确保您的任何值都不包含非法 XML 的内容 - user1151923
我总是使用 XML 方法。确保您使用有效的 XML。我有两个函数可以在有效的 XML 和文本之间进行转换。 (我倾向于去掉回车,因为我通常不需要它们。
CREATE FUNCTION dbo.udf_ConvertTextToXML (@Text varchar(MAX))
RETURNS varchar(MAX)
AS
BEGIN
SET @Text = REPLACE(@Text,CHAR(10),'');
SET @Text = REPLACE(@Text,CHAR(13),'');
SET @Text = REPLACE(@Text,'<','<');
SET @Text = REPLACE(@Text,'&','&');
SET @Text = REPLACE(@Text,'>','>');
SET @Text = REPLACE(@Text,'''',''');
SET @Text = REPLACE(@Text,'"','"');
RETURN @Text;
END;
CREATE FUNCTION dbo.udf_ConvertTextFromXML (@Text VARCHAR(MAX))
RETURNS VARCHAR(max)
AS
BEGIN
SET @Text = REPLACE(@Text,'<','<');
SET @Text = REPLACE(@Text,'&','&');
SET @Text = REPLACE(@Text,'>','>');
SET @Text = REPLACE(@Text,''','''');
SET @Text = REPLACE(@Text,'"','"');
RETURN @Text;
END;
【讨论】:
那里的代码有一个小问题。它会将 ' 不需要这样的功能...只要使用隐含的能力。试试这个:SELECT (SELECT '<&> blah' + CHAR(13)+CHAR(10) + 'next line' FOR XML PATH(''))
【参考方案8】:
SQL Server 2016 终于结束了等待。他们引入了拆分字符串功能,STRING_SPLIT
:
select OtherID, cs.Value --SplitData
from yourtable
cross apply STRING_SPLIT (Data, ',') cs
所有其他拆分字符串的方法,如 XML、Tally 表、while 循环等,都被这个 STRING_SPLIT
函数所震撼。
这里有一篇很棒的文章,有性能比较:Performance Surprises and Assumptions: STRING_SPLIT。
对于旧版本,使用 tally table 这是一个拆分字符串函数(最好的方法)
CREATE FUNCTION [dbo].[DelimitedSplit8K]
(@pString VARCHAR(8000), @pDelimiter CHAR(1))
RETURNS TABLE WITH SCHEMABINDING AS
RETURN
--===== "Inline" CTE Driven "Tally Table" produces values from 0 up to 10,000...
-- enough to cover NVARCHAR(4000)
WITH E1(N) AS (
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
), --10E+1 or 10 rows
E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows
E4(N) AS (SELECT 1 FROM E2 a, E2 b), --10E+4 or 10,000 rows max
cteTally(N) AS (--==== This provides the "base" CTE and limits the number of rows right up front
-- for both a performance gain and prevention of accidental "overruns"
SELECT TOP (ISNULL(DATALENGTH(@pString),0)) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
),
cteStart(N1) AS (--==== This returns N+1 (starting position of each "element" just once for each delimiter)
SELECT 1 UNION ALL
SELECT t.N+1 FROM cteTally t WHERE SUBSTRING(@pString,t.N,1) = @pDelimiter
),
cteLen(N1,L1) AS(--==== Return start and length (for use in substring)
SELECT s.N1,
ISNULL(NULLIF(CHARINDEX(@pDelimiter,@pString,s.N1),0)-s.N1,8000)
FROM cteStart s
)
--===== Do the actual split. The ISNULL/NULLIF combo handles the length for the final element when no delimiter is found.
SELECT ItemNumber = ROW_NUMBER() OVER(ORDER BY l.N1),
Item = SUBSTRING(@pString, l.N1, l.L1)
FROM cteLen l
;
转自Tally OH! An Improved SQL 8K “CSV Splitter” Function
【讨论】:
如果只有服务器在 SQL Server 2016 上,我会使用 STRING_SPLIT!顺便说一句,根据您链接到的页面,它输出的字段名称是value
,而不是SplitData
。
接受的答案有效,但考虑到现在是 2021 年,这是现在应该优先考虑的答案。谢谢你 - SPLIT_STRING 正是我想要的。【参考方案9】:
很高兴看到它已在 2016 版本中解决,但对于所有未解决的问题,这里有上述方法的两个通用和简化版本。
XML 方法更短,但当然需要字符串以允许 xml 技巧(没有“坏”字符。)
XML 方法:
create function dbo.splitString(@input Varchar(max), @Splitter VarChar(99)) returns table as
Return
SELECT Split.a.value('.', 'VARCHAR(max)') AS Data FROM
( SELECT CAST ('<M>' + REPLACE(@input, @Splitter, '</M><M>') + '</M>' AS XML) AS Data
) AS A CROSS APPLY Data.nodes ('/M') AS Split(a);
递归方法:
create function dbo.splitString(@input Varchar(max), @Splitter Varchar(99)) returns table as
Return
with tmp (DataItem, ix) as
( select @input , CHARINDEX('',@Input) --Recu. start, ignored val to get the types right
union all
select Substring(@input, ix+1,ix2-ix-1), ix2
from (Select *, CHARINDEX(@Splitter,@Input+@Splitter,ix+1) ix2 from tmp) x where ix2<>0
) select DataItem from tmp where ix<>0
实际作用
Create table TEST_X (A int, CSV Varchar(100));
Insert into test_x select 1, 'A,B';
Insert into test_x select 2, 'C,D';
Select A,data from TEST_X x cross apply dbo.splitString(x.CSV,',') Y;
Drop table TEST_X
XML-METHOD 2:Unicode 友好 ?(由 Max Hodges 提供)
create function dbo.splitString(@input nVarchar(max), @Splitter nVarchar(99)) returns table as
Return
SELECT Split.a.value('.', 'NVARCHAR(max)') AS Data FROM
( SELECT CAST ('<M>' + REPLACE(@input, @Splitter, '</M><M>') + '</M>' AS XML) AS Data
) AS A CROSS APPLY Data.nodes ('/M') AS Split(a);
【讨论】:
这看起来很明显,但是你如何使用这两个函数呢?特别是,您能展示如何在 OP 的用例中使用它吗? 这里是一个简单的例子: 创建表 TEST_X (A int, CSV Varchar(100));插入 test_x 选择 1, 'A,B';插入 test_x 选择 2, 'C,D'; Select A,data from TEST_X x cross apply dbo.splitString(x.CSV,',') Y;删除表 TEST_X 这正是我所需要的!谢谢。【参考方案10】:以下适用于 sql server 2008
select *, ROW_NUMBER() OVER(order by items) as row#
from
( select 134 myColumn1, 34 myColumn2, 'd,c,k,e,f,g,h,a' comaSeperatedColumn) myTable
cross apply
SPLIT (rtrim(comaSeperatedColumn), ',') splitedTable -- gives 'items' column
将获得所有带有原始表列的笛卡尔积加上拆分表的“项目”。
【讨论】:
【参考方案11】:功能
CREATE FUNCTION dbo.SplitToRows (@column varchar(100), @separator varchar(10))
RETURNS @rtnTable TABLE
(
ID int identity(1,1),
ColumnA varchar(max)
)
AS
BEGIN
DECLARE @position int = 0;
DECLARE @endAt int = 0;
DECLARE @tempString varchar(100);
set @column = ltrim(rtrim(@column));
WHILE @position<=len(@column)
BEGIN
set @endAt = CHARINDEX(@separator,@column,@position);
if(@endAt=0)
begin
Insert into @rtnTable(ColumnA) Select substring(@column,@position,len(@column)-@position);
break;
end;
set @tempString = substring(ltrim(rtrim(@column)),@position,@endAt-@position);
Insert into @rtnTable(ColumnA) select @tempString;
set @position=@endAt+1;
END;
return;
END;
用例
select * from dbo.SplitToRows('T14; p226.0001; eee; 3554;', ';');
或者只是一个带有多个结果集的选择
DECLARE @column varchar(max)= '1234; 4748;abcde; 324432';
DECLARE @separator varchar(10) = ';';
DECLARE @position int = 0;
DECLARE @endAt int = 0;
DECLARE @tempString varchar(100);
set @column = ltrim(rtrim(@column));
WHILE @position<=len(@column)
BEGIN
set @endAt = CHARINDEX(@separator,@column,@position);
if(@endAt=0)
begin
Select substring(@column,@position,len(@column)-@position);
break;
end;
set @tempString = substring(ltrim(rtrim(@column)),@position,@endAt-@position);
select @tempString;
set @position=@endAt+1;
END;
【讨论】:
在多语句表值函数中使用 while 循环几乎是拆分字符串的最糟糕方法。这个问题已经有很多基于集合的选项了。【参考方案12】:请参考下面的 TSQL。 STRING_SPLIT 函数仅在兼容级别 130 及以上可用。
TSQL:
DECLARE @stringValue NVARCHAR(400) = 'red,blue,green,yellow,black';
DECLARE @separator CHAR = ',';
SELECT [value] As Colour
FROM STRING_SPLIT(@stringValue, @separator);
结果:
颜色
红色 蓝色 绿色 黄色的 黑色
【讨论】:
【参考方案13】:很晚,但试试这个:
SELECT ColumnID, Column1, value --Do not change 'value' name. Leave it as it is.
FROM tbl_Sample
CROSS APPLY STRING_SPLIT(Tags, ','); --'Tags' is the name of column containing comma separated values
所以我们有这个: tbl_Sample:
ColumnID| Column1 | Tags
--------|-----------|-------------
1 | ABC | 10,11,12
2 | PQR | 20,21,22
运行此查询后:
ColumnID| Column1 | value
--------|-----------|-----------
1 | ABC | 10
1 | ABC | 11
1 | ABC | 12
2 | PQR | 20
2 | PQR | 21
2 | PQR | 22
谢谢!
【讨论】:
STRING_SPLIT
很漂亮,但它需要 SQL Server 2016。docs.microsoft.com/en-us/sql/t-sql/functions/…
优雅的解决方案。
是的,@SangramNandkhile 确实说过,这是最优雅的解决方案,无需声明任何变量,注释很好的代码,这就是我想要的。感谢地牢【参考方案14】:
您可以使用以下函数来提取数据
CREATE FUNCTION [dbo].[SplitString]
(
@RowData NVARCHAR(MAX),
@Delimeter NVARCHAR(MAX)
)
RETURNS @RtnValue TABLE
(
ID INT IDENTITY(1,1),
Data NVARCHAR(MAX)
)
AS
BEGIN
DECLARE @Iterator INT;
SET @Iterator = 1;
DECLARE @FoundIndex INT;
SET @FoundIndex = CHARINDEX(@Delimeter,@RowData);
WHILE (@FoundIndex>0)
BEGIN
INSERT INTO @RtnValue (data)
SELECT
Data = LTRIM(RTRIM(SUBSTRING(@RowData, 1, @FoundIndex - 1)));
SET @RowData = SUBSTRING(@RowData,
@FoundIndex + DATALENGTH(@Delimeter) / 2,
LEN(@RowData));
SET @Iterator = @Iterator + 1;
SET @FoundIndex = CHARINDEX(@Delimeter, @RowData);
END;
INSERT INTO @RtnValue (Data)
SELECT Data = LTRIM(RTRIM(@RowData));
RETURN;
END;
【讨论】:
在多语句表值函数中使用 while 循环几乎是拆分字符串的最糟糕方法。这个问题已经有很多基于集合的选项了。【参考方案15】:通过创建分割字符串的函数 ([DelimitedSplit]),您可以对 SELECT 执行 OUTER APPLY。
CREATE FUNCTION [dbo].[DelimitedSplit]
--===== Define I/O parameters
(@pString VARCHAR(8000), @pDelimiter CHAR(1))
--WARNING!!! DO NOT USE MAX DATA-TYPES HERE! IT WILL KILL PERFORMANCE!
RETURNS TABLE WITH SCHEMABINDING AS
RETURN
--===== "Inline" CTE Driven "Tally Table" produces values from 1 up to 10,000...
-- enough to cover VARCHAR(8000)
WITH E1(N) AS (
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
), --10E+1 or 10 rows
E2(N) AS (SELECT 1 FROM E1 a INNER JOIN E1 b ON b.N = a.N), --10E+2 or 100 rows
E4(N) AS (SELECT 1 FROM E2 a INNER JOIN E2 b ON b.N = a.N), --10E+4 or 10,000 rows max
cteTally(N) AS (--==== This provides the "base" CTE and limits the number of rows right up front
-- for both a performance gain and prevention of accidental "overruns"
SELECT TOP (ISNULL(DATALENGTH(@pString),0)) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
),
cteStart(N1) AS (--==== This returns N+1 (starting position of each "element" just once for each delimiter)
SELECT 1 UNION ALL
SELECT t.N+1 FROM cteTally t WHERE SUBSTRING(@pString,t.N,1) = @pDelimiter
),
cteLen(N1,L1) AS(--==== Return start and length (for use in substring)
SELECT s.N1,
ISNULL(NULLIF(CHARINDEX(@pDelimiter,@pString,s.N1),0)-s.N1,8000)
FROM cteStart s
)
--===== Do the actual split. The ISNULL/NULLIF combo handles the length for the final element when no delimiter is found.
SELECT ItemNumber = ROW_NUMBER() OVER(ORDER BY l.N1),
Item = SUBSTRING(@pString, l.N1, l.L1)
FROM cteLen l
;
测试
CREATE TABLE #Testdata
(
SomeID INT,
OtherID INT,
String VARCHAR(MAX)
);
INSERT #Testdata SELECT 1, 9, '18,20,22';
INSERT #Testdata SELECT 2, 8, '17,19';
INSERT #Testdata SELECT 3, 7, '13,19,20';
INSERT #Testdata SELECT 4, 6, '';
INSERT #Testdata SELECT 9, 11, '1,2,3,4';
SELECT
*
FROM #Testdata
OUTER APPLY [dbo].[DelimitedSplit](String,',');
DROP TABLE #Testdata;
结果
SomeID OtherID String ItemNumber Item
1 9 18,20,22 1 18
1 9 18,20,22 2 20
1 9 18,20,22 3 22
2 8 17,19 1 17
2 8 17,19 2 19
3 7 13,19,20 1 13
3 7 13,19,20 2 19
3 7 13,19,20 3 20
4 6 1
9 11 1,2,3,4 1 1
9 11 1,2,3,4 2 2
9 11 1,2,3,4 3 3
9 11 1,2,3,4 4 4
【讨论】:
【参考方案16】:我知道它有很多答案,但我想像其他人一样编写我的拆分函数版本,就像 string_split SQL Server 2016 原生函数一样。
create function [dbo].[Split]
(
@Value nvarchar(max),
@Delimiter nvarchar(50)
)
returns @tbl table
(
Seq int primary key identity(1, 1),
Value nvarchar(max)
)
as begin
declare @Xml xml = cast('<d>' + replace(@Value, @Delimiter, '</d><d>') + '</d>' as xml);
insert into @tbl
(Value)
select a.split.value('.', 'nvarchar(max)') as Value
from @Xml.nodes('/d') a(split);
return;
end;
Seq 列作为主键,支持与其他真实表或Split 函数返回表的快速连接。
使用 XML 函数支持大数据(大数据时循环版本会明显变慢)
这是问题的答案。
CREATE TABLE Testdata
(
SomeID INT,
OtherID INT,
String VARCHAR(MAX)
);
INSERT Testdata SELECT 1, 9, '18,20,22';
INSERT Testdata SELECT 2, 8, '17,19';
INSERT Testdata SELECT 3, 7, '13,19,20';
INSERT Testdata SELECT 4, 6, '';
INSERT Testdata SELECT 9, 11, '1,2,3,4';
select t.SomeID, t.OtherID, s.Value
from Testdata t
cross apply dbo.Split(t.String, ',') s;
--Output
SomeID OtherID Value
1 9 18
1 9 20
1 9 22
2 8 17
2 8 19
3 7 13
3 7 19
3 7 20
4 6
9 11 1
9 11 2
9 11 3
9 11 4
与其他拆分加入拆分
declare @Names nvarchar(max) = 'a,b,c,d';
declare @Codes nvarchar(max) = '10,20,30,40';
select n.Seq, n.Value Name, c.Value Code
from dbo.Split(@Names, ',') n
inner join dbo.Split(@Codes, ',') c on n.Seq = c.Seq;
--Output
Seq Name Code
1 a 10
2 b 20
3 c 30
4 d 40
分两次
declare @NationLocSex nvarchar(max) = 'Korea,Seoul,1;Vietnam,Kiengiang,0;China,Xian,0';
with rows as
(
select Value
from dbo.Split(@NationLocSex, ';')
)
select rw.Value r, cl.Value c
from rows rw
cross apply dbo.Split(rw.Value, ',') cl;
--Output
r c
Korea,Seoul,1 Korea
Korea,Seoul,1 Seoul
Korea,Seoul,1 1
Vietnam,Kiengiang,0 Vietnam
Vietnam,Kiengiang,0 Kiengiang
Vietnam,Kiengiang,0 0
China,Xian,0 China
China,Xian,0 Xian
China,Xian,0 0
分列
declare @Numbers nvarchar(50) = 'First,Second,Third';
with t as
(
select case when Seq = 1 then Value end f1,
case when Seq = 2 then Value end f2,
case when Seq = 3 then Value end f3
from dbo.Split(@Numbers, ',')
)
select min(f1) f1, min(f2) f2, min(f3) f3
from t;
--Output
f1 f2 f3
First Second Third
按范围生成行
declare @Ranges nvarchar(50) = '1-2,4-6';
declare @Numbers table (Num int);
insert into @Numbers values (1),(2),(3),(4),(5),(6),(7),(8);
with t as
(
select r.Seq, r.Value,
min(case when ft.Seq = 1 then ft.Value end) ValueFrom,
min(case when ft.Seq = 2 then ft.Value end) ValueTo
from dbo.Split(@Ranges, ',') r
cross apply dbo.Split(r.Value, '-') ft
group by r.Seq, r.Value
)
select t.Seq, t.Value, t.ValueFrom, t.ValueTo, n.Num
from t
inner join @Numbers n on n.Num between t.ValueFrom and t.ValueTo;
--Output
Seq Value ValueFrom ValueTo Num
1 1-2 1 2 1
1 1-2 1 2 2
2 4-6 4 6 4
2 4-6 4 6 5
2 4-6 4 6 6
【讨论】:
以上是关于将逗号分隔的字符串转换为单独的行的主要内容,如果未能解决你的问题,请参考以下文章