T-SQL拆分string

我有一个SQL Server 2008 R2列包含一个string,我需要用逗号分隔。 我在StackOverflow上看到了很多答案,但是它们都不适用于R2。 我已经确定我有任何分割函数示例的select权限。 任何帮助不胜感激。

我以前使用过这个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 

并使用它:

 SELECT * FROM dbo.splitstring('91,12,65,78,56,789') 

没有recursion的CTE和while循环,有没有人考虑过一个更基于集合的方法?

 CREATE FUNCTION [dbo].[SplitString] ( @List NVARCHAR(MAX), @Delim VARCHAR(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_objects) AS x WHERE Number <= LEN(@List) AND SUBSTRING(@Delim + @List, [Number], LEN(@Delim)) = @Delim ) AS y ); 

更多关于拆分函数的信息,为什么(以及certificate)while循环和recursionCTE不能缩放,以及更好的select,如果拆分来自应用层的string:

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

http://sqlblog.com/blogs/aaron_bertrand/archive/2010/07/07/splitting-a-list-of-integers-another-roundup.aspx

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

 select * From STRING_SPLIT ('a,b', ',') cs 

所有其他的方法来拆分像XML,Tally表,while循环等string已被这个STRING_SPLIT函数吹走了。

这里是一个很好的文章与性能比较: 性能惊喜和假设:STRING_SPLIT

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

 UPDATE #Emails SET ZIPCode = SUBSTRING(ZIPCode, 1, (CHARINDEX('-', ZIPCODE)-1)) WHERE ZIPCode LIKE '%-%' 

没有处理…没有UDF …只是一个紧凑的内联命令,它必须做的。 不花哨,不优雅。

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

如果你更换

 WHILE CHARINDEX(',', @stringToSplit) > 0 

 WHILE LEN(@stringToSplit) > 0 

你可以消除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 

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

1.将string转换为没有表的行

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) 

结果

  x---------x | Value | x---------x | String1 | | String2 | | String3 | x---------x 

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

源表

  x-----x--------------------------x | Id | Value | x-----x--------------------------x | 1 | String1,String2,String3 | | 2 | String4,String5,String6 | x-----x--------------------------x 

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) 

结果

  x-----x----------x | Id | Value | x-----x----------x | 1 | String1 | | 1 | String2 | | 1 | String3 | | 2 | String4 | | 2 | String5 | | 2 | String6 | x-----x----------x 

一个使用CTE的解决scheme,如果有人需要的话(除了我,明显是谁,这就是我写这个的原因)。

 declare @StringToSplit varchar(100) = 'Test1,Test2,Test3'; declare @SplitChar varchar(10) = ','; with StringToSplit as ( select ltrim( rtrim( substring( @StringToSplit, 1, charindex( @SplitChar, @StringToSplit ) - 1 ) ) ) Head , substring( @StringToSplit, charindex( @SplitChar, @StringToSplit ) + 1, len( @StringToSplit ) ) Tail union all select ltrim( rtrim( substring( Tail, 1, charindex( @SplitChar, Tail ) - 1 ) ) ) Head , substring( Tail, charindex( @SplitChar, Tail ) + 1, len( Tail ) ) Tail from StringToSplit where charindex( @SplitChar, Tail ) > 0 union all select ltrim( rtrim( Tail ) ) Head , '' Tail from StringToSplit where charindex( @SplitChar, Tail ) = 0 and len( Tail ) > 0 ) select Head from StringToSplit 

