kri*_*tof 136 t-sql sql-server sql-server-2005
可能重复:
SQL中的拆分字符串
我在SQL中看到了几个与字符串连接相关的问题.我想知道你将如何处理相反的问题:将昏迷分隔的字符串拆分为数据行:
可以说我有桌子:
userTypedTags(userID,commaSeparatedTags) 'one entry per user
tags(tagID,name)
Run Code Online (Sandbox Code Playgroud)
并希望将数据插入表中
userTag(userID,tagID) 'multiple entries per user
Run Code Online (Sandbox Code Playgroud)
灵感来自数据库中没有哪些标签?题
编辑
谢谢你的答案,实际上有一个值得接受,但我只能选择一个,而Cade Roux提出的递归解决方案对我来说似乎很干净.它适用于SQL Server 2005及更高版本.
对于早期版本的SQL Server,可以使用miies提供的解决方案.对于使用文本数据类型,wcm answer会很有帮助.再次感谢.
Cad*_*oux 148
这里记录了这个问题的各种解决方案,包括这个小宝石:
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
)
Run Code Online (Sandbox Code Playgroud)
Nat*_*ler 85
您也可以使用XML实现此效果,如此处所示,这消除了所提供的答案的限制,这些答案似乎都以某种方式包含递归.我在这里所做的特殊用法允许最多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)
)
Run Code Online (Sandbox Code Playgroud)
然后你可以使用以下方法调用它:
SELECT * FROM dbo.Split(' ', 'I hate bunnies')
Run Code Online (Sandbox Code Playgroud)
哪个回报:
-----------
|I |
|---------|
|hate |
|---------|
|bunnies |
-----------
Run Code Online (Sandbox Code Playgroud)
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)
)
Run Code Online (Sandbox Code Playgroud)
use*_*603 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
Run Code Online (Sandbox Code Playgroud)
Mar*_*ith 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
Run Code Online (Sandbox Code Playgroud)
返回
display_term Cnt
------------------------------ -----------
the 3
brown 2
lorry 2
sea 2
Run Code Online (Sandbox Code Playgroud)
稍微修改上面的解决方案,使其适用于可变长度分隔符.
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
)
Run Code Online (Sandbox Code Playgroud)
注意:我使用了datalength(),因为如果有尾随空格,len()会报错.
这是一个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
Run Code Online (Sandbox Code Playgroud)
使用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);
}
}
Run Code Online (Sandbox Code Playgroud)
当然,它仍然比PostgreSQL慢8倍regexp_split_to_table.
SELECT substring(commaSeparatedTags,0,charindex(',',commaSeparatedTags))
Run Code Online (Sandbox Code Playgroud)
会给你第一个标签.你可以通过类似的方式继续获得第二个,依此类推,每次将substring和charindex组合为一层.这是一个直接的解决方案,但它只适用于非常少的标签,因为查询的大小变得非常快并且变得不可读.然后转到功能,如本文其他更复杂的答案所述.
| 归档时间: |
|
| 查看次数: |
167799 次 |
| 最近记录: |