T-SQL split string
У меня есть столбец SQL Server 2008 R2, содержащий строку, которую мне нужно разделить запятой. Я видел много ответов на StackOverflow, но ни один из них не работает в R2. Я убедился, что у меня есть права выбора на любые примеры функций разделения. Любая помощь очень ценится.
24 ответа:
я использовал этот 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')
вместо рекурсивных CTEs и циклов 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 );
подробнее о функциях разделения, почему (и доказательство этого), в то время как циклы и рекурсивные 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
наконец ожидание закончилось в SQL Server 2016 они ввели функцию Split string:
STRING_SPLIT
select * From STRING_SPLIT ('a,b', ',') cs
все другие методы для разделения строки, такие как XML, Tally table, while loop и т. д.. был взорван этим
самый простой способ сделать это с помощью .
1. Преобразование строки в строки без таблицы
запрос
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
источник Таблица
x-----x--------------------------x | Id | Value | x-----x--------------------------x | 1 | String1,String2,String3 | | 2 | String4,String5,String6 | x-----x--------------------------x
запрос
-- 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
мне нужен был быстрый способ избавиться от
+4
С почтовый индекс.UPDATE #Emails SET ZIPCode = SUBSTRING(ZIPCode, 1, (CHARINDEX('-', ZIPCODE)-1)) WHERE ZIPCode LIKE '%-%'
нет proc... нет УДФ... просто одна маленькая встроенная команда, которая делает то, что должна. Не роскошный, не шикарный.
измените разделитель по мере необходимости и т. д., и он будет работать для чего угодно.
если заменить
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
все функции для разбиения строк, которые используют какой-то цикл (итерации), имеют плохую производительность. Они должны быть заменены на решение на основе набора.
этот код выполняется отлично.
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
мне пришлось написать что-то вроде этого недавно. Вот решение, которое я придумал. Он обобщен для любой строки разделителя, и я думаю, что он будет работать немного лучше:
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
решение с использованием CTE, если кому-то это нужно (кроме меня, кто, очевидно, это сделал, поэтому я написал его).
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
здесь есть правильная версия, но я подумал, что было бы неплохо добавить немного отказоустойчивости, если у них есть конечная запятая, а также сделать так, чтобы вы могли использовать ее не как функцию, а как часть большего фрагмента кода. На всякий случай вы используете его только один раз и не нуждаетесь в функции. Это также для целых чисел (для чего мне это было нужно), поэтому вам, возможно, придется изменить типы данных.
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
Я немного изменил функцию Энди Робинсона . Теперь вы можете выбрать только необходимую часть из возвращаемой таблицы:
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
вот версия, которая может разбиться на шаблон с помощью patindex, простая адаптация поста выше. У меня был случай, когда мне нужно было разделить строку, содержащую несколько разделителей символов.
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','%[/,]%');
результат выглядит так
строках stringb икс год z
лично я использую эту функцию :
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 )
Я разработал двойной сплиттер (принимает два разделенных символа) по запросу здесь. Может иметь некоторое значение в этом потоке, видя его наиболее ссылочным для запросов, связанных с разделением строк.
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 ломается в случае запрещенных символов. Это подход к использованию этого метода с любым типом символа, даже с точкой с запятой в качестве разделителя.
фокус в том, чтобы сначала использовать
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
Если вам нужно быстрое специальное решение для распространенных случаев с минимальным кодом, чем этот рекурсивный CTE two-liner сделает это:
DECLARE @s VARCHAR(200) = ',1,2,,3,,,4,,,,5,' ;WITH a AS (SELECT i=-1, j=0 UNION ALL SELECT j, CHARINDEX(',', @s, j + 1) FROM a WHERE j > i), b AS (SELECT SUBSTRING(@s, i+1, IIF(j>0, j, LEN(@s)+1)-i-1) s FROM a WHERE i >= 0) SELECT * FROM b
либо используйте это как отдельный оператор, либо просто добавьте вышеуказанные CTEs в любой из ваших запросов, и вы сможете присоединиться к результирующей таблице
b
С другими для использования в любых дальнейших выражениях.edit (by Shnugo)
если вы добавите счетчик, вы получите индекс позиции вместе с Список:
DECLARE @s VARCHAR(200) = '1,2333,344,4' ;WITH a AS (SELECT n=0, i=-1, j=0 UNION ALL SELECT n+1, j, CHARINDEX(',', @s, j+1) FROM a WHERE j > i), b AS (SELECT n, SUBSTRING(@s, i+1, IIF(j>0, j, LEN(@s)+1)-i-1) s FROM a WHERE i >= 0) SELECT * FROM b;
результат
n s 1 1 2 2333 3 344 4 4
это более узкоспециализированный. Когда я это делаю, у меня обычно есть разделенный запятыми список уникальных идентификаторов (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, изменив его на In-line табличное значение и приведение как 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
вы можете использовать эту функцию:
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
вот пример, который вы можете использовать в качестве функции или также вы можете поместить ту же логику в процедуру. -- Выберите * из [dbo].fn_SplitString;
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, '&', '&'), '<', '<'), '>', '>'), '''', '''), '"', '"'); DECLARE @xml XML; SET @xml = '<i>' + REPLACE(@vCSV, @vDelimeter, '</i><i>') + '</i>'; INSERT INTO @retTable SELECT x.i.value('.', 'varchar(max)') AS COLUMNNAME FROM @xml.nodes('//i')AS x(i); RETURN; END;
/*
ответ T-SQL split string
На основе ответов от Энди Робинсон и AviG
Расширенные функциональные возможности ссылка: функция LEN не включая конечные пробелы в SQL Server
Этот "файл" должен быть действительным как файл markdown, так и 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
/*
"'
тестовые случаи: см. 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
*/
рекурсивное решение на основе cte
declare @T table (iden int identity, col1 varchar(100)); insert into @T(col1) values ('ROOT/South America/Lima/Test/Test2') , ('ROOT/South America/Peru/Test/Test2') , ('ROOT//South America/Venuzuala ') , ('RtT/South America / ') , ('ROOT/South Americas// '); declare @split char(1) = '/'; select @split as split; with cte as ( select t.iden, case when SUBSTRING(REVERSE(rtrim(t.col1)), 1, 1) = @split then LTRIM(RTRIM(t.col1)) else LTRIM(RTRIM(t.col1)) + @split end as col1, 0 as pos , 1 as cnt from @T t union all select t.iden, t.col1 , charindex(@split, t.col1, t.pos + 1), cnt + 1 from cte t where charindex(@split, t.col1, t.pos + 1) > 0 ) select t1.*, t2.pos, t2.cnt , ltrim(rtrim(SUBSTRING(t1.col1, t1.pos+1, t2.pos-t1.pos-1))) as bingo from cte t1 join cte t2 on t2.iden = t1.iden and t2.cnt = t1.cnt+1 and t2.pos > t1.pos order by t1.iden, t1.cnt;
при всем уважении к @AviG это ошибка бесплатной версии функции, разработанной им, чтобы вернуть все токены в полном объеме.
IF EXISTS (SELECT * FROM sys.objects WHERE type = 'TF' AND name = 'TF_SplitString') DROP FUNCTION [dbo].[TF_SplitString] GO -- ============================================= -- Author: AviG -- Amendments: Parameterize the delimeter and included the missing chars in last token - Gemunu Wickremasinghe -- Description: Tabel valued function that Breaks the delimeted string by given delimeter and returns a tabel having split results -- Usage -- select * from [dbo].[TF_SplitString]('token1,token2,,,,,,,,token969',',') -- 969 items should be returned -- select * from [dbo].[TF_SplitString]('4672978261,4672978255',',') -- 2 items should be returned -- ============================================= CREATE FUNCTION dbo.TF_SplitString ( @stringToSplit VARCHAR(MAX) , @delimeter char = ',' ) RETURNS @returnList TABLE ([Name] [nvarchar] (500)) AS BEGIN DECLARE @name NVARCHAR(255) DECLARE @pos INT WHILE LEN(@stringToSplit) > 0 BEGIN SELECT @pos = CHARINDEX(@delimeter, @stringToSplit) if @pos = 0 BEGIN SELECT @pos = LEN(@stringToSplit) SELECT @name = SUBSTRING(@stringToSplit, 1, @pos) END else BEGIN SELECT @name = SUBSTRING(@stringToSplit, 1, @pos-1) END INSERT INTO @returnList SELECT @name SELECT @stringToSplit = SUBSTRING(@stringToSplit, @pos+1, LEN(@stringToSplit)-@pos) END RETURN END
самый простой способ:
- установить SQL Server 2016
- использовать STRING_SPLIT https://msdn.microsoft.com/en-us/library/mt684588.aspx
Он работает даже в express edition :).