在这里有一个正确的版本,但我认为这将是很好的添加一点容错,以防止他们有一个尾随逗号,以及使它可以使用它不作为一个函数,但作为一个较大的代码的一部分。 以防万一您只使用一次,不需要function。 这也是整数(这是我所需要的),所以你可能不得不改变你的数据types。

 DECLARE @StringToSeperate VARCHAR(10) SET @StringToSeperate = '1,2,5' --SELECT @StringToSeperate IDs INTO #Test DROP TABLE #IDs CREATE TABLE #IDs (ID int) DECLARE @CommaSeperatedValue NVARCHAR(255) = '' DECLARE @Position INT = LEN(@StringToSeperate) --Add Each Value WHILE CHARINDEX(',', @StringToSeperate) > 0 BEGIN SELECT @Position = CHARINDEX(',', @StringToSeperate) SELECT @CommaSeperatedValue = SUBSTRING(@StringToSeperate, 1, @Position-1) INSERT INTO #IDs SELECT @CommaSeperatedValue SELECT @StringToSeperate = SUBSTRING(@StringToSeperate, @Position+1, LEN(@StringToSeperate)-@Position) END --Add Last Value IF (LEN(LTRIM(RTRIM(@StringToSeperate)))>0) BEGIN INSERT INTO #IDs SELECT SUBSTRING(@StringToSeperate, 1, @Position) END SELECT * FROM #IDs 

我修改了Andy Robinson的function。 现在,您只能从返回表中select所需的部分:

 CREATE FUNCTION dbo.splitstring ( @stringToSplit VARCHAR(MAX) ) RETURNS @returnList TABLE ([numOrder] [tinyint] , [Name] [nvarchar] (500)) AS BEGIN DECLARE @name NVARCHAR(255) DECLARE @pos INT DECLARE @orderNum INT SET @orderNum=0 WHILE CHARINDEX('.', @stringToSplit) > 0 BEGIN SELECT @orderNum=@orderNum+1; SELECT @pos = CHARINDEX('.', @stringToSplit) SELECT @name = SUBSTRING(@stringToSplit, 1, @pos-1) INSERT INTO @returnList SELECT @orderNum,@name SELECT @stringToSplit = SUBSTRING(@stringToSplit, @pos+1, LEN(@stringToSplit)-@pos) END SELECT @orderNum=@orderNum+1; INSERT INTO @returnList SELECT @orderNum, @stringToSplit RETURN END Usage: 

SELECT Name FROM dbo.splitstring('ELIS.YD.CRP1.1.CBA.MDSP.T389.BT') WHERE numOrder=5

所有使用某种循环(迭代)的string拆分function性能都不好。 他们应该被replace为基于集合的解决scheme。

