可能重复:
Split string in SQL
我在SQL中看到a couple of questions related to string concatenation。 我想知道你将如何解决相反的问题:将昏迷分隔的字符串拆分为数据行:
假设我有桌子:
userTypedTags(userID,commaSeparatedTags) 'one entry per user
tags(tagID,name)
并希望将数据插入表格
userTag(userID,tagID) 'multiple entries per user
灵感来自Which tags are not in the database?问题
修改
感谢您的回答,实际上多了一个值得接受,但我只能选择一个,solution presented by Cade Roux递归对我来说似乎很干净。它适用于SQL Server 2005及更高版本。
对于早期版本的SQL Server,可以使用解决方案provided by miies。 对于处理文本数据,wcm answer类型将会很有帮助。再次感谢。
答案 0 :(得分:147)
这个问题documented here有很多种解决方案,包括这个小宝石:
CREATE FUNCTION dbo.Split (@sep char(1), @s varchar(512))
RETURNS table
AS
RETURN (
WITH Pieces(pn, start, stop) AS (
SELECT 1, 1, CHARINDEX(@sep, @s)
UNION ALL
SELECT pn + 1, stop + 1, CHARINDEX(@sep, @s, stop + 1)
FROM Pieces
WHERE stop > 0
)
SELECT pn,
SUBSTRING(@s, start, CASE WHEN stop > 0 THEN stop-start ELSE 512 END) AS s
FROM Pieces
)
答案 1 :(得分:84)
您还可以使用XML as seen here来实现此效果,这样可以消除所提供答案的限制,这些答案似乎都以某种方式包含递归。我在这里做的特殊用法允许最多32个字符的分隔符,但是可以增加它需要的大。
create FUNCTION [dbo].[Split] (@sep VARCHAR(32), @s VARCHAR(MAX))
RETURNS TABLE
AS
RETURN
(
SELECT r.value('.','VARCHAR(MAX)') as Item
FROM (SELECT CONVERT(XML, N'<root><r>' + REPLACE(REPLACE(REPLACE(@s,'& ','& '),'<','<'), @sep, '</r><r>') + '</r></root>') as valxml) x
CROSS APPLY x.valxml.nodes('//root/r') AS RECORDS(r)
)
然后你可以使用:
调用它SELECT * FROM dbo.Split(' ', 'I hate bunnies')
返回:
-----------
|I |
|---------|
|hate |
|---------|
|bunnies |
-----------
<小时/> 我应该注意,我实际上并不讨厌兔子......它只是出于某种原因突然出现在我脑海中。
CREATE FUNCTION [dbo].[Split] (@sep VARCHAR(32), @s VARCHAR(MAX))
RETURNS TABLE
AS
RETURN
(
SELECT r.value('.','VARCHAR(MAX)') as Item
FROM (SELECT CONVERT(XML, N'<root><r>' + REPLACE(@s, @sep, '</r><r>') + '</r></root>') as valxml) x
CROSS APPLY x.valxml.nodes('//root/r') AS RECORDS(r)
)
答案 2 :(得分:18)
我使用此功能(SQL Server 2005及更高版本)。
create function [dbo].[Split]
(
@string nvarchar(4000),
@delimiter nvarchar(10)
)
returns @table table
(
[Value] nvarchar(4000)
)
begin
declare @nextString nvarchar(4000)
declare @pos int, @nextPos int
set @nextString = ''
set @string = @string + @delimiter
set @pos = charindex(@delimiter, @string)
set @nextPos = 1
while (@pos <> 0)
begin
set @nextString = substring(@string, 1, @pos - 1)
insert into @table
(
[Value]
)
values
(
@nextString
)
set @string = substring(@string, @pos + len(@delimiter), len(@string))
set @nextPos = @pos
set @pos = charindex(@delimiter, @string)
end
return
end
答案 3 :(得分:11)
对于将字符串拆分为单词的特殊情况,我遇到了另一种SQL Server 2008解决方案。
with testTable AS
(
SELECT 1 AS Id, N'how now brown cow' AS txt UNION ALL
SELECT 2, N'she sells sea shells upon the sea shore' UNION ALL
SELECT 3, N'red lorry yellow lorry' UNION ALL
SELECT 4, N'the quick brown fox jumped over the lazy dog'
)
SELECT display_term, COUNT(*) As Cnt
FROM testTable
CROSS APPLY sys.dm_fts_parser('"' + txt + '"', 1033, 0,0)
GROUP BY display_term
HAVING COUNT(*) > 1
ORDER BY Cnt DESC
返回
display_term Cnt
------------------------------ -----------
the 3
brown 2
lorry 2
sea 2
答案 4 :(得分:7)
对the solution进行轻微修改,使其适用于可变长度分隔符。
create FUNCTION dbo.fn_Split2 (@sep nvarchar(10), @s nvarchar(4000))
RETURNS table
AS
RETURN (
WITH Pieces(pn, start, stop) AS (
SELECT 1, 1, CHARINDEX(@sep, @s)
UNION ALL
SELECT pn + 1, stop + (datalength(@sep)/2), CHARINDEX(@sep, @s, stop + (datalength(@sep)/2))
FROM Pieces
WHERE stop > 0
)
SELECT pn,
SUBSTRING(@s, start, CASE WHEN stop > 0 THEN stop-start ELSE 4000 END) AS s
FROM Pieces
)
注意:我使用了datalength(),因为如果有尾随空格,len()会报错。
答案 5 :(得分:7)
这是一个Split
函数,它与2005之前的SQL Server版本兼容。
CREATE FUNCTION dbo.Split(@data nvarchar(4000), @delimiter nvarchar(100))
RETURNS @result table (Id int identity(1,1), Data nvarchar(4000))
AS
BEGIN
DECLARE @pos INT
DECLARE @start INT
DECLARE @len INT
DECLARE @end INT
SET @len = LEN('.' + @delimiter + '.') - 2
SET @end = LEN(@data) + 1
SET @start = 1
SET @pos = 0
WHILE (@pos < @end)
BEGIN
SET @pos = CHARINDEX(@delimiter, @data, @start)
IF (@pos = 0) SET @pos = @end
INSERT @result (data) SELECT SUBSTRING(@data, @start, @pos - @start)
SET @start = @pos + @len
END
RETURN
END
答案 6 :(得分:7)
使用CLR,这是一个更简单的替代方案,适用于所有情况,但比接受的答案快40%:
using System;
using System.Collections;
using System.Data.SqlTypes;
using System.Text.RegularExpressions;
using Microsoft.SqlServer.Server;
public class UDF
{
[SqlFunction(FillRowMethodName="FillRow")]
public static IEnumerable RegexSplit(SqlString s, SqlString delimiter)
{
return Regex.Split(s.Value, delimiter.Value);
}
public static void FillRow(object row, out SqlString str)
{
str = new SqlString((string) row);
}
}
当然,它仍然比PostgreSQL的regexp_split_to_table
慢8倍。
答案 7 :(得分:5)
SELECT substring(commaSeparatedTags,0,charindex(',',commaSeparatedTags))
会给你第一个标签。你可以通过类似的方式继续获得第二个,依此类推,每次将substring和charindex组合为一层。这是一个直接的解决方案,但它只适用于非常少的标签,因为查询的大小变得非常快并且变得不可读。然后转到功能,如本文其他更复杂的答案所述。
答案 8 :(得分:2)
我写了一会儿。它假定分隔符是逗号,并且各个值不大于127个字符。它可以很容易地修改。
它的好处是不限于4,000个字符。
祝你好运!ALTER Function [dbo].[SplitStr] (
@txt text
)
Returns @tmp Table
(
value varchar(127)
)
as
BEGIN
declare @str varchar(8000)
, @Beg int
, @last int
, @size int
set @size=datalength(@txt)
set @Beg=1
set @str=substring(@txt,@Beg,8000)
IF len(@str)<8000 set @Beg=@size
ELSE BEGIN
set @last=charindex(',', reverse(@str))
set @str=substring(@txt,@Beg,8000-@last)
set @Beg=@Beg+8000-@last+1
END
declare @workingString varchar(25)
, @stringindex int
while @Beg<=@size Begin
WHILE LEN(@str) > 0 BEGIN
SELECT @StringIndex = CHARINDEX(',', @str)
SELECT
@workingString = CASE
WHEN @StringIndex > 0 THEN SUBSTRING(@str, 1, @StringIndex-1)
ELSE @str
END
INSERT INTO
@tmp(value)
VALUES
(cast(rtrim(ltrim(@workingString)) as varchar(127)))
SELECT @str = CASE
WHEN CHARINDEX(',', @str) > 0 THEN SUBSTRING(@str, @StringIndex+1, LEN(@str))
ELSE ''
END
END
set @str=substring(@txt,@Beg,8000)
if @Beg=@size set @Beg=@Beg+1
else IF len(@str)<8000 set @Beg=@size
ELSE BEGIN
set @last=charindex(',', reverse(@str))
set @str=substring(@txt,@Beg,8000-@last)
set @Beg=@Beg+8000-@last+1
END
END
return
END
答案 9 :(得分:2)
我投了“Nathan Wheeler”的答案,因为我发现“Cade Roux”的答案在某个字符串大小之上无效。
几点意见
- 我发现为我添加了DISTINCT关键字改进的性能。
-Nathan的回答仅在您的标识符为5个字符或更少时才有效,当然您可以调整...如果您要分割的项目是 INT 标识符,我可以使用我在下面:
CREATE FUNCTION [dbo].Split
(
@sep VARCHAR(32),
@s VARCHAR(MAX)
)
RETURNS
@result TABLE (
Id INT NULL
)
AS
BEGIN
DECLARE @xml XML
SET @XML = N'<root><r>' + REPLACE(@s, @sep, '</r><r>') + '</r></root>'
INSERT INTO @result(Id)
SELECT DISTINCT r.value('.','int') as Item
FROM @xml.nodes('//root//r') AS RECORDS(r)
RETURN
END
答案 10 :(得分:0)
我通常使用以下代码执行此操作:
create function [dbo].[Split](@string varchar(max), @separator varchar(10))
returns @splited table ( stringPart varchar(max) )
with execute as caller
as
begin
declare @stringPart varchar(max);
set @stringPart = '';
while charindex(@separator, @string) > 0
begin
set @stringPart = substring(@string, 0, charindex(@separator, @string));
insert into @splited (stringPart) values (@stringPart);
set @string = substring(@string, charindex(@separator, @string) + len(@separator), len(@string) + 1);
end
return;
end
go
您可以使用此查询对其进行测试:
declare @example varchar(max);
set @example = 'one;string;to;rule;them;all;;';
select * from [dbo].[Split](@example, ';');