我如何分割一个string,所以我可以访问项目x?
使用SQL Server,如何分割一个string,以便可以访问项目x?
采取一个string“你好John Smith”。 我怎样才能分割string的空间和访问索引1应返回“约翰”的项目?
您可能会发现解决scheme在SQL用户定义函数parsing分隔符string有帮助(从代码项目 )。
你可以使用这个简单的逻辑:
Declare @products varchar(200) = '1|20|3|343|44|6|8765' Declare @individual varchar(20) = null WHILE LEN(@products) > 0 BEGIN IF PATINDEX('%|%', @products) > 0 BEGIN SET @individual = SUBSTRING(@products, 0, PATINDEX('%|%', @products)) SELECT @individual SET @products = SUBSTRING(@products, LEN(@individual + '|') + 1, LEN(@products)) END ELSE BEGIN SET @individual = @products SET @products = NULL SELECT @individual END END
我不相信SQL Server有一个内置的拆分function,所以除UDF以外,我唯一知道的其他答案是劫持PARSENAME函数:
SELECT PARSENAME(REPLACE('Hello John Smith', ' ', '.'), 2)
PARSENAME接受一个string并将其分割为句点字符。 它接受一个数字作为第二个参数,该数字指定要返回的string的哪一部分(从后到前工作)。
SELECT PARSENAME(REPLACE('Hello John Smith', ' ', '.'), 3) --return Hello
明显的问题是当string已经包含一个句点。 我仍然认为使用UDF是最好的方法…任何其他的build议?
首先,创build一个函数(使用CTE,通用表expression式不需要临时表)
create function dbo.SplitString ( @str nvarchar(4000), @separator char(1) ) returns table AS return ( with tokens(p, a, b) AS ( select 1, 1, charindex(@separator, @str) union all select p + 1, b + 1, charindex(@separator, @str, b + 1) from tokens where b > 0 ) select p-1 zeroBasedOccurance, substring( @str, a, case when b > 0 then ba ELSE 4000 end) AS s from tokens ) GO
然后,将它用作任何表格(或修改它以适应您现有的存储过程),像这样。
select s from dbo.SplitString('Hello John Smith', ' ') where zeroBasedOccurance=1
更新
以前的版本会inputstring长度超过4000个字符。 这个版本照顾的限制:
create function dbo.SplitString ( @str nvarchar(max), @separator char(1) ) returns table AS return ( with tokens(p, a, b) AS ( select cast(1 as bigint), cast(1 as bigint), charindex(@separator, @str) union all select p + 1, b + 1, charindex(@separator, @str, b + 1) from tokens where b > 0 ) select p-1 ItemIndex, substring( @str, a, case when b > 0 then ba ELSE LEN(@str) end) AS s from tokens ); GO
用法保持不变。
这里的大多数解决scheme使用while循环或recursionCTE。 基于集合的方法将是优越的,我保证:
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
您可以利用数字表格来进行string分析。
创build一个物理数字表:
create table dbo.Numbers (N int primary key); insert into dbo.Numbers select top 1000 row_number() over(order by number) from master..spt_values go
用1000000行创buildtesting表
create table #yak (i int identity(1,1) primary key, array varchar(50)) insert into #yak(array) select 'a,b,c' from dbo.Numbers n cross join dbo.Numbers nn go
创build函数
create function [dbo].[ufn_ParseArray] ( @Input nvarchar(4000), @Delimiter char(1) = ',', @BaseIdent int ) returns table as return ( select row_number() over (order by n asc) + (@BaseIdent - 1) [i], substring(@Input, n, charindex(@Delimiter, @Input + @Delimiter, n) - n) s from dbo.Numbers where n <= convert(int, len(@Input)) and substring(@Delimiter + @Input, n, 1) = @Delimiter ) go
用法(在我的笔记本电脑上输出40米行数)
select * from #yak cross apply dbo.ufn_ParseArray(array, ',', 1)
清理
drop table dbo.Numbers; drop function [dbo].[ufn_ParseArray]
这里的性能并不奇妙,但是调用一百万行以上的函数并不是最好的主意。 如果执行一个string分割多行,我会避免该函数。
这是一个UDF,它将做到这一点。 它将返回一个分隔值的表,还没有尝试所有的场景,但你的例子工作正常。
CREATE FUNCTION SplitString ( -- Add the parameters for the function here @myString varchar(500), @deliminator varchar(10) ) RETURNS @ReturnTable TABLE ( -- Add the column definitions for the TABLE variable here [id] [int] IDENTITY(1,1) NOT NULL, [part] [varchar](50) NULL ) AS BEGIN Declare @iSpaces int Declare @part varchar(50) --initialize spaces Select @iSpaces = charindex(@deliminator,@myString,0) While @iSpaces > 0 Begin Select @part = substring(@myString,0,charindex(@deliminator,@myString,0)) Insert Into @ReturnTable(part) Select @part Select @myString = substring(@mystring,charindex(@deliminator,@myString,0)+ len(@deliminator),len(@myString) - charindex(' ',@myString,0)) Select @iSpaces = charindex(@deliminator,@myString,0) end If len(@myString) > 0 Insert Into @ReturnTable Select @myString RETURN END GO
你会这样称呼它:
Select * From SplitString('Hello John Smith',' ')
编辑:更新解决scheme来处理分隔符len> 1,如下所示:
select * From SplitString('Hello**John**Smith','**')
没有代码,但阅读关于这个权威性的文章。 其他答案中的所有解决scheme都是本文列出的解决scheme: SQL Server 2005及更高版本中的数组和列表
就个人而言,我经常使用数字表格解决scheme,因为它适合我所要做的…
在这里,我发布了一个简单的解决方法
CREATE FUNCTION [dbo].[split]( @delimited NVARCHAR(MAX), @delimiter NVARCHAR(100) ) RETURNS @t TABLE (id INT IDENTITY(1,1), val NVARCHAR(MAX)) AS BEGIN DECLARE @xml XML SET @xml = N'<t>' + REPLACE(@delimited,@delimiter,'</t><t>') + '</t>' INSERT INTO @t(val) SELECT r.value('.','varchar(MAX)') as item FROM @xml.nodes('/t') as records(r) RETURN END
执行像这样的function
select * from dbo.split('Hello John Smith',' ')
在我看来,你们是太复杂了。 只需创build一个CLR UDF并完成它。
using System; using System.Data; using System.Data.SqlClient; using System.Data.SqlTypes; using Microsoft.SqlServer.Server; using System.Collections.Generic; public partial class UserDefinedFunctions { [SqlFunction] public static SqlString SearchString(string Search) { List<string> SearchWords = new List<string>(); foreach (string s in Search.Split(new char[] { ' ' })) { if (!s.ToLower().Equals("or") && !s.ToLower().Equals("and")) { SearchWords.Add(s); } } return new SqlString(string.Join(" OR ", SearchWords.ToArray())); } };
怎么样使用string
和values()
语句?
DECLARE @str varchar(max) SET @str = 'Hello John Smith' DECLARE @separator varchar(max) SET @separator = ' ' DECLARE @Splited TABLE(id int IDENTITY(1,1), item varchar(max)) SET @str = REPLACE(@str, @separator, '''),(''') SET @str = 'SELECT * FROM (VALUES(''' + @str + ''')) AS V(A)' INSERT INTO @Splited EXEC(@str) SELECT * FROM @Splited
达到了结果集。
id item 1 Hello 2 John 3 Smith
我使用frederic的答案,但是这在SQL Server 2005中不起作用
我修改它,我使用select
与union all
,它的工作原理
DECLARE @str varchar(max) SET @str = 'Hello John Smith how are you' DECLARE @separator varchar(max) SET @separator = ' ' DECLARE @Splited table(id int IDENTITY(1,1), item varchar(max)) SET @str = REPLACE(@str, @separator, ''' UNION ALL SELECT ''') SET @str = ' SELECT ''' + @str + ''' ' INSERT INTO @Splited EXEC(@str) SELECT * FROM @Splited
结果集是:
id item 1 Hello 2 John 3 Smith 4 how 5 are 6 you
问题不是关于string拆分方法 ,而是关于如何获得第n个元素 。
这里的所有答案正在使用recursion, CTE
,多个CHARINDEX
, REVERSE
和PATINDEX
,发明函数,调用CLR方法,数字表, CROSS APPLY
等进行某种types的string拆分…大多数答案都包含许多代码行。
但是,如果你真的只想得到第n个元素 ,就可以做到真正的一行 ,没有UDF,甚至没有子select…作为额外的好处: types安全
获取由空间分隔的第2部分:
DECLARE @input NVARCHAR(100)=N'part1 part2 part3'; SELECT CAST(N'<x>' + REPLACE(@input,N' ',N'</x><x>') + N'</x>' AS XML).value('/x[2]','nvarchar(max)')
当然你可以使用variables来定界和定位(使用sql:column
直接从查询的值中获取位置):
DECLARE @dlmt NVARCHAR(10)=N' '; DECLARE @pos INT = 2; SELECT CAST(N'<x>' + REPLACE(@input,@dlmt,N'</x><x>') + N'</x>' AS XML).value('/x[sql:variable("@pos")][1]','nvarchar(max)')
如果你的string可能包含禁止的字符 (尤其是&><
之一),你仍然可以这样做。 首先在string上使用FOR XML PATH
来隐式地用拟合转义序列replace所有禁止的字符。
这是一个非常特殊的情况,如果 – 另外 – 你的分隔符是分号 。 在这种情况下,首先将分隔符replace为“#DLMT#”,并最终将其replace为XML标签:
SET @input=N'Some <, > and &;Other äöü@€;One more'; SET @dlmt=N';'; SELECT CAST(N'<x>' + REPLACE((SELECT REPLACE(@input,@dlmt,'#DLMT#') AS [*] FOR XML PATH('')),N'#DLMT#',N'</x><x>') + N'</x>' AS XML).value('/x[sql:variable("@pos")][1]','nvarchar(max)');
这种模式工作正常,你可以概括
Convert(xml,'<n>'+Replace(FIELD,'.','</n><n>')+'</n>').value('(/n[INDEX])','TYPE') ^^^^^ ^^^^^ ^^^^
注意FIELD , INDEX和TYPE 。
让一些带有标识符的表
sys.message.1234.warning.A45 sys.message.1235.error.O98 ....
然后,你可以写
SELECT Source = q.value('(/n[1])', 'varchar(10)'), RecordType = q.value('(/n[2])', 'varchar(20)'), RecordNumber = q.value('(/n[3])', 'int'), Status = q.value('(/n[4])', 'varchar(5)') FROM ( SELECT q = Convert(xml,'<n>'+Replace(fieldName,'.','</n><n>')+'</n>') FROM some_TABLE ) Q
拆分和铸造所有部件。
我在网上寻找解决scheme,下面的工作对我来说。 Ref 。
你可以这样调用函数:
SELECT * FROM dbo.split('ram shyam hari gopal',' ')
SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO CREATE FUNCTION [dbo].[Split](@String VARCHAR(8000), @Delimiter CHAR(1)) RETURNS @temptable TABLE (items VARCHAR(8000)) AS BEGIN DECLARE @idx INT DECLARE @slice VARCHAR(8000) SELECT @idx = 1 IF len(@String)<1 OR @String IS NULL RETURN WHILE @idx!= 0 BEGIN SET @idx = charindex(@Delimiter,@String) IF @idx!=0 SET @slice = LEFT(@String,@idx - 1) ELSE SET @slice = @String IF(len(@slice)>0) INSERT INTO @temptable(Items) VALUES(@slice) SET @String = RIGHT(@String,len(@String) - @idx) IF len(@String) = 0 break END RETURN END
还有另一个通过delimeter函数得到string的第n部分:
create function GetStringPartByDelimeter ( @value as nvarchar(max), @delimeter as nvarchar(max), @position as int ) returns NVARCHAR(MAX) AS BEGIN declare @startPos as int declare @endPos as int set @endPos = -1 while (@position > 0 and @endPos != 0) begin set @startPos = @endPos + 1 set @endPos = charindex(@delimeter, @value, @startPos) if(@position = 1) begin if(@endPos = 0) set @endPos = len(@value) + 1 return substring(@value, @startPos, @endPos - @startPos) end set @position = @position - 1 end return null end
和用法:
select dbo.GetStringPartByDelimeter ('a;b;c;d;e', ';', 3)
它返回:
c
尝试这个:
CREATE function [SplitWordList] ( @list varchar(8000) ) returns @t table ( Word varchar(50) not null, Position int identity(1,1) not null ) as begin declare @pos int, @lpos int, @item varchar(100), @ignore varchar(100), @dl int, @a1 int, @a2 int, @z1 int, @z2 int, @n1 int, @n2 int, @c varchar(1), @a smallint select @a1 = ascii('a'), @a2 = ascii('A'), @z1 = ascii('z'), @z2 = ascii('Z'), @n1 = ascii('0'), @n2 = ascii('9') set @ignore = '''"' set @pos = 1 set @dl = datalength(@list) set @lpos = 1 set @item = '' while (@pos <= @dl) begin set @c = substring(@list, @pos, 1) if (@ignore not like '%' + @c + '%') begin set @a = ascii(@c) if ((@a >= @a1) and (@a <= @z1)) or ((@a >= @a2) and (@a <= @z2)) or ((@a >= @n1) and (@a <= @n2)) begin set @item = @item + @c end else if (@item > '') begin insert into @t values (@item) set @item = '' end end set @pos = @pos + 1 end if (@item > '') begin insert into @t values (@item) end return end
像这样testing它:
select * from SplitWordList('Hello John Smith')
以下示例使用recursionCTE
更新 18.09.2013
CREATE FUNCTION dbo.SplitStrings_CTE(@List nvarchar(max), @Delimiter nvarchar(1)) RETURNS @returns TABLE (val nvarchar(max), [level] int, PRIMARY KEY CLUSTERED([level])) AS BEGIN ;WITH cte AS ( SELECT SUBSTRING(@List, 0, CHARINDEX(@Delimiter, @List + @Delimiter)) AS val, CAST(STUFF(@List + @Delimiter, 1, CHARINDEX(@Delimiter, @List + @Delimiter), '') AS nvarchar(max)) AS stval, 1 AS [level] UNION ALL SELECT SUBSTRING(stval, 0, CHARINDEX(@Delimiter, stval)), CAST(STUFF(stval, 1, CHARINDEX(@Delimiter, stval), '') AS nvarchar(max)), [level] + 1 FROM cte WHERE stval != '' ) INSERT @returns SELECT REPLACE(val, ' ','' ) AS val, [level] FROM cte WHERE val > '' RETURN END
演示SQLFiddle
Alter Function dbo.fn_Split ( @Expression nvarchar(max), @Delimiter nvarchar(20) = ',', @Qualifier char(1) = Null ) RETURNS @Results TABLE (id int IDENTITY(1,1), value nvarchar(max)) AS BEGIN /* USAGE Select * From dbo.fn_Split('apple pear grape banana orange honeydew cantalope 3 2 1 4', ' ', Null) Select * From dbo.fn_Split('1,abc,"Doe, John",4', ',', '"') Select * From dbo.fn_Split('Hello 0,"&""&&&&', ',', '"') */ -- Declare Variables DECLARE @X xml, @Temp nvarchar(max), @Temp2 nvarchar(max), @Start int, @End int -- HTML Encode @Expression Select @Expression = (Select @Expression For XML Path('')) -- Find all occurences of @Delimiter within @Qualifier and replace with
***
While PATINDEX('%' + @Qualifier + '%', @Expression) > 0 AND Len(IsNull(@Qualifier, '')) > 0 BEGIN Select — Starting character position of @Qualifier @Start = PATINDEX('%' + @Qualifier + '%', @Expression), — @Expression starting at the @Start position @Temp = SubString(@Expression, @Start + 1, LEN(@Expression)-@Start+1), — Next position of @Qualifier within @Expression @End = PATINDEX('%' + @Qualifier + '%', @Temp) – 1, — The part of Expression found between the @Qualifiers @Temp2 = Case When @End < 0 Then @Temp Else Left(@Temp, @End) End, — New @Expression @Expression = REPLACE(@Expression, @Qualifier + @Temp2 + Case When @End < 0 Then '' Else @Qualifier End, Replace(@Temp2, @Delimiter, '
***
') ) END — Replace all occurences of @Delimiter within @Expression with '</fn_Split><fn_Split>' — And convert it to XML so we can select from it SET @X = Cast('<fn_Split>' + Replace(@Expression, @Delimiter, '</fn_Split><fn_Split>') + '</fn_Split>' as xml) — Insert into our returnable table replacing '
***
' back to @Delimiter INSERT @Results SELECT "Value" = LTRIM(RTrim(Replace(C.value('.', 'nvarchar(max)'), '
***
', @Delimiter))) FROM @X.nodes('fn_Split') as X(C) — Return our temp table RETURN END
我知道这是一个古老的问题,但我认为有人可以从我的解决scheme中受益。
select SUBSTRING(column_name,1,CHARINDEX(' ',column_name,1)-1) ,SUBSTRING(SUBSTRING(column_name,CHARINDEX(' ',column_name,1)+1,LEN(column_name)) ,1 ,CHARINDEX(' ',SUBSTRING(column_name,CHARINDEX(' ',column_name,1)+1,LEN(column_name)),1)-1) ,SUBSTRING(SUBSTRING(column_name,CHARINDEX(' ',column_name,1)+1,LEN(column_name)) ,CHARINDEX(' ',SUBSTRING(column_name,CHARINDEX(' ',column_name,1)+1,LEN(column_name)),1)+1 ,LEN(column_name)) from table_name
SQL FIDDLE
优点:
- 它将所有3个子string分隔符用“'分开。
- 一定不要使用while循环,因为它会降低性能。
- 不需要枢轴,因为所有的结果子string将显示在一行中
限制:
- 你必须知道总数。 (子串)。
注意 :解决scheme可以将子串提供给N.
为了克服限制,我们可以使用下面的参考 。
但是,上述解决scheme再次不能用在表格中(我无法使用它)。
再次,我希望这个解决scheme可以帮助一个人。
更新:如果logging> 50000,则不build议使用LOOPS
,否则会降低性能
几乎所有其他的答案分裂代码正在取代被拆分的string,浪费CPU周期,并执行不必要的内存分配。
我介绍了一个更好的方法来做一个string拆分: http : //www.digitalruby.com/split-string-sql-server/
这里是代码:
SET NOCOUNT ON -- You will want to change nvarchar(MAX) to nvarchar(50), varchar(50) or whatever matches exactly with the string column you will be searching against DECLARE @SplitStringTable TABLE (Value nvarchar(MAX) NOT NULL) DECLARE @StringToSplit nvarchar(MAX) = 'your|string|to|split|here' DECLARE @SplitEndPos int DECLARE @SplitValue nvarchar(MAX) DECLARE @SplitDelim nvarchar(1) = '|' DECLARE @SplitStartPos int = 1 SET @SplitEndPos = CHARINDEX(@SplitDelim, @StringToSplit, @SplitStartPos) WHILE @SplitEndPos > 0 BEGIN SET @SplitValue = SUBSTRING(@StringToSplit, @SplitStartPos, (@SplitEndPos - @SplitStartPos)) INSERT @SplitStringTable (Value) VALUES (@SplitValue) SET @SplitStartPos = @SplitEndPos + 1 SET @SplitEndPos = CHARINDEX(@SplitDelim, @StringToSplit, @SplitStartPos) END SET @SplitValue = SUBSTRING(@StringToSplit, @SplitStartPos, 2147483647) INSERT @SplitStringTable (Value) VALUES(@SplitValue) SET NOCOUNT OFF -- You can select or join with the values in @SplitStringTable at this point.
使用TVF
和recursionCTE
纯集合解决scheme。 您可以JOIN
和APPLY
此function到任何数据集。
create function [dbo].[SplitStringToResultSet] (@value varchar(max), @separator char(1)) returns table as return with r as ( select value, cast(null as varchar(max)) [x], -1 [no] from (select rtrim(cast(@value as varchar(max))) [value]) as j union all select right(value, len(value)-case charindex(@separator, value) when 0 then len(value) else charindex(@separator, value) end) [value] , left(r.[value], case charindex(@separator, r.value) when 0 then len(r.value) else abs(charindex(@separator, r.[value])-1) end ) [x] , [no] + 1 [no] from r where value > '') select ltrim(x) [value], [no] [index] from r where x is not null; go
用法:
select * from [dbo].[SplitStringToResultSet]('Hello John Smith', ' ') where [index] = 1;
结果:
value index ------------- John 1
您可以在不需要函数的情况下在SQL中拆分string:
DECLARE @bla varchar(MAX) SET @bla = 'BED40DFC-F468-46DD-8017-00EF2FA3E4A4,64B59FC5-3F4D-4B0E-9A48-01F3D4F220B0,A611A108-97CA-42F3-A2E1-057165339719,E72D95EA-578F-45FC-88E5-075F66FD726C' -- http://stackoverflow.com/questions/14712864/how-to-query-values-from-xml-nodes SELECT x.XmlCol.value('.', 'varchar(36)') AS val FROM ( SELECT CAST('<e>' + REPLACE(@bla, ',', '</e><e>') + '</e>' AS xml) AS RawXml ) AS b CROSS APPLY b.RawXml.nodes('e') x(XmlCol);
如果您需要支持任意string(使用xml特殊字符)
DECLARE @bla NVARCHAR(MAX) SET @bla = '<html>unsafe & safe Utf8CharsDon''tGetEncoded ÄöÜ - "Conex"<html>,Barnes & Noble,abc,def,ghi' -- http://stackoverflow.com/questions/14712864/how-to-query-values-from-xml-nodes SELECT x.XmlCol.value('.', 'nvarchar(MAX)') AS val FROM ( SELECT CAST('<e>' + REPLACE((SELECT @bla FOR XML PATH('')), ',', '</e><e>') + '</e>' AS xml) AS RawXml ) AS b CROSS APPLY b.RawXml.nodes('e') x(XmlCol);
从SQL Server 2016开始我们string_split
DECLARE @string varchar(100) = 'Richard, Mike, Mark' SELECT value FROM string_split(@string, ',')
那么,我不是那么简单,但这里是我用来将逗号分隔的inputvariables分割成单个值的代码,并将其放入一个表variables。 我相信你可以稍微修改这个基于空间分割,然后对该表variables做一个基本的SELECT查询来获得你的结果。
-- Create temporary table to parse the list of accounting cycles. DECLARE @tblAccountingCycles table ( AccountingCycle varchar(10) ) DECLARE @vchAccountingCycle varchar(10) DECLARE @intPosition int SET @vchAccountingCycleIDs = LTRIM(RTRIM(@vchAccountingCycleIDs)) + ',' SET @intPosition = CHARINDEX(',', @vchAccountingCycleIDs, 1) IF REPLACE(@vchAccountingCycleIDs, ',', '') <> '' BEGIN WHILE @intPosition > 0 BEGIN SET @vchAccountingCycle = LTRIM(RTRIM(LEFT(@vchAccountingCycleIDs, @intPosition - 1))) IF @vchAccountingCycle <> '' BEGIN INSERT INTO @tblAccountingCycles (AccountingCycle) VALUES (@vchAccountingCycle) END SET @vchAccountingCycleIDs = RIGHT(@vchAccountingCycleIDs, LEN(@vchAccountingCycleIDs) - @intPosition) SET @intPosition = CHARINDEX(',', @vchAccountingCycleIDs, 1) END END
这个概念几乎是一样的。 另一种select是利用SQL Server 2005本身的.NET兼容性。 你基本上可以自己写一个简单的.NET方法来分割string,然后将其作为存储过程/函数公开。
这是我为了获得一个string中的特定标记而做的事情。 (在MSSQL 2008中testing)
首先,创build以下function:(发现在这里 :
CREATE FUNCTION dbo.SplitStrings_Moden ( @List NVARCHAR(MAX), @Delimiter NVARCHAR(255) ) RETURNS TABLE WITH SCHEMABINDING AS RETURN WITH E1(N) AS ( SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1), E2(N) AS (SELECT 1 FROM E1 a, E1 b), E4(N) AS (SELECT 1 FROM E2 a, E2 b), E42(N) AS (SELECT 1 FROM E4 a, E2 b), cteTally(N) AS (SELECT 0 UNION ALL SELECT TOP (DATALENGTH(ISNULL(@List,1))) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E42), cteStart(N1) AS (SELECT t.N+1 FROM cteTally t WHERE (SUBSTRING(@List,tN,1) = @Delimiter OR tN = 0)) SELECT Item = SUBSTRING(@List, s.N1, ISNULL(NULLIF(CHARINDEX(@Delimiter,@List,s.N1),0)-s.N1,8000)) FROM cteStart s;
和
create FUNCTION dbo.getToken ( @List NVARCHAR(MAX), @Delimiter NVARCHAR(255), @Pos int ) RETURNS varchar(max) as begin declare @returnValue varchar(max); select @returnValue = tbl.Item from ( select ROW_NUMBER() over (order by (select null)) as id, * from dbo.SplitStrings_Moden(@List, @Delimiter) ) as tbl where tbl.id = @Pos return @returnValue end
then you can use it like that:
select dbo.getToken('1111_2222_3333_', '_', 1)
which return 1111
Recursive CTE solution with server pain, test it
MS SQL Server 2008 Schema Setup :
create table Course( Courses varchar(100) ); insert into Course values ('Hello John Smith');
Query 1 :
with cte as ( select left( Courses, charindex( ' ' , Courses) ) as a_l, cast( substring( Courses, charindex( ' ' , Courses) + 1 , len(Courses ) ) + ' ' as varchar(100) ) as a_r, Courses as a, 0 as n from Course t union all select left(a_r, charindex( ' ' , a_r) ) as a_l, substring( a_r, charindex( ' ' , a_r) + 1 , len(a_R ) ) as a_r, cte.a, cte.n + 1 as n from Course t inner join cte on t.Courses = cte.a and len( a_r ) > 0 ) select a_l, n from cte --where N = 1
Results :
| A_L | N | |--------|---| | Hello | 0 | | John | 1 | | Smith | 2 |
while similar to the xml based answer by josejuan, i found that processing the xml path only once, then pivoting was moderately more efficient:
select ID, [3] as PathProvidingID, [4] as PathProvider, [5] as ComponentProvidingID, [6] as ComponentProviding, [7] as InputRecievingID, [8] as InputRecieving, [9] as RowsPassed, [10] as InputRecieving2 from ( select id,message,d.* from sysssislog cross apply ( SELECT Item = yivalue('(./text())[1]', 'varchar(200)'), row_number() over(order by yi) as rn FROM ( SELECT x = CONVERT(XML, '<i>' + REPLACE(Message, ':', '</i><i>') + '</i>').query('.') ) AS a CROSS APPLY x.nodes('i') AS y(i) ) d WHERE event = 'OnPipelineRowsSent' ) as tokens pivot ( max(item) for [rn] in ([3],[4],[5],[6],[7],[8],[9],[10]) ) as data
ran in 8:30
select id, tokens.value('(/n[3])', 'varchar(100)')as PathProvidingID, tokens.value('(/n[4])', 'varchar(100)') as PathProvider, tokens.value('(/n[5])', 'varchar(100)') as ComponentProvidingID, tokens.value('(/n[6])', 'varchar(100)') as ComponentProviding, tokens.value('(/n[7])', 'varchar(100)') as InputRecievingID, tokens.value('(/n[8])', 'varchar(100)') as InputRecieving, tokens.value('(/n[9])', 'varchar(100)') as RowsPassed from ( select id, Convert(xml,'<n>'+Replace(message,'.','</n><n>')+'</n>') tokens from sysssislog WHERE event = 'OnPipelineRowsSent' ) as data
ran in 9:20
CREATE FUNCTION [dbo].[fnSplitString] ( @string NVARCHAR(MAX), @delimiter CHAR(1) ) RETURNS @output TABLE(splitdata NVARCHAR(MAX) ) BEGIN DECLARE @start INT, @end INT SELECT @start = 1, @end = CHARINDEX(@delimiter, @string) WHILE @start < LEN(@string) + 1 BEGIN IF @end = 0 SET @end = LEN(@string) + 1 INSERT INTO @output (splitdata) VALUES(SUBSTRING(@string, @start, @end - @start)) SET @start = @end + 1 SET @end = CHARINDEX(@delimiter, @string, @start) END RETURN END
AND USE IT
select *from dbo.fnSplitString('Querying SQL Server','')
if anyone wants to get only one part of the seperatured text can use this
select * from fromSplitStringSep('Word1 wordr2 word3',' ')
CREATE function [dbo].[SplitStringSep] ( @str nvarchar(4000), @separator char(1) ) returns table AS return ( with tokens(p, a, b) AS ( select 1, 1, charindex(@separator, @str) union all select p + 1, b + 1, charindex(@separator, @str, b + 1) from tokens where b > 0 ) select p-1 zeroBasedOccurance, substring( @str, a, case when b > 0 then ba ELSE 4000 end) AS s from tokens )
I devoloped this,
declare @x nvarchar(Max) = 'ali.veli.deli.'; declare @item nvarchar(Max); declare @splitter char='.'; while CHARINDEX(@splitter,@x) != 0 begin set @item = LEFT(@x,CHARINDEX(@splitter,@x)) set @x = RIGHT(@x,len(@x)-len(@item) ) select @item as item, @x as x; end
the only attention you should is dot '.' that end of the @x is always should be there.