此代码执行优秀。

 CREATE FUNCTION dbo.SplitStrings ( @List NVARCHAR(MAX), @Delimiter NVARCHAR(255) ) RETURNS TABLE WITH SCHEMABINDING AS RETURN ( SELECT Item = yivalue('(./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 

我最近不得不写这样的东西。 这是我提出的解决scheme。 这是泛化的任何分隔符string,我认为它会稍微好一点:

 CREATE FUNCTION [dbo].[SplitString] ( @string nvarchar(4000) , @delim nvarchar(100) ) RETURNS @result TABLE ( [Value] nvarchar(4000) NOT NULL , [Index] int NOT NULL ) AS BEGIN DECLARE @str nvarchar(4000) , @pos int , @prv int = 1 SELECT @pos = CHARINDEX(@delim, @string) WHILE @pos > 0 BEGIN SELECT @str = SUBSTRING(@string, @prv, @pos - @prv) INSERT INTO @result SELECT @str, @prv SELECT @prv = @pos + LEN(@delim) , @pos = CHARINDEX(@delim, @string, @pos + 1) END INSERT INTO @result SELECT SUBSTRING(@string, @prv, 4000), @prv RETURN END 

这里是一个版本,可以使用patindex,一个简单的适应上面的post拆分模式。 我有一个情况,我需要拆分包含多个分隔符string。

 alter FUNCTION dbo.splitstring ( @stringToSplit VARCHAR(1000), @splitPattern varchar(10) ) RETURNS @returnList TABLE ([Name] [nvarchar] (500)) AS BEGIN DECLARE @name NVARCHAR(255) DECLARE @pos INT WHILE PATINDEX(@splitPattern, @stringToSplit) > 0 BEGIN SELECT @pos = PATINDEX(@splitPattern, @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 select * from dbo.splitstring('stringa/stringb/x,y,z','%[/,]%'); 

结果看起来像这样

弦乐弦乐团x y z

Personnaly我使用这个function:

 ALTER FUNCTION [dbo].[CUST_SplitString] ( @String NVARCHAR(4000), @Delimiter NCHAR(1) ) RETURNS TABLE AS RETURN ( WITH Split(stpos,endpos) AS( SELECT 0 AS stpos, CHARINDEX(@Delimiter,@String) AS endpos UNION ALL SELECT endpos+1, CHARINDEX(@Delimiter,@String,endpos+1) FROM Split WHERE endpos > 0 ) SELECT 'Id' = ROW_NUMBER() OVER (ORDER BY (SELECT 1)), 'Data' = SUBSTRING(@String,stpos,COALESCE(NULLIF(endpos,0),LEN(@String)+1)-stpos) FROM Split ) 

我开发了一个双分配器(需要两个分割字符)按要求在这里 。 在这个线程中可能有一些值可以看到它对于与string分割相关的查询最引用。

 CREATE FUNCTION uft_DoubleSplitter ( -- Add the parameters for the function here @String VARCHAR(4000), @Splitter1 CHAR, @Splitter2 CHAR ) RETURNS @Result TABLE (Id INT,MId INT,SValue VARCHAR(4000)) AS BEGIN DECLARE @FResult TABLE(Id INT IDENTITY(1, 1), SValue VARCHAR(4000)) DECLARE @SResult TABLE(Id INT IDENTITY(1, 1), MId INT, SValue VARCHAR(4000)) SET @String = @String+@Splitter1 WHILE CHARINDEX(@Splitter1, @String) > 0 BEGIN DECLARE @WorkingString VARCHAR(4000) = NULL SET @WorkingString = SUBSTRING(@String, 1, CHARINDEX(@Splitter1, @String) - 1) --Print @workingString INSERT INTO @FResult SELECT CASE WHEN @WorkingString = '' THEN NULL ELSE @WorkingString END SET @String = SUBSTRING(@String, LEN(@WorkingString) + 2, LEN(@String)) END IF ISNULL(@Splitter2, '') != '' BEGIN DECLARE @OStartLoop INT DECLARE @OEndLoop INT SELECT @OStartLoop = MIN(Id), @OEndLoop = MAX(Id) FROM @FResult WHILE @OStartLoop <= @OEndLoop BEGIN DECLARE @iString VARCHAR(4000) DECLARE @iMId INT SELECT @iString = SValue+@Splitter2, @iMId = Id FROM @FResult WHERE Id = @OStartLoop WHILE CHARINDEX(@Splitter2, @iString) > 0 BEGIN DECLARE @iWorkingString VARCHAR(4000) = NULL SET @IWorkingString = SUBSTRING(@iString, 1, CHARINDEX(@Splitter2, @iString) - 1) INSERT INTO @SResult SELECT @iMId, CASE WHEN @iWorkingString = '' THEN NULL ELSE @iWorkingString END SET @iString = SUBSTRING(@iString, LEN(@iWorkingString) + 2, LEN(@iString)) END SET @OStartLoop = @OStartLoop + 1 END INSERT INTO @Result SELECT MId AS PrimarySplitID, ROW_NUMBER() OVER (PARTITION BY MId ORDER BY Mid, Id) AS SecondarySplitID , SValue FROM @SResult END ELSE BEGIN INSERT INTO @Result SELECT Id AS PrimarySplitID, NULL AS SecondarySplitID, SValue FROM @FResult END RETURN 

用法:

 --FirstSplit SELECT * FROM uft_DoubleSplitter('ValueA=ValueB=ValueC=ValueD==ValueE&ValueA=ValueB=ValueC===ValueE&ValueA=ValueB==ValueD===','&',NULL) --Second Split SELECT * FROM uft_DoubleSplitter('ValueA=ValueB=ValueC=ValueD==ValueE&ValueA=ValueB=ValueC===ValueE&ValueA=ValueB==ValueD===','&','=') 

可能的用法(获取每个分割的第二个值):

 SELECT fn.SValue FROM uft_DoubleSplitter('ValueA=ValueB=ValueC=ValueD==ValueE&ValueA=ValueB=ValueC===ValueE&ValueA=ValueB==ValueD===', '&', '=')AS fn WHERE fn.mid = 2 

在禁用字符的情况下,XML元素经常使用的方法会中断。 这是一种使用这种方法的任何types的字符,即使用分号作为分隔符。

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

 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) 

结果

 ID Part 1 A&B 1 C 1 D 1 E, F 2 "C" & 'D' 2 <C> 2 D 2 E, F 

这是更加狭隘的。 当我这样做的时候,我通常有一个逗号分隔的唯一ID(INT或者BIGINT)列表,我想把它作为一个表来转换,作为内部联接到另一个具有INT或者BIGINT主键的表。 我想要一个内联的表值函数返回,以便我有最有效的连接可能。

示例用法是:

  DECLARE @IDs VARCHAR(1000); SET @IDs = ',99,206,124,8967,1,7,3,45234,2,889,987979,'; SELECT me.Value FROM dbo.MyEnum me INNER JOIN dbo.GetIntIdsTableFromDelimitedString(@IDs) ids ON me.PrimaryKey = ids.ID 

我偷了从http://sqlrecords.blogspot.com/2012/11/converting-delimited-list-to-table.html的想法,将其更改为内联表值并转换为INT。;

 create function dbo.GetIntIDTableFromDelimitedString ( @IDs VARCHAR(1000) --this parameter must start and end with a comma, eg ',123,456,' --all items in list must be perfectly formatted or function will error ) RETURNS TABLE AS RETURN SELECT CAST(SUBSTRING(@IDs,Nums.number + 1,CHARINDEX(',',@IDs,(Nums.number+2)) - Nums.number - 1) AS INT) AS ID FROM [master].[dbo].[spt_values] Nums WHERE Nums.Type = 'P' AND Nums.number BETWEEN 1 AND DATALENGTH(@IDs) AND SUBSTRING(@IDs,Nums.number,1) = ',' AND CHARINDEX(',',@IDs,(Nums.number+1)) > Nums.number; GO 
 ALTER FUNCTION [dbo].func_split_string ( @input as varchar(max), @delimiter as varchar(10) = ";" ) RETURNS @result TABLE ( id smallint identity(1,1), csv_value varchar(max) not null ) AS BEGIN DECLARE @pos AS INT; DECLARE @string AS VARCHAR(MAX) = ''; WHILE LEN(@input) > 0 BEGIN SELECT @pos = CHARINDEX(@delimiter,@input); IF(@pos<=0) select @pos = len(@input) IF(@pos <> LEN(@input)) SELECT @string = SUBSTRING(@input, 1, @pos-1); ELSE SELECT @string = SUBSTRING(@input, 1, @pos); INSERT INTO @result SELECT @string SELECT @input = SUBSTRING(@input, @pos+len(@delimiter), LEN(@input)-@pos) END RETURN END 

你可以使用这个function:

  CREATE FUNCTION SplitString ( @Input NVARCHAR(MAX), @Character CHAR(1) ) RETURNS @Output TABLE ( Item NVARCHAR(1000) ) AS BEGIN DECLARE @StartIndex INT, @EndIndex INT SET @StartIndex = 1 IF SUBSTRING(@Input, LEN(@Input) - 1, LEN(@Input)) <> @Character BEGIN SET @Input = @Input + @Character END WHILE CHARINDEX(@Character, @Input) > 0 BEGIN SET @EndIndex = CHARINDEX(@Character, @Input) INSERT INTO @Output(Item) SELECT SUBSTRING(@Input, @StartIndex, @EndIndex - 1) SET @Input = SUBSTRING(@Input, @EndIndex + 1, LEN(@Input)) END RETURN END GO 

最简单的方法是:

  1. 安装SQL Server 2016
  2. 使用STRING_SPLIT https://msdn.microsoft.com/zh-CN/library/mt684588.aspx

它甚至在快递版:) :)。

