У меня есть столбец SQL Server 2008 R2, содержащий строку, которую мне нужно разделить запятой. Я видел много ответов на StackOverflow, но ни один из них не работает в R2. Я убедился, что у меня есть права выбора для любых примеров функции разделения. Любая помощь очень ценится.
sql
sql-server
tsql
sql-server-2008
split
Ли Гриндон
источник
источник
mdq.RegexSplit
надстройке Master Data Services есть функция, которая может помочь. Конечно, стоит изучить .Ответы:
Я использовал этот 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')
источник
select * from dbo.splitstring('')
Кто-нибудь рассматривал подход, более основанный на наборах, вместо рекурсивных CTE и циклов while? Обратите внимание, что эта функция была написана для вопроса, который основан на SQL Server 2008 и запятой в качестве разделителя . В SQL Server 2016 и выше (и на уровне совместимости 130 и выше)
STRING_SPLIT()
это лучший вариант .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
Если вы хотите избежать ограничения длины строки <= количество строк в
sys.all_columns
(9 980 дюймов вmodel
SQL Server 2017; намного больше в ваших собственных пользовательских базах данных), вы можете использовать другие подходы для получения чисел, например построение собственной таблицы чисел . Вы также можете использовать рекурсивный CTE в тех случаях, когда вы не можете использовать системные таблицы или создавать свои собственные:CREATE FUNCTION dbo.SplitString ( @List nvarchar(max), @Delim nvarchar(255) ) RETURNS TABLE WITH SCHEMABINDING AS RETURN ( WITH n(n) AS (SELECT 1 UNION ALL SELECT n+1 FROM n WHERE n <= LEN(@List)) SELECT [Value] = SUBSTRING(@List, n, CHARINDEX(@Delim, @List + @Delim, n) - n) FROM n WHERE n <= LEN(@List) AND SUBSTRING(@Delim + @List, n, DATALENGTH(@Delim)/2) = @Delim ); GO
Но вам нужно будет добавить
OPTION (MAXRECURSION 0)
(илиMAXRECURSION <longest possible string length if < 32768>
) к внешнему запросу, чтобы избежать ошибок с рекурсией для строк> 100 символов. Если это тоже не лучшая альтернатива, см. Этот ответ, как указано в комментариях.(Кроме того, должен быть разделитель
NCHAR(<=1228)
. Почему все еще не понятно.)Подробнее о функциях разделения, почему (и доказательство того, что) циклы while и рекурсивные CTE не масштабируются, а также о лучших альтернативах при разделении строк, поступающих с уровня приложения:
источник
sys.all_objects
меньше, чем количество символов во входной строке, то строка будет усечена, и значения пропадут. Посколькуsys.all_objects
он используется просто как хитрость для генерации строк, есть лучшие способы сделать это, например, этот ответ .Наконец, ожидание закончилось, в SQL Server 2016 они ввели функцию разделения строк:
STRING_SPLIT
select * From STRING_SPLIT ('a,b', ',') cs
Все другие методы разделения строки, такие как XML, таблица Tally, цикл while и т. Д., Были уничтожены этой
STRING_SPLIT
функцией.Вот отличная статья со сравнением производительности: Сюрпризы производительности и предположения: STRING_SPLIT
источник
Самый простой способ сделать это - использовать
XML
формат.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
источник
@String
содержит запрещенные символы ... Я только что опубликовал ответ, чтобы решить эту проблему.Мне нужен был быстрый способ избавиться
+4
от почтового индекса .UPDATE #Emails SET ZIPCode = SUBSTRING(ZIPCode, 1, (CHARINDEX('-', ZIPCODE)-1)) WHERE ZIPCode LIKE '%-%'
Нет процедуры ... нет UDF ... всего одна маленькая встроенная команда, которая делает то, что должна. Не модно, не элегантно.
При необходимости измените разделитель и т. Д., И он будет работать для чего угодно.
источник
если вы замените
с участием
вы можете удалить эту последнюю вставку после цикла 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
источник
+1
к,SELECT @pos = LEN(@stringToSplit)
похоже, решает эту проблему. Однако функцияSELECT @stringToSplit = SUBSTRING(@stringToSplit, @pos+1, LEN(@stringToSplit)-@pos)
вернется,Invalid length parameter passed to the LEFT or SUBSTRING function
если вы не добавите еще+1
и третий параметр SUBSTRING. или вы можете заменить это назначение наSET @stringToSplit = SUBSTRING(@stringToSplit, @pos+1, 4000) --MAX len of nvarchar is 4000
Все функции для разделения строк, использующие какой-либо цикл (итерации), имеют плохую производительность. Их следует заменить комплексным решением.
Этот код отлично работает.
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
источник
@List
содержит запрещенные символы ... Я только что опубликовал ответ, чтобы решить эту проблему.Часто используемый подход с элементами 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)
Результат
источник
Недавно мне пришлось написать что-то подобное. Вот решение, которое я придумал. Он обобщен для любой строки разделителя, и я думаю, что он будет работать немного лучше:
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
источник
Это более узко. Когда я это делаю, у меня обычно есть список уникальных идентификаторов (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
источник
Здесь есть правильная версия, но я подумал, что было бы неплохо добавить немного отказоустойчивости на случай, если у них есть конечная запятая, а также сделать так, чтобы вы могли использовать ее не как функцию, а как часть большего фрагмента кода . На всякий случай, если вы используете его только один раз и вам не нужна функция. Это также для целых чисел (для чего мне это было нужно), поэтому вам, возможно, придется изменить типы данных.
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
источник
SET @StringToSeperate = @StringToSeperate+','
непосредственно передWHILE
циклом, я думаю, вы могли бы удалить блок «добавить последнее значение». Смотрите также мой sol'n на githubЯ немного изменил функцию + Andy Robinson. Теперь вы можете выбрать только нужную деталь из таблицы возврата:
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
источник
Если вам нужно быстрое специальное решение для общих случаев с минимумом кода, то этот рекурсивный двухстрочный алгоритм CTE сделает это:
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
Либо используйте это как отдельный оператор, либо просто добавьте указанные выше CTE к любому из ваших запросов, и вы сможете объединить полученную таблицу
b
с другими для использования в любых дальнейших выражениях.редактировать (Шнуго)
Если вы добавите счетчик, вы получите индекс позиции вместе со списком:
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;
Результат:
источник
Я беру xml-маршрут, оборачивая значения в элементы (M, но все работает):
declare @v nvarchar(max) = '100,201,abcde' select a.value('.', 'varchar(max)') from (select cast('<M>' + REPLACE(@v, ',', '</M><M>') + '</M>' AS XML) as col) as A CROSS APPLY A.col.nodes ('/M') AS Split(a)
источник
вот версия, которая может быть разделена по шаблону с помощью 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','%[/,]%');
результат выглядит так
струна строкаb x y 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
источник
Рекурсивное решение на основе 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
источник
Это основано на ответе Энди Робертсона, мне нужен был разделитель, отличный от запятой.
CREATE FUNCTION dbo.splitstring ( @stringToSplit nvarchar(MAX), @delim nvarchar(max)) RETURNS @returnList TABLE ([value] [nvarchar] (MAX)) AS BEGIN DECLARE @value NVARCHAR(max) DECLARE @pos INT WHILE CHARINDEX(@delim, @stringToSplit) > 0 BEGIN SELECT @pos = CHARINDEX(@delim, @stringToSplit) SELECT @value = SUBSTRING(@stringToSplit, 1, @pos - 1) INSERT INTO @returnList SELECT @value SELECT @stringToSplit = SUBSTRING(@stringToSplit, @pos + LEN(@delim), LEN(@stringToSplit) - @pos) END INSERT INTO @returnList SELECT @stringToSplit RETURN END GO
И использовать это:
SELECT * FROM dbo.splitstring('test1 test2 test3', ' ');
(Проверено на SQL Server 2008 R2)
РЕДАКТИРОВАТЬ: правильный тестовый код
источник
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;
источник
@vCSV
содержит запрещенные символы ... Я только что опубликовал ответ, чтобы решить эту проблему./ *
*/ 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
* /
источник
Самый простой способ:
Работает даже в экспресс-редакции :).
источник