I have a large character string (nvarchar(MAX)) that I am trying to split 2 and identify which part of the original string they are.
Example
String:
5;718;0;1071;1.23|0;750;0;997;1.25|0;750;0;997;1.25|0;750;0;997;1.25|0;750;0;997;1.25|0;750;0;997;1.25
I would first need to split the strings based on the '|' character so something like this:
5;718;0;1071;1.23
0;750;0;997;1.25
0;750;0;997;1.25
0;750;0;997;1.25
0;750;0;997;1.25
0;750;0;997;1.25
I would then to split each of those based on the ';' character:
So 5;718;0;1071;1.23 would then split into:
5
718
0
1071
1.23
I know I could do a string_split on the '|' then another string_split on the ';' but that does not maintain an order or identify from which portion of the string the result is split from and I am unfortunately not quite able to get the results I am looking for when trying to use OPENJSON():
Based on the example above I would need a result that could identify that the 718 was from the first group and the 2nd item in said group.
Here is an option that will parse your string and maintain the sequence
Example
Declare #S varchar(max) = '5;718;0;1071;1.23|0;750;0;997;1.25|0;750;0;997;1.25|0;750;0;997;1.25|0;750;0;997;1.25|0;750;0;997;1.25'
Select Seq1 = A.RetSeq
,Val1 = A.RetVal
,Seq2 = B.RetSeq
,Val2 = B.RetVal
From [dbo].[tvf-Str-Parse](#S,'|') A
Cross Apply [dbo].[tvf-Str-Parse](A.RetVal,';') B
Returns
The Function if Interested
CREATE FUNCTION [dbo].[tvf-Str-Parse] (#String varchar(max),#Delimiter varchar(10))
Returns Table
As
Return (
Select RetSeq = row_number() over (order by 1/0)
,RetVal = ltrim(rtrim(B.i.value('(./text())[1]', 'varchar(max)')))
From (Select x = Cast('<x>' + replace((Select replace(#String,#Delimiter,'§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>')+'</x>' as xml).query('.')) as A
Cross Apply x.nodes('x') AS B(i)
);
EDIT - Update for TABLE
Declare #YourTable table (ID int,SomeCol varchar(max))
Insert Into #YourTable values
(1,'5;718;0;1071;1.23|0;750;0;997;1.25|0;750;0;997;1.25|0;750;0;997;1.25|0;750;0;997;1.25|0;750;0;997;1.25')
Select A.ID
,Seq1 = B.RetSeq
,Val1 = B.RetVal
,Seq2 = C.RetSeq
,Val2 = C.RetVal
From #YourTable A
Cross Apply [dbo].[tvf-Str-Parse](SomeCol,'|') B
Cross Apply [dbo].[tvf-Str-Parse](B.RetVal,';') C
here's my old school take:
declare #v nvarchar(max) = N'5;718;0;1071;1.23|0;750;0;997;1.25|0;750;0;997;1.25|0;750;0;997;1.25|0;750;0;997;1.25|0;750;0;997;1.25'
select a.value('.', 'nvarchar(max)') [value], v2.col, rnInternal,ROW_NUMBER() over(partition by rnInternal order by rnInternal, Split.a) rnExternal
from
(
select cast('<M>' + REPLACE(v.[value], ';', '</M><M>') + '</M>' AS XML) as col, rnInternal
from
(
select
a.value('.', 'nvarchar(max)') [value],
ROW_NUMBER() over(order by Split.a) rnInternal
from
(select cast('<M>' + REPLACE(#v, '|', '</M><M>') + '</M>' AS XML) as col) as A
CROSS APPLY A.col.nodes ('/M') AS Split(a)
where
a.value('.', 'nvarchar(max)') <> ''
) v
) v2
CROSS APPLY v2.col.nodes ('/M') AS Split(a)
order by rnInternal, rnExternal
You are right about the STRING_SPLIT() (as is mentioned in the documentation the output rows might be in any order), but you may use a JSON-based approach to get the expected results.
You need to transform the input text into a valid nested JSON array (5;718;0;1071;1.23|0;750;0;997;1.25| into [[5,718,0,1071,1.23],[0,750,0,997,1.25]]) and parse this array with two OPENJSON() calls using a default schema. The result from the OPENJSON() call is a table with columns key, value and type and in case of JSON array the key column returns the index of the element in the specified array:
DECLARE #text nvarchar(max) = N'5;718;0;1071;1.23|0;750;0;997;1.25|0;750;0;997;1.25|0;750;0;997;1.25|0;750;0;997;1.25|0;750;0;997;1.25'
SELECT
CONVERT(int, j1.[key]) + 1 AS id1,
CONVERT(int, j2.[key]) + 1 AS id2,
j2.[value]
FROM OPENJSON(CONCAT('[[', REPLACE(REPLACE(#text, '|', '],['), ';', ','), ']]')) j1
CROSS APPLY OPENJSON(j1.[value]) j2
ORDER BY CONVERT(int, j1.[key]), CONVERT(int, j2.[key])
Result:
id1 id2 value
1 1 5
1 2 718
1 3 0
1 4 1071
1 5 1.23
2 1 0
2 2 750
...
6 1 0
6 2 750
6 3 0
6 4 997
6 5 1.25
If you want to identify the id's of each group and subgroup, you need to add an appropriate WHERE clause:
SELECT
CONVERT(int, j1.[key]) + 1 AS id1,
CONVERT(int, j2.[key]) + 1 AS id2
FROM OPENJSON(CONCAT('[[', REPLACE(REPLACE(#text, '|', '],['), ';', ','), ']]')) j1
CROSS APPLY OPENJSON(j1.[value]) j2
WHERE j2.[value] = '718'
ORDER BY CONVERT(int, j1.[key]), CONVERT(int, j2.[key])
Result:
id1 id2
1 2
Related
I have a requirement where my client wants me to retrieve specific information from a text column
Following is the sample of the same
the student scored following result: class: 6 subject: result: english 80 math 23
science 45
The expected outcome needs to be like -
English Maths Science
80 23 45
I tried using string_split
select value from STRING_SPLIT( (select value from mytable where [student roll number] = 'SCH-01097') , ' ' )
but that only split the value into multiple rows that can't be queried.
I also tried using LTRIM with CHARINDEX approach, but the column have different text and not always organized. the initial text is different most of the time.
can this be done?
edit - I am close but just not there yet
So far I have reached here
SELECT VALUE FROM STRING_SPLIT ((select
substring(value, charindex('Block',value),1000)
from mytable where [rollnumber ] = 'SCH-01097'),' ') WHERE VALUE <> ' '
this gives me everything I need but in a single column
class6:
Subject
result
english
80
math
23
science
45
now how to make it in desired table form?
To maintain the order of the split values this answer uses DelimitedSplit8K. Something like this works.
[Edit] Instead of having specific strings in a CTE, the query now uses 'stems' to map multiple strings to the same class. For example, if English is entered as En it will still be mapped to English.
Table and data
drop table if exists #tTest;
go
create table #tTest(
string Varchar(256));
insert #tTest(string) values
('the student scored following result: class: 6 subject: result: english 80 math 23');
DelimitedSplit8k
CREATE FUNCTION dbo.DelimitedSplit8K
--===== Define I/O parameters
(#pString VARCHAR(8000), #pDelimiter CHAR(1))
--WARNING!!! DO NOT USE MAX DATA-TYPES HERE! IT WILL KILL PERFORMANCE!
RETURNS TABLE WITH SCHEMABINDING AS
RETURN
--===== "Inline" CTE Driven "Tally Table" produces values from 1 up to 10,000...
-- enough to cover VARCHAR(8000)
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
), --10E+1 or 10 rows
E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows
E4(N) AS (SELECT 1 FROM E2 a, E2 b), --10E+4 or 10,000 rows max
cteTally(N) AS (--==== This provides the "base" CTE and limits the number of rows right up front
-- for both a performance gain and prevention of accidental "overruns"
SELECT TOP (ISNULL(DATALENGTH(#pString),0)) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
),
cteStart(N1) AS (--==== This returns N+1 (starting position of each "element" just once for each delimiter)
SELECT 1 UNION ALL
SELECT t.N+1 FROM cteTally t WHERE SUBSTRING(#pString,t.N,1) = #pDelimiter
),
cteLen(N1,L1) AS(--==== Return start and length (for use in substring)
SELECT s.N1,
ISNULL(NULLIF(CHARINDEX(#pDelimiter,#pString,s.N1),0)-s.N1,8000)
FROM cteStart s
)
--===== Do the actual split. The ISNULL/NULLIF combo handles the length for the final element when no delimiter is found.
SELECT ItemNumber = ROW_NUMBER() OVER(ORDER BY l.N1),
Item = SUBSTRING(#pString, l.N1, l.L1)
FROM cteLen l
;
Query
;with
stems_cte(stem, word) as (
select 'English', 'English' union all
select 'En', 'English' union all
select 'Math', 'Math' union all
select 'Maths', 'Math' union all
select 'Science', 'Science'),
splt_cte(string, str_val, ndx, lead_ndx, lead_len, rn) as (
select t.string, ds.Item, charndx.ndx,
lead(charndx.ndx) over (order by ds.ItemNumber),
lead(len(ds.[Item])) over (order by ds.ItemNumber),
ItemNumber
from #tTest t
cross apply dbo.DelimitedSplit8K(t.string, ' ') ds
cross apply (select charindex(ds.Item, t.string, 1) ndx) charndx
where Item <> ' '),
spec_rows_cte(word, ndx, lead_ndx, lead_len, rn) as (
select sp.word, sc.ndx, sc.lead_ndx, sc.lead_len, sc.rn
from splt_cte sc
join stems_cte sp on sc.str_val=sp.stem)
select max(case when src.word='English' then substring(sc.string, src.lead_ndx, src.lead_len) else null end) English,
max(case when src.word='Math' then substring(sc.string, src.lead_ndx, src.lead_len) else null end) Math,
max(case when src.word='Science' then substring(sc.string, src.lead_ndx, src.lead_len) else null end) Science
from splt_cte sc
join spec_rows_cte src on sc.rn=src.rn;
Output
English Math Science
80 23 NULL
You can insert the results from your query into a table variable with an Identity column, then get the next row for each required subset:
declare #tmp table (Id int identity, Value varchar(20))
Insert into #tmp (VALUE)
SELECT VALUE FROM STRING_SPLIT ((select
substring(value, charindex('Block',value),1000)
from mytable where [rollnumber ] = 'SCH-01097'),' ') WHERE VALUE <> ' '
select
English = (Select top 1 Value From #tmp where Id = (Select Id + 1 From #tmp where Value = 'english')),
Math = (Select top 1 Value From #tmp where Id = (Select Id + 1 From #tmp where Value = 'math')),
Science = (Select top 1 Value From #tmp where Id = (Select Id + 1 From #tmp where Value = 'science'))
Output:
I need a function to return the longest sequence of digits in a string, for example:
P0123/99282 returns 99282,
P9-123BB-12339 returns 12339,
12345/54321 returns 12345 (should return first instance when length is the same).
I have developed this but this is very slow, I wonder if there is something faster than this:
DECLARE #str NVARCHAR(40) = N'P0120993/123-AB1239'
DECLARE #x XML
;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 CROSS JOIN (SELECT 1 as t UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1) AS b),
n(Number) AS(SELECT n = ROW_NUMBER() OVER (ORDER BY n) FROM e2)
SELECT #x = CAST(N'<A>'+ REPLACE((SELECT CAST(CAST((
SELECT
CASE WHEN SUBSTRING(#str, Number, 1) like N'[^0-9]' THEN N' ' ELSE SUBSTRING(#str, Number, 1) end
FROM n
WHERE Number <= LEN(#str) FOR XML Path(''))
AS xml) AS nvarchar(max))),N' ',N'</A><A>')+ N'</A>' AS XML)
SELECT TOP 1
case when t.value('.', 'nvarchar(max)') = N'' then null else t.value('.', 'nvarchar(max)') end AS inVal
FROM
#x.nodes('/A') AS x(t)
ORDER BY
LEN(t.value('.', 'nvarchar(max)')) DESC;
EXPLANATION:
The max length of the string I will pass is 40, and what I do is to generate a sequence of numbers from one to forty, extract the Nth character from the string where N is the sequence value but if the character is not a digit then I replace with a white space, then I return the XML as string enlcosing with <A>XXX</A>
to then convert to xml and then query that and return the first item order by it's length desc.
thanks,
While I'm not 100% sure how much better this would be with performance, here is an approach that breaks down the strings into any potential numeric combination and returns the first with the longest length:
DECLARE #foo TABLE(ID varchar(40));
INSERT #foo VALUES('P0123/99282'),('P9-123BB-12339'),('12345/54321');
;WITH NumbersTable AS
(
SELECT TOP (40) n = ROW_NUMBER() OVER (ORDER BY Number)
FROM master.dbo.spt_values
ORDER BY Number
), Results AS
(
SELECT f.Id, SUBSTRING(f.ID, t1.n, t2.n) numericvalues,
row_number() over (partition by f.Id
order by LEN(SUBSTRING(f.ID, t1.n, t2.n)) desc) rn
FROM NumbersTable t1
INNER JOIN #foo AS f
ON t1.n <= LEN(f.ID)
INNER JOIN NumbersTable t2
ON t2.n <= LEN(f.ID)
WHERE SUBSTRING(f.ID, t1.n, t2.n) NOT LIKE '%[^0-9]%'
)
SELECT *
FROM Results
WHERE rn = 1
This creates a numbers table from 1 to 40 (since that was your max length), and then using joins creates every substring variation of the data that have a numeric value using NOT LIKE '%[^0-9]%', and then establishes a row_number based on the len of that substring.
Fiddle Demo
The string is in
#txt nvarchar(max)='2450,10,54,kb2344,kd5433;87766,500,100,ki5332108,ow092827'
And I want output like this:
Id. Val1. Val2. Val3. Val4. Val5.
1. 2450 10 54 kb2344 kd5433.
2. 87766 500 100 ki5332108 ow09287
Can anybody suggest how to do that?
I google it , and found this solution. But it is for two comma separated values but in my case there are five:
DECLARE #Var NVARCHAR(100) = '2450,10,54,kb2344,kd5433;87766,500,100,ki5332108,ow092827'
SELECT LEFT(#Var, CHARINDEX(';', #Var) - 1) ,SUBSTRING(#Var, CHARINDEX(';', #Var) + 1, LEN(#Var)- LEN(LEFT(#Var, CHARINDEX(';', #Var)))- LEN(RIGHT(#Var, CHARINDEX(';', REVERSE(#Var))))) AS [Job] , RIGHT(#Var, CHARINDEX(';', REVERSE(#Var))-1)
Assuming values 1-5. This can easily be done with a little XML in concert with a CROSS APPLY
If the number columns are variable, you would have to go DYNAMIC.
EDIT - Changed to nvarchar
Example
Declare #txt nvarchar(max)='2450,10,54,kb2344,kd5433;87766,500,100,ki5332108,ow092827'
Select ID=A.RetSeq
,B.*
From (
Select RetSeq = Row_Number() over (Order By (Select null))
,RetVal = LTrim(RTrim(B.i.value('(./text())[1]', 'nvarchar(max)')))
From (Select x = Cast('<x>' + replace(#txt ,';','</x><x>')+'</x>' as xml).query('.')) as A
Cross Apply x.nodes('x') AS B(i)
) A
Cross Apply (
Select Val1 = ltrim(rtrim(xDim.value('/x[1]','nvarchar(max)')))
,Val2 = ltrim(rtrim(xDim.value('/x[2]','nvarchar(max)')))
,Val3 = ltrim(rtrim(xDim.value('/x[3]','nvarchar(max)')))
,Val4 = ltrim(rtrim(xDim.value('/x[4]','nvarchar(max)')))
,Val5 = ltrim(rtrim(xDim.value('/x[5]','nvarchar(max)')))
From (Select Cast('<x>' + replace(A.RetVal,',','</x><x>')+'</x>' as xml) as xDim) as B1
) B
Returns
ID Val1 Val2 Val3 Val4 Val5
1 2450 10 54 kb2344 kd5433
2 87766 500 100 ki5332108 ow092827
I have a Description column that contains a long string with varying length and want to extract the content found between every brackets in that string.
I am able to extract the content found in the first pair of brackets but not sure how to tackle cases where more pairs of brackets are found.
I would like to use a SELECT statement only if possible.
My query so far looks like this
SELECT SUBSTRING (Description, CHARINDEX('[', Description)+1, CHARINDEX(']', Description)-CHARINDEX('[', Description)-1)
FROM [MyTable].[Description]
WHERE Description like '%(%'
So for example with the following data
Description (column)
Row 1 blablablablalbaalala (blibliblobloblo) blalblalala (blululublululu)
My query will only return
'blibliblobloblo' but I also want 'blululublululu'
SQL Server 2016 and above
DECLARE #foo varchar(100) = 'lablablablalbaalala (blibliblobloblo) blalblalala (blululublululu)'
SELECT
LEFT(value, CHARINDEX(')', value)-1)
FROM
STRING_SPLIT(#foo, '(')
WHERE
value LIKE '%)%'
If you are open to a TVF (Table-Valued Function).
Tired of extracting strings (charindex,left,right,...) I modified a parse function to accept two non-like delimiters. In your case this would be ( and ).
Example
Declare #YourTable table (ID int,SomeCol varchar(max))
Insert Into #YourTable values
(1,'blablablablalbaalala (blibliblobloblo) blalblalala (blululublululu)')
Select A.ID
,B.*
From #YourTable A
Cross Apply [dbo].[udf-Str-Extract](A.SomeCol,'(',')') B
Returns
ID RetSeq RetPos RetVal
1 1 23 blibliblobloblo
1 2 53 blululublululu
The UDF if Interested
CREATE FUNCTION [dbo].[udf-Str-Extract] (#String varchar(max),#Delimiter1 varchar(100),#Delimiter2 varchar(100))
Returns Table
As
Return (
with cte1(N) As (Select 1 From (Values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) N(N)),
cte2(N) As (Select Top (IsNull(DataLength(#String),0)) Row_Number() over (Order By (Select NULL)) From (Select N=1 From cte1 N1,cte1 N2,cte1 N3,cte1 N4,cte1 N5,cte1 N6) A ),
cte3(N) As (Select 1 Union All Select t.N+DataLength(#Delimiter1) From cte2 t Where Substring(#String,t.N,DataLength(#Delimiter1)) = #Delimiter1),
cte4(N,L) As (Select S.N,IsNull(NullIf(CharIndex(#Delimiter1,#String,s.N),0)-S.N,8000) From cte3 S)
Select RetSeq = Row_Number() over (Order By N)
,RetPos = N
,RetVal = left(RetVal,charindex(#Delimiter2,RetVal)-1)
From (
Select *,RetVal = Substring(#String, N, L)
From cte4
) A
Where charindex(#Delimiter2,RetVal)>1
)
/*
Max Length of String 1MM characters
Declare #String varchar(max) = 'Dear [[FirstName]] [[LastName]], ...'
Select * From [dbo].[udf-Str-Extract] (#String,'[[',']]')
*/
I am working with an employee hierarchy string that is in the format of the following. These number represent employeeID numbers and how the are structured within the company, thus being able to follow the chain of management.
123|456|789|012|345|320
I am trying to take this string of data and turn it into a temp table so I can work with each of the ID's as their own value.
I tried making a function to split the string:
ALTER FUNCTION [dbo].[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))
FROM
Split
)
This however resulted in the following:
Id Data
-------------------
1 123
2 456|7893
3 7893|012|345|
4 012|345|320
5 345|320
6 320
Is there a better way to approach this, maybe not needing a function at all or will it be required to achieve this?
Without a Parse Function
Declare #YourTable table (ID int,IDList varchar(Max))
Insert Into #YourTable values
(1,'123|456|789|012|345|320'),
(2,'123|456')
Select A.ID
,B.*
From #YourTable A
Cross Apply (
Select RetSeq = Row_Number() over (Order By (Select null))
,RetVal = LTrim(RTrim(B.i.value('(./text())[1]', 'varchar(max)')))
From (Select x = Cast('<x>'+ replace((Select A.IDList as [*] For XML Path('')),'|','</x><x>')+'</x>' as xml).query('.')) as A
Cross Apply x.nodes('x') AS B(i)
) B
Returns
ID RetSeq RetVal
1 1 123
1 2 456
1 3 789
1 4 012
1 5 345
1 6 320
2 1 123
2 2 456
OR with the SUPER DUPER Parse (orig source listed below / couple of tweaks)
Select A.ID
,B.*
From #YourTable A
Cross Apply [dbo].[udf-Str-Parse-8K](A.IDList,'|') B
Would Return the same as above
CREATE FUNCTION [dbo].[udf-Str-Parse-8K] (#String varchar(max),#Delimiter varchar(10))
Returns Table
As
Return (
with cte1(N) As (Select 1 From (Values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) N(N)),
cte2(N) As (Select Top (IsNull(DataLength(#String),0)) Row_Number() over (Order By (Select NULL)) From (Select N=1 From cte1 a,cte1 b,cte1 c,cte1 d) A ),
cte3(N) As (Select 1 Union All Select t.N+DataLength(#Delimiter) From cte2 t Where Substring(#String,t.N,DataLength(#Delimiter)) = #Delimiter),
cte4(N,L) As (Select S.N,IsNull(NullIf(CharIndex(#Delimiter,#String,s.N),0)-S.N,8000) From cte3 S)
Select RetSeq = Row_Number() over (Order By A.N)
,RetVal = Substring(#String, A.N, A.L)
From cte4 A
);
--Orginal Source http://www.sqlservercentral.com/articles/Tally+Table/72993/
--Much faster than str-Parse, but limited to 8K
--Select * from [dbo].[udf-Str-Parse-8K]('Dog,Cat,House,Car',',')
--Select * from [dbo].[udf-Str-Parse-8K]('John||Cappelletti||was||here','||')
Edit - Stand Alone
Declare #String varchar(max) = '123|456|789|012|345|320'
Declare #Delim varchar(10) = '|'
Select RetSeq = Row_Number() over (Order By (Select null))
,RetVal = LTrim(RTrim(B.i.value('(./text())[1]', 'varchar(max)')))
From (Select x = Cast('<x>'+ replace((Select #String as [*] For XML Path('')),#Delim,'</x><x>')+'</x>' as xml).query('.')) as A
Cross Apply x.nodes('x') AS B(i)
If you need a string "splitter" the fastest one available for 2012 (pre- 2016) is going to be found here. This will blow the doors off of anything posted thusfar. If your items/tokens are all the same size then an even faster method would be this:
DECLARE #yourstring varchar(8000) = '123|456|789|012|345|320';
WITH E(N) AS (SELECT 1 FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1))t(v)),
iTally(N) AS (SELECT TOP ((LEN(#yourstring)/4)+1) ROW_NUMBER() OVER (ORDER BY (SELECT 1))
FROM e a, e b, e c, e d)
SELECT itemNumber = ROW_NUMBER() OVER (ORDER BY N), item = SUBSTRING(#yourstring, ((N*4)-3), 3)
FROM iTally;
Results:
itemNumber item
-------------------- ----
1 123
2 456
3 789
4 012
5 345
6 320
I write more about this and provide examples of how to put this logic into a function here.
I use this version of the Split function.
CREATE FUNCTION [dbo].[Split]
(
#delimited nvarchar(max),
#delimiter nvarchar(100)
) RETURNS #t TABLE
(
-- Id column can be commented out, not required for sql splitting string
id int identity(1,1), -- I use this column for numbering splitted parts
val nvarchar(max)
)
AS
BEGIN
declare #xml xml
set #xml = N'<root><r>' + replace(#delimited,#delimiter,'</r><r>') + '</r></root>'
insert into #t(val)
select
r.value('.','varchar(max)') as item
from #xml.nodes('//root/r') as records(r)
RETURN
END
You Query would look something like....
SELECT *
FROM TableName t
CROSS APPLY [dbo].[Split](t.EmpIDs, '|')