I have a data set (approx 900k lines) where I need to split a data based on a '(' or ')'. For Example
Table A data:-
> Vendor Is_Active
ABC(1263) 1
efgh (187 1
pqrs 890ag) 1
xyz 1
lmno(488) 1
(9867-12) 1
Output
ID Name
1263 ABC
187 efgh
890ag pqrs
xyz
488 lmno
9867-12
I tried query
SELECT
vendor,
CASE WHEN vendor LIKE '%(%' OR vendor LIKE '%)%'
THEN REPLACE(REPLACE(RIGHT(Vendor, charindex(' ', reverse(vendor)) - 1),'(',''),')','')
END AS 'test'
FROM
tableA
Error :- Msg 536, Level 16, State 4, Line 13 Invalid length parameter
passed to the RIGHT function.
You can remove chars ( and ) then search for number occurrence. Check this query
declare #t table (
vendor varchar(100)
)
insert into #t values
('ABC(1263)')
,('efgh (187')
,('pqrs 890ag)')
,('xyz')
,('lmno(488)')
,('(9867-12)')
select
ID = case when p = 0 then '' else substring(v, p, len(v)) end
, Name = case when p = 0 then v else left(v, p - 1) end
from
#t
cross apply (select v = replace(replace(vendor, '(', ''), ')', '')) q1
cross apply (select p = patindex('%[0-9]%', v)) q2
Output
ID Name
---------------
1263 ABC
187 efgh
890ag pqrs
xyz
488 lmno
9867-12
Hmmm. I'm thinking:
select v.*, v2.name,
replace(stuff(v.x, 1, len(v2.name) + 1, ''), ')', '') as id
from (values ('ABC(1263)'), ('abc'), ('(1234)')) v(x) cross apply
(values (left(v.x, charindex('(', v.x + '(') - 1))) v2(name);
I find apply useful for repetitive string operations.
SELECT
(CASE WHEN Vendor LIKE '%(%)' THEN SUBSTRING(Vendor,CHARINDEX('(',Vendor)+1,CHARINDEX(')',Vendor)-CHARINDEX('(',Vendor)-1)
WHEN Vendor LIKE '%(%' THEN SUBSTRING(Vendor,CHARINDEX('(',Vendor)+1,LEN(Vendor))
WHEN Vendor LIKE '%)%' THEN SUBSTRING(Vendor,CHARINDEX(' ',Vendor)+1,(CHARINDEX(')',Vendor)-CHARINDEX(' ',Vendor))-1)
ELSE ''
END )AS ID ,
(CASE WHEN Vendor LIKE '%(%)' THEN SUBSTRING(Vendor,1,CHARINDEX('(',Vendor)-1)
WHEN Vendor LIKE '%(%' THEN SUBSTRING(Vendor,1,CHARINDEX('(',Vendor)-1)
WHEN Vendor LIKE '%)%' THEN SUBSTRING(Vendor,1,CHARINDEX(' ',Vendor))
ELSE Vendor END ) AS Name
FROM Table A
Related
CREATE TABLE testcheck
(
id int,
colname nvarchar(255)
)
INSERT INTO testcheck (id, colname)
VALUES (1, 'xxx xxxxx (xx11111) | yyyy yyyyyy yyyyyyyy (yy1111)')
SELECT
SUBSTRING(RIGHT(Colname, LEN(Colname) - CHARINDEX('|', Colname)),
CHARINDEX('(', RIGHT(Colname, LEN(Colname) - CHARINDEX('|', Colname))) + 1,
CHARINDEX(')', Colname) - CHARINDEX('(', RIGHT(Colname, LEN(Colname) - CHARINDEX('|', Colname))) - 1) Colname
FROM
dbo.testcheck
My value is 'xxx xxxxx (xx11111) | yyyy yyyyyy yyyyyyyy (yy1111)' not sure why I get this error:
Invalid length parameter passed to the LEFT or SUBSTRING function
I want to extract text between brackets my complete code is as below
select case when LEN(colname)=0 then '' else case when SUBSTRING(Colname,CHARINDEX('(',Colname)+1,CHARINDEX(')',Colname)-CHARINDEX('(',Colname)-1)=
SUBSTRING(RIGHT(Colname,LEN(Colname)-CHARINDEX('|',Colname)),CHARINDEX('(',RIGHT(Colname,LEN(Colname)-CHARINDEX('|',Colname)))+1,CHARINDEX(')',Colname)-CHARINDEX('(',RIGHT(Colname,LEN(Colname)-CHARINDEX('|',Colname)))-1)
then SUBSTRING(Colname,CHARINDEX('(',Colname)+1,CHARINDEX(')',Colname)-CHARINDEX('(',Colname)-1)
else CONCAT(SUBSTRING(Colname,CHARINDEX('(',Colname)+1,CHARINDEX(')',Colname)-CHARINDEX('(',Colname)-1),',',SUBSTRING(RIGHT(Colname,LEN(Colname)-CHARINDEX('|',Colname)),CHARINDEX('(',RIGHT(Colname,LEN(Colname)-CHARINDEX('|',Colname)))+1,CHARINDEX(')',RIGHT(Colname,LEN(Colname)-CHARINDEX('|',Colname)))-CHARINDEX('(',RIGHT(Colname,LEN(Colname)-CHARINDEX('|',Colname)))-1)) end
END
from dbo.testcheck
This works for most values but doesnt work for few values
I usually handle this just by appending
SUBSTRING(RIGHT(Colname, LEN(Colname) - CHARINDEX('|', Colname || '|')),
CHARINDEX('(', RIGHT(Colname, LEN(Colname) - CHARINDEX('|', Colname || '|'))) + 1,
CHARINDEX(')', Colname) - CHARINDEX('(', RIGHT(Colname, LEN(Colname) - CHARINDEX('|', Colname || '|'))) - 1) Colname
You haven't specified what the code is supposed to be doing. But this should at least answer your question and eliminate the error.
Please try the following solutions.
SQL #1
It is for SQL Server 2017 onwards.
-- DDL and sample data population, start
DECLARE #tbl TABLE (id INT IDENTITY PRIMARY KEY, tokens NVARCHAR(255));
INSERT INTO #tbl (tokens) VALUES
('xxx xxxxx (xx11111) | yyyy yyyyyy yyyyyyyy (yy1111)');
-- DDL and sample data population, end
WITH rs AS
(
SELECT ID, TRIM([value]) AS [row]
FROM #tbl
CROSS APPLY STRING_SPLIT(tokens, '|')
)
SELECT ID, TRIM('()' FROM value) AS Result
FROM rs
CROSS APPLY STRING_SPLIT(row, SPACE(1))
WHERE LEFT(value,1) = '(';
SQL #2
It is for SQL Server 2012 onwards.
;WITH rs as
(
SELECT *
FROM #tbl
CROSS APPLY (SELECT TRY_CAST('<root><r><![CDATA[' +
REPLACE(tokens, SPACE(1), ']]></r><r><![CDATA[') +
']]></r></root>' AS XML)) AS t1(c)
)
SELECT ID --, c
, SUBSTRING(token, 2, LEN(token) - 2) AS Result
FROM rs
CROSS APPLY c.nodes('/root/r[substring(./text()[1],1,1)="("]/text()') AS t2(x)
CROSS APPLY (VALUES (x.value('.', 'VARCHAR(20)'))) AS t3(token);
Output
+----+---------+
| ID | Result |
+----+---------+
| 1 | xx11111 |
| 1 | yy1111 |
+----+---------+
how to split one value from sql?
EX: column SQL
Now, I want split to
Code:
declare #ND nvarchar(max)= N'AA (AA11) [37100]'
select substring(#ND,1,patindex('%[0-9]%',#ND)-2) as Name
,substring(#ND,patindex('%[0-9]%',#ND),len(#ND)) as Number
Declare #t table (fullname varchar(50))
insert into #t values ('AA [1111]')
insert into #t values ('BB(15CC) [2222]')
select
fullname,
substring(fullname,1,CHARINDEX('[',fullname)-1) Name,
replace(substring(fullname,CHARINDEX('[',fullname)+1,len(fullname)),']','') as number
from #t
In your sample data, the last 6 characters are always square braces with the number. If this is always the case, you can use simpler logic:
select left(fullname, len(fullname) - 7),
left(right(fullname, 5), 4)
from #t;
Here is a db<>fiddle.
For a more general case, I would also want to handle the situation where there is no [ in the string without generating an error:
select left(fullname, charindex('[', fullname + '[') - 1),
replace(stuff(fullname, 1, charindex('[', fullname + '['), ''), ']', '')
from #t;
Here is another db<>fiddle.
You can do that by using CHARINDEX(), REPLACE(), LEN() and SUBSTRING() functions as
SELECT Str FullName,
REPLACE(Str, SUBSTRING(Str, CHARINDEX('[', Str), CHARINDEX(']', Str)), '') Name,
SUBSTRING(Str, CHARINDEX('[', Str) + 1, LEN(Str) - CHARINDEX('[', Str)-1) Number
FROM (VALUES('BBB(15CC)[222]'), ('AA[1111]')) T(Str);
OR
;WITH CTE AS
(
SELECT Str FullName,
LEFT(Str, CHARINDEX('[', Str) -1) Name
FROM (VALUES('BBB(15CC)[222]'), ('AA[1111]')) T(Str)
)
SELECT FullName,
Name,
REPLACE(REPLACE(FullName, Name + '[', ''), ']', '') Number
FROM CTE;
Returns:
+----------------+-----------+--------+
| FullName | Name | Number |
+----------------+-----------+--------+
| BBB(15CC)[222] | BBB(15CC) | 222 |
| AA[1111] | AA | 1111 |
+----------------+-----------+--------+
Using Xml conversion we can get the expected result
Declare #t table (fullname varchar(50))
insert into #t values ('AA [1111]')
insert into #t values ('BB(15CC) [2222]')
SELECT DISTINCT Fullname,
Split.a.value('/S[1]','nvarchar(100)') AS Name,
Split.a.value('/S[2]','nvarchar(100)') AS Number
FROM
(
SELECT Fullname,
CAST('<S>'+REPLACE(REPLACE(REPLACE(Fullname,' ','</S><S>')+'</S>','[',''),']','') AS XML ) AS FullnameData
FROM #t
)AS A
CROSS APPLY FullnameData.nodes('S') AS Split(a)
Result
Fullname Name Number
----------------------------------------
AA [1111] AA 1111
BB(15CC) [2222] BB(15CC) 2222
I need a query which would extract the first second and third word of a string.
I have approximately 5 words in each row and I need only the first three words out of 5 in the same row (1 row). Example "ATV BDSG 232 continue with other words".
I need only the first three words together in one row (in the same row) like "ATV BDSG 232" as a first row. The table has about 1000 rows and at the end of it I should have 1000 rows again but each row should contain only the first three words of the string.
I found a query which works fine for extracting first two like "ATV BDSG" discussed in stack overflow. The query is
"SELECT SUBSTRING(field1, 0, CHARINDEX(' ', field1, CHARINDEX(' ', field1, 0)+1))
FROM Table"
Can we derive this for extracting first three words?
Thanks in advance
If you don't want to create a dedicated function, you can use successive CROSS APPLYs:
SELECT
T.s,
FirstSpace.i,
SecondSpace.j,
ThirdSpace.k,
CASE
When ThirdSpace.k > 0 THEN LEFT(T.s, Thirdspace.k - 1)
ELSE T.S
END AS Phrase
FROM t
CROSS APPLY (SELECT CHARINDEX(' ', T.s, 1)) AS FirstSpace(i)
CROSS APPLY (SELECT CHARINDEX(' ', T.S, FirstSpace.i + 1)) AS SecondSpace(j)
CROSS APPLY (SELECT CHARINDEX(' ', T.s, SecondSpace.j + 1)) AS ThirdSpace(k)
gives you the results you need:
| s | i | j | k | phrase |
|----------------------------------------|---|---|----|------------------|
| ATV BDSG 232 Continue with other words | 4 | 9 | 13 | ATV BDSG 232 |
Things are easy, SQL Server provide STRING_SPLIT() function make that too easy
DECLARE #Var VARCHAR(100) = 'ATV BDSG 232 Continue with other words';
SELECT Word
FROM
(
SELECT Value AS Word,
ROW_NUMBER()OVER(ORDER BY (SELECT NULL)) RN
FROM STRING_SPLIT(#Var, ' ')
) T
WHERE RN <= 3;
But since you are working on 2012 version, you need to define your own function.
You can also take the hard way, first you need to get the first word, then replace it with '' and get the second word, then do the same for the 3rd word as
DECLARE #Var VARCHAR(100) = 'ATV BDSG 232 Continue with other words';
WITH FW AS
(
SELECT LEFT(#Var, CHARINDEX(' ', #Var)) FirstWord
),
SW AS
(
SELECT LEFT(REPLACE(#Var, FirstWord, ''),
CHARINDEX(' ', REPLACE(#Var, FirstWord, ''))) SecondWord
FROM FW
)
SELECT FirstWord,
SecondWord,
LEFT(REPLACE(REPLACE(V, FirstWord, ''), SecondWord, ''),
CHARINDEX(' ', REPLACE(REPLACE(V, FirstWord, ''), SecondWord, ''))
) ThirdWord
FROM
(
SELECT *, #Var V
FROM FW CROSS APPLY SW
) T
Demo
UPDATE
If you want to select the three first words then simply
SELECT SUBSTRING(Str, 0, CHARINDEX(' ', Str, CHARINDEX(' ', Str, CHARINDEX(' ', Str, 0)+1)+1)) Words
FROM Strings
Demo
--make some test data
declare #test as nvarchar(100) = 'my test string for words';
select 1 id, cast('my test string for words' as nvarchar(max)) word into #test;
insert #test (id,word) values (2,'a b c d e f g hhh yyyyyy') ;
insert #test (id,word) values (3,' a required test string d e f g hhh yyyyyy') ;
insert #test (id,word) values (4,'a quick test') ;
insert #test (id,word) values (5,'a test') ;
insert #test (id,word) values (6,'last') ;
--break up letters, count the first 3 words
;WITH CTE AS (SELECT 1 x, substring(#test,1,1) charx
UNION ALL
SELECT X + 1, substring(#test,x + 1,1) from CTE WHERE x < len(#test)
)
select * from cte c3 where (SELECT count(0) cnt FROM CTE c1 JOIN CTE c2 on c1.x <= c3.x and c1.x + 1 = c2.x and c1.charx =' ' and c2.charx != ' ') < 3
;WITH tabx as (select id, cast(ltrim(word) as nvarchar(max)) 'word' from #test), --do some ltrim
CTE AS (
SELECT id, 1 x, substring(word,1,1) charx from tabx
UNION ALL
SELECT t.id, c.X + 1, substring(t.word,x + 1,1)
from tabx t
JOIN CTE c on c.id = t.id and x < len(t.word)
),
disj as
(select * from cte c3 where
(SELECT count(0) cnt
FROM CTE c1
JOIN CTE c2 on c1.id = c3.id and c1.id = c2.id and c1.x <= c3.x and c1.x + 1 = c2.x and c1.charx =' ' and c2.charx != ' '
) < 3
),
rj as
(select disj.id,disj.x, disj.charx z
from disj
where disj.x = 1
UNION ALL
select d.id, d.x, r.z + d.charx
FROM rj r
join disj d on r.id = d.id and r.x + 1 = d.x
)
select *
from rj r1
cross apply (select max(r2.x) TheRow from rj r2 where r1.id = r2.id) dq
where r1.x = dq.TheRow
order by r1.id;
--delete test data
drop table #test
/* This is not perfect - but interesting */
declare #t table (fullname varchar(100))
insert #t values('Mr Jones'),('Mrs Amy smith'),('Jim Smith'),('Dr Harry Web '),('Paul Fred andrew jones')
select fullname,
a.value as a ,
b.Value as b,
c.Value as c,
d.Value as d,
e.Value as e,
f.value as f
from #t
outer apply (select top 1 value from STRING_SPLIT(fullname, ' ')) a
outer apply (select top 1 value from STRING_SPLIT(fullname, ' ') where value not in (a.value )) b
outer apply (select top 1 value from STRING_SPLIT(fullname, ' ') where value not in (a.value,b.value ) ) c
outer apply (select top 1 value from STRING_SPLIT(fullname, ' ') where value not in (a.value,b.value,c.value )) d
outer apply (select top 1 value from STRING_SPLIT(fullname, ' ') where value not in (a.value,b.value,c.value,d.value) ) e
outer apply (select top 1 value from STRING_SPLIT(fullname, ' ') where value not in (a.value,b.value ,c.value,d.value,e.value) ) f
To Select First Word -
Select top 1 Ltrim(Rtrim(value)) FROM STRING_SPLIT(#input,' ')
To Select Only Second Word -
Select Ltrim(Rtrim(value)) from STRING_SPLIT(#input,' ') Order by (Select NULL) OFFSET 1 ROWS FETCH NEXT 1 ROWS ONLY
I have a data source which contains data in delimited fields which exist in a staging area in SQL Server. I'd like to transform this data into many rows so it is easier to work with. This differs from the numerous other questions and answers on similar topics in that I have multiple fields where this delimited data exists. Here is an example of what my data looks like:
ID | Field | Value
---+-------+------
1 | a,b,c | 1,2,3
2 | a,c | 5,2
And this is the desired output:
ID | Field | Value
---+-------+------
1 | a | 1
1 | b | 2
1 | c | 3
2 | a | 5
2 | c | 2
My code so far uses the XML parsing method like the one mentioned here: Turning a Comma Separated string into individual rows I needed to extend it to join each field to its corresponding value which I have done by generating a row_number for each ID and then matching based on the ID and this row_number.
My issue is that it is painfully slow so I wondered if anyone has any more performant methods?
select
[Value].ID, [Field], [Value]
from
(select
A.ID, Split.a.value('.', 'varchar(100)') as [Value],
row_number() over (partition by ID order by Split.a) as RowNumber
from
(select
ID, cast('<M>' + replace([Value], ',', '</M><M>') + '</M>' as xml) as [Value]
from
#source_table
where
[Field] not like '%[<>&%]%' and [Value] not like '%[<>&%]%') as A
cross apply
[Value].nodes ('/M') as Split(a)
) [Value]
inner join
(
select
A.ID, Split.a.value('.', 'varchar(100)') as [Field],
row_number() over (partition by A.ID order by Split.a) as RowNumber
from
(select
ID, cast('<M>' + replace([Field], ',', '</M><M>') + '</M>' as xml) as [Field]
from
#source_table
where
[Field] not like '%[<>&%]%' and [Value] not like '%[<>&%]%') as A
cross apply
[Field].nodes ('/M') as Split(a)
) [Field] on [Value].ID = [Field].ID and [Value].RowNumber = [Field].RowNumber
Here is an approach using the splitter from Jeff Moden. http://www.sqlservercentral.com/articles/Tally+Table/72993/ One nice feature of that splitter is that it returns the ordinal position of each element so you can use it for joins and such.
Starting with some data.
declare #Something table
(
ID int
, Field varchar(50)
, Value varchar(50)
)
insert #Something values
(1, 'a,b,c', '1,2,3')
, (2, 'a,c', '5,2')
;
Since you have two sets of delimited data you will be forced to split this for each set of delimited values. Here is how you can leverage this splitter to accomplish this.
with Fields as
(
select *
from #Something s
cross apply dbo.DelimitedSplit8K(s.Field, ',') f
)
, Value as
(
select *
from #Something s
cross apply dbo.DelimitedSplit8K(s.Value, ',') v
)
select f.ID
, Field = f.Item
, Value = v.Item
from Fields f
join Value v on v.ItemNumber = f.ItemNumber and v.ID = f.ID
If at all possible it would be best to see if you can change whatever process it is that is populating your source data so it is normalized and not delimited because it is a pain to work with.
Basing on #Gordon Linoff s query here another recursive cte:
DECLARE #t TABLE(
ID int
,Field VARCHAR(MAX)
,Value VARCHAR(MAX)
)
INSERT INTO #t VALUES
(1, 'a,b,c', '1,2,3')
,(2, 'a,c', '5,2')
,(3, 'x', '7');
with cte as (
select ID
,SUBSTRING(Field, 1, CASE WHEN CHARINDEX(',', Field) > 0 THEN CHARINDEX(',', Field)-1 ELSE LEN(Field) END) AS Field
,SUBSTRING(Value, 1, CASE WHEN CHARINDEX(',', Value) > 0 THEN CHARINDEX(',', Value)-1 ELSE LEN(Value) END) AS Value
,SUBSTRING(Field, CASE WHEN CHARINDEX(',', Field) > 0 THEN CHARINDEX(',', Field)+1 ELSE 1 END, LEN(Field)-CASE WHEN CHARINDEX(',', Field) > 0 THEN CHARINDEX(',', Field) ELSE 0 END) as field_list
,SUBSTRING(Value, CASE WHEN CHARINDEX(',', Value) > 0 THEN CHARINDEX(',', Value)+1 ELSE 1 END, LEN(Value)-CASE WHEN CHARINDEX(',', Value) > 0 THEN CHARINDEX(',', Value) ELSE 0 END) as value_list
,0 as lev
from #t
WHERE CHARINDEX(',', Field) > 0
UNION ALL
select ID
,SUBSTRING(field_list, 1, CASE WHEN CHARINDEX(',', field_list) > 0 THEN CHARINDEX(',', field_list)-1 ELSE LEN(field_list) END) AS Field
,SUBSTRING(value_list, 1, CASE WHEN CHARINDEX(',', value_list) > 0 THEN CHARINDEX(',', value_list)-1 ELSE LEN(value_list) END) AS Value
,CASE WHEN CHARINDEX(',', field_list) > 0 THEN SUBSTRING(field_list, CHARINDEX(',', field_list)+1, LEN(field_list)-CHARINDEX(',', field_list)) ELSE '' END as field_list
,CASE WHEN CHARINDEX(',', value_list) > 0 THEN SUBSTRING(value_list, CHARINDEX(',', value_list)+1, LEN(value_list)-CHARINDEX(',', value_list)) ELSE '' END as value_list
,lev + 1
from cte
WHERE LEN(field_list) > 0
)
select ID, Field, Value
from cte
UNION ALL
SELECT ID, Field, Value
FROM #t
WHERE CHARINDEX(',', Field) = 0
ORDER BY ID, Field
OPTION (MAXRECURSION 0)
One method is a recursive CTE:
with cte as (
select id, cast(NULL as varchar(max)) as field, cast(NULL as varchar(max)) as value, field as field_list, value as value_list, 0 as lev
from t
union all
select id, left(field_list, charindex(',', field_list + ',') - 1),
left(value_list, charindex(',', value_list + ',') - 1),
substring(field_list, charindex(',', field_list + ',') + 1, len(field_list)),
substring(value_list, charindex(',', value_list + ',') + 1, len(value_list)),
1 + lev
from cte
where field_list <> '' and value_list <> ''
)
select *
from cte
where lev > 0;
Here is an example of how it works.
I wonder if anyone can help me.
I need a tsql function to split a given value such as:
1) 00 Not specified
3) 01-05 Global WM&BB | Operations
2) 02-05-01 Global WM&BB | Operations | Operations n/a
I need to get a result like this:
cat1 cat1descr cat2 cat2descr cat3 cat3descr
----------------------------------------------------------------
00 Not especified null null null null
01 Global WM&BB 05 Operations null null
01 Global WM&BB 05 Operations 01 Operations n/a
Result will have always 6 columns
select funcX('00 Not specified');
cat1 cat1descr cat2 cat2descr cat3 cat3descr
----------------------------------------------------------------
00 Not especified null null null null
This will work on SQL Server 2005 and SQL Server 2008. I have assumed that your first sequence of digits is fixed to 2-digit groups of 1, 2, or 3. You can do this with fewer cascading CTEs but I find the SUBSTRING/CHARINDEX/LEN syntax can quickly become very difficult to read and debug.
DECLARE #foo TABLE
(
bar VARCHAR(4000)
);
INSERT #foo(bar) SELECT '00 Not specified'
UNION ALL SELECT '01-05 Global WM&BB | Operations'
UNION ALL SELECT '02-05-01 Global WM&BB | Operations | Operations n/a';
WITH split1 AS
(
SELECT
n = SUBSTRING(bar, 1, CHARINDEX(' ', bar)-1),
w = SUBSTRING(bar, CHARINDEX(' ', bar)+1, LEN(bar)),
rn = ROW_NUMBER() OVER (ORDER BY bar)
FROM
#foo
),
split2 AS
(
SELECT
rn,
cat1 = LEFT(n, 2),
wl = RTRIM(SUBSTRING(w, 1,
COALESCE(NULLIF(CHARINDEX('|', w), 0)-1, LEN(w)))),
wr = LTRIM(SUBSTRING(w, NULLIF(CHARINDEX('|', w),0) + 1, LEN(w))),
cat2 = NULLIF(SUBSTRING(n, 4, 2), ''),
cat3 = NULLIF(SUBSTRING(n, 7, 2), '')
FROM
split1
),
split3 AS
(
SELECT
rn,
cat1descr = wl,
cat2descr = RTRIM(SUBSTRING(wr, 1,
COALESCE(NULLIF(CHARINDEX('|', wr), 0)-1, LEN(wr)))),
cat3descr = LTRIM(SUBSTRING(wr,
NULLIF(CHARINDEX('|', wr),0) + 1, LEN(wr)))
FROM
split2
)
SELECT
s2.cat1, s3.cat1descr,
s2.cat2, s3.cat2descr,
s2.cat3, s3.cat3descr
FROM split2 AS s2
INNER JOIN split3 AS s3
ON s2.rn = s3.rn;
You can do this using PatIndex and SubString
If #In is the value of the string you're trying to parse, then try this:
Select
Case When firstDash = 0 Then zz.sKey
Else left(zz.sKey, FirstDash-1) End cat1,
Ltrim(RTrim(Case When firstPipe = 0 Then zz.Vals
Else Left(zz.Vals, firstPipe -1) End)) ca1Desc,
Case When firstDash = 0 Then Null
When secondDash = 0
Then SubString(zz.sKey, FirstDash+1, Len(zz.skey))
Else SubString(zz.sKey, FirstDash+1, secondDash-firstDash-1) End cat2,
Ltrim(RTrim(Case When firstPipe = 0 Then Null
When secondPipe = 0
Then SubString(zz.Vals, firstPipe+1, Len(zz.Vals))
Else SubString(zz.Vals, firstPipe+1,
secondPipe-firstPipe-1) End)) cat2Desc,
Case When secondDash > 0
Then Substring(zz.sKey, secondDash+1, len(sKey)-seconddash) End cat3,
Ltrim(RTrim(Case When secondPipe > 0
Then Substring(zz.Vals, secondPipe+1,
len(Vals)-secondPipe) End)) cat3Desc
From (Select Z.sKey, Z.Vals,
charIndex('-', Z.skey) firstDash,
charIndex('-', Z.skey, 1 + charIndex('-', Z.skey)) secondDash,
charIndex('|', Z.Vals) firstPipe,
charIndex('|', Z.Vals, 1 + charIndex('|', Z.Vals)) secondPipe
From (Select Left(#In, CharIndex(' ', #In)-1) skey,
substring(#In, CharIndex(' ', #In)+ 1, Len(#In)) vals) Z) ZZ