这里是一个你可以用作函数的例子,或者你也可以在程序中使用相同的逻辑。 – 从[dbo] .fn_SplitString中select*

 CREATE FUNCTION [dbo].[fn_SplitString] (@CSV VARCHAR(MAX), @Delimeter VARCHAR(100) = ',') RETURNS @retTable TABLE ( [value] VARCHAR(MAX) NULL )AS BEGIN DECLARE @vCSV VARCHAR (MAX) = @CSV, @vDelimeter VARCHAR (100) = @Delimeter; IF @vDelimeter = ';' BEGIN SET @vCSV = REPLACE(@vCSV, ';', '~!~#~'); SET @vDelimeter = REPLACE(@vDelimeter, ';', '~!~#~'); END; SET @vCSV = REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(@vCSV, '&', '&amp;'), '<', '&lt;'), '>', '&gt;'), '''', '&apos;'), '"', '&quot;'); DECLARE @xml XML; SET @xml = '<i>' + REPLACE(@vCSV, @vDelimeter, '</i><i>') + '</i>'; INSERT INTO @retTable SELECT xivalue('.', 'varchar(max)') AS COLUMNNAME FROM @xml.nodes('//i')AS x(i); RETURN; END; 

/ *

回答T-SQL拆分string
根据Andy Robinson和AviG的回答
增强functionref: LEN函数不包括SQL Server中的尾随空格
这个'文件'应该同时作为降价文件和SQL文件

“`

* /

 CREATE FUNCTION dbo.splitstring ( --CREATE OR ALTER @stringToSplit NVARCHAR(MAX) ) RETURNS @returnList TABLE ([Item] NVARCHAR (MAX)) AS BEGIN DECLARE @name NVARCHAR(MAX) DECLARE @pos BIGINT SET @stringToSplit = @stringToSplit + ',' -- this should allow entries that end with a `,` to have a blank value in that "column" WHILE ((LEN(@stringToSplit+'_') > 1)) BEGIN -- `+'_'` gets around LEN trimming terminal spaces. See URL referenced above SET @pos = COALESCE(NULLIF(CHARINDEX(',', @stringToSplit),0),LEN(@stringToSplit+'_')) -- COALESCE grabs first non-null value SET @name = SUBSTRING(@stringToSplit, 1, @pos-1) --MAX size of string of type nvarchar is 4000 SET @stringToSplit = SUBSTRING(@stringToSplit, @pos+1, 4000) -- With SUBSTRING fn (MS web): "If start is greater than the number of characters in the value expression, a zero-length expression is returned." INSERT INTO @returnList SELECT @name --additional debugging parameters below can be added -- + ' pos:' + CAST(@pos as nvarchar) + ' remain:''' + @stringToSplit + '''(' + CAST(LEN(@stringToSplit+'_')-1 as nvarchar) + ')' END RETURN END GO 

/ *

“`

testing用例:请参阅上面引用为“增强function”的URL

SELECT *,LEN(Item+'_')-1 'L' from splitstring('a,,b')

 Item | L --- | --- a | 1 | 0 b | 1 

SELECT *,LEN(Item+'_')-1 'L' from splitstring('a,,')

 Item | L --- | --- a | 1 | 0 | 0 

SELECT *,LEN(Item+'_')-1 'L' from splitstring('a,, ')

 Item | L --- | --- a | 1 | 0 | 1 

SELECT *,LEN(Item+'_')-1 'L' from splitstring('a,, c ')

 Item | L --- | --- a | 1 | 0 c | 3 

* /