T-SQL拆分字符串

Lee*_*don 121 sql t-sql sql-server split sql-server-2008

我有一个SQL Server 2008 R2列包含一个字符串,我需要用逗号分隔.我在StackOverflow上看到了很多答案,但它们都不适用于R2.我确保我对任何拆分函数示例都有选择权限.任何帮助非常感谢.

And*_*son 203

我之前使用过这个SQL可能对你有用: -

CREATE FUNCTION dbo.splitstring ( @stringToSplit VARCHAR(MAX) )
RETURNS
 @returnList TABLE ([Name] [nvarchar] (500))
AS
BEGIN

 DECLARE @name NVARCHAR(255)
 DECLARE @pos INT

 WHILE CHARINDEX(',', @stringToSplit) > 0
 BEGIN
  SELECT @pos  = CHARINDEX(',', @stringToSplit)  
  SELECT @name = SUBSTRING(@stringToSplit, 1, @pos-1)

  INSERT INTO @returnList 
  SELECT @name

  SELECT @stringToSplit = SUBSTRING(@stringToSplit, @pos+1, LEN(@stringToSplit)-@pos)
 END

 INSERT INTO @returnList
 SELECT @stringToSplit

 RETURN
END
Run Code Online (Sandbox Code Playgroud)

并使用它: -

SELECT * FROM dbo.splitstring('91,12,65,78,56,789')
Run Code Online (Sandbox Code Playgroud)

  • 虽然这是一个很好的答案,但它已经过时了......程序方法(特别是循环)是值得避免的......值得研究更新的答案...... (8认同)
  • 非常感谢Andy。我对您的脚本进行了一些小的增强,以允许函数在拆分字符串中的特定索引处返回项目。仅当您正在解析第一列的结构时,它才有用。https://gist.github.com/klimaye/8147193 (2认同)
  • 我完全同意@Shnugo。循环分离器可以工作,但速度非常慢。像http://www.sqlservercentral.com/articles/Tally+Table/72993/这样的东西要好得多。一些其他基于集合的优秀选项可以在这里找到。https://sqlperformance.com/2012/07/t-sql-queries/split-strings (2认同)

Aar*_*and 57

而不是递归CTE和while循环,有没有人考虑过更基于集合的方法?

CREATE FUNCTION dbo.SplitString
(
  @List     nvarchar(max),
  @Delim    nvarchar(255)
)
RETURNS TABLE
AS
  RETURN ( SELECT [Value] FROM 
  ( 
    SELECT [Value] = LTRIM(RTRIM(SUBSTRING(@List, [Number],
      CHARINDEX(@Delim, @List + @Delim, [Number]) - [Number])))
    FROM (SELECT Number = ROW_NUMBER() OVER (ORDER BY name)
      FROM sys.all_columns) AS x WHERE Number <= LEN(@List)
      AND SUBSTRING(@Delim + @List, [Number], DATALENGTH(@Delim)/2) = @Delim
    ) AS y
  );
GO
Run Code Online (Sandbox Code Playgroud)

有关分割函数的更多信息,为什么(并证明)while循环和递归CTE不能缩放,以及更好的替代方法,如果分割来自应用程序层的字符串:

http://www.sqlperformance.com/2012/07/t-sql-queries/split-strings

http://www.sqlperformance.com/2012/08/t-sql-queries/splitting-strings-now-with-less-t-sql

https://sqlblog.org/2010/07/07/splitting-a-list-of-integers-another-roundup


Pரத*_*ீப் 46

最后等待在SQL Server 2016中结束了他们引入了Split字符串函数:STRING_SPLIT

select * From STRING_SPLIT ('a,b', ',') cs 
Run Code Online (Sandbox Code Playgroud)

所有其他分割字符串的方法,如XML,Tally表,while循环等等,都被这个STRING_SPLIT函数所震撼.

以下是性能比较的优秀文章:性能惊喜和假设:STRING_SPLIT

  • 显然回答了如何为那些拥有更新服务器的人分割字符串的问题,但是我们这些人仍然坚持2008/2008R2,将不得不在这里使用其中一个答案. (4认同)
  • 您需要查看数据库中的兼容级别.如果它低于130,您将无法使用STRING_SPLIT功能. (2认同)
  • 实际上,如果兼容性不是 130 并且您运行的是 2016(或 Azure SQL),您可以使用以下命令将兼容性设置为 130: ALTER DATABASE DatabaseName SET COMPATIBILITY_LEVEL = 130 (2认同)

Sar*_*avu 23

最简单的方法是使用XML格式.

1.将字符串转换为不带表的行

QUERY

DECLARE @String varchar(100) = 'String1,String2,String3'
-- To change ',' to any other delimeter, just change ',' to your desired one
DECLARE @Delimiter CHAR = ','    

SELECT LTRIM(RTRIM(Split.a.value('.', 'VARCHAR(100)'))) 'Value' 
FROM  
(     
     SELECT CAST ('<M>' + REPLACE(@String, @Delimiter, '</M><M>') + '</M>' AS XML) AS Data            
) AS A 
CROSS APPLY Data.nodes ('/M') AS Split(a)
Run Code Online (Sandbox Code Playgroud)

结果

 x---------x
 | Value   |
 x---------x
 | String1 |
 | String2 |
 | String3 |
 x---------x
Run Code Online (Sandbox Code Playgroud)

2.从表中转换为具有每个CSV行的ID的行

消息表

 x-----x--------------------------x
 | Id  |           Value          |
 x-----x--------------------------x
 |  1  |  String1,String2,String3 |
 |  2  |  String4,String5,String6 |     
 x-----x--------------------------x
Run Code Online (Sandbox Code Playgroud)

QUERY

-- To change ',' to any other delimeter, just change ',' before '</M><M>' to your desired one
DECLARE @Delimiter CHAR = ','

SELECT ID,LTRIM(RTRIM(Split.a.value('.', 'VARCHAR(100)'))) 'Value' 
FROM  
(     
     SELECT ID,CAST ('<M>' + REPLACE(VALUE, @Delimiter, '</M><M>') + '</M>' AS XML) AS Data            
     FROM TABLENAME
) AS A 
CROSS APPLY Data.nodes ('/M') AS Split(a)
Run Code Online (Sandbox Code Playgroud)

结果

 x-----x----------x
 | Id  |  Value   |
 x-----x----------x
 |  1  |  String1 |
 |  1  |  String2 |  
 |  1  |  String3 |
 |  2  |  String4 |  
 |  2  |  String5 |
 |  2  |  String6 |     
 x-----x----------x
Run Code Online (Sandbox Code Playgroud)


小智 9

我需要一个快速的方法来摆脱+4从一个邮政编码.

UPDATE #Emails 
  SET ZIPCode = SUBSTRING(ZIPCode, 1, (CHARINDEX('-', ZIPCODE)-1)) 
  WHERE ZIPCode LIKE '%-%'
Run Code Online (Sandbox Code Playgroud)

没有proc ...没有UDF ...只是一个紧凑的小内联命令,它做了它必须做的事情.不花哨,不优雅.

根据需要更改分隔符等,它将适用于任何事情.

  • 这不是问题所在.OP有一个类似'234,542,23'的值,他们希望将它分成三行......第一行:234,第二行:542,第三行:23.在SQL中这是一个棘手的事情. (4认同)

Avi*_*viG 7

如果你更换

WHILE CHARINDEX(',', @stringToSplit) > 0
Run Code Online (Sandbox Code Playgroud)

WHILE LEN(@stringToSplit) > 0
Run Code Online (Sandbox Code Playgroud)

你可以在while循环之后消除最后一个插入!

CREATE FUNCTION dbo.splitstring ( @stringToSplit VARCHAR(MAX) )
RETURNS
 @returnList TABLE ([Name] [nvarchar] (500))
AS
BEGIN

 DECLARE @name NVARCHAR(255)
 DECLARE @pos INT

 WHILE LEN(@stringToSplit) > 0
 BEGIN
  SELECT @pos  = CHARINDEX(',', @stringToSplit)


if @pos = 0
        SELECT @pos = LEN(@stringToSplit)


  SELECT @name = SUBSTRING(@stringToSplit, 1, @pos-1)

  INSERT INTO @returnList 
  SELECT @name

  SELECT @stringToSplit = SUBSTRING(@stringToSplit, @pos+1, LEN(@stringToSplit)-@pos)
 END

 RETURN
END
Run Code Online (Sandbox Code Playgroud)


小智 5

所有使用某种循环(迭代)的字符串分割函数的性能都很差。它们应该被基于集合的解决方案取代。

这段代码执行得非常好。

CREATE FUNCTION dbo.SplitStrings
(
   @List       NVARCHAR(MAX),
   @Delimiter  NVARCHAR(255)
)
RETURNS TABLE
WITH SCHEMABINDING
AS
   RETURN 
   (  
      SELECT Item = y.i.value('(./text())[1]', 'nvarchar(4000)')
      FROM 
      ( 
        SELECT x = CONVERT(XML, '<i>' 
          + REPLACE(@List, @Delimiter, '</i><i>') 
          + '</i>').query('.')
      ) AS a CROSS APPLY x.nodes('i') AS y(i)
   );
GO
Run Code Online (Sandbox Code Playgroud)


Shn*_*ugo 5

XML 元素的常用方法在出现禁止字符的情况下会中断。这是一种将此方法用于任何类型的字符的方法,即使使用分号作为分隔符也是如此。

诀窍是,首先使用SELECT SomeString AS [*] FOR XML PATH('')来正确转义所有禁止的字符。这就是为什么我将分隔符替换为魔术值以避免使用;as 分隔符的麻烦的原因。

DECLARE @Dummy TABLE (ID INT, SomeTextToSplit NVARCHAR(MAX))
INSERT INTO @Dummy VALUES
 (1,N'A&B;C;D;E, F')
,(2,N'"C" & ''D'';<C>;D;E, F');

DECLARE @Delimiter NVARCHAR(10)=';'; --special effort needed (due to entities coding with "&code;")!

WITH Casted AS
(
    SELECT *
          ,CAST(N'<x>' + REPLACE((SELECT REPLACE(SomeTextToSplit,@Delimiter,N'§§Split$me$here§§') AS [*] FOR XML PATH('')),N'§§Split$me$here§§',N'</x><x>') + N'</x>' AS XML) AS SplitMe
    FROM @Dummy
)
SELECT Casted.ID
      ,x.value(N'.',N'nvarchar(max)') AS Part 
FROM Casted
CROSS APPLY SplitMe.nodes(N'/x') AS A(x)
Run Code Online (Sandbox Code Playgroud)

结果

ID  Part
1   A&B
1   C
1   D
1   E, F
2   "C" & 'D'
2   <C>
2   D
2   E, F
Run Code Online (Sandbox Code Playgroud)