Extract a part of string between multiple delimiters - sql

I have a column with value as '/1064_MyHoldings/ONLINE/Adhoc/Rpt_CompanyCodeElig'
Now, my requirement is to extract every value which is there between the delimeters; '1064 MyHoldings', 'ONLINE', 'Adhoc' etc?
I tried the below code, but it is only taking '1064 MyHoldings'. But I need the other values as well
Can someone please help me here?
WITH yourTable AS (
SELECT '/1064_MyHoldings/ONLINE/Adhoc/Rpt_CompanyCodeElig' AS Path
)
SELECT
CASE WHEN Path LIKE '%/%/%' THEN
SUBSTRING(Path,
CHARINDEX('/', Path) + 1,
CHARINDEX('/', Path, CHARINDEX('/', Path) + 1) - CHARINDEX('/', Path) - 1)
ELSE 'NA' END AS first_component
FROM yourTable;

Use string_split():
select s.value
from t cross apply
string_split(path, '/') s

You can go for recursive search using CTE and split the strings.
WITH yourTable AS (
SELECT '/1064_MyHoldings/ONLINE/Adhoc/Rpt_CompanyCodeElig' AS Path
),
cte_splitTable as
(
SELECT value as val, 1 as lvl
from yourTable
cross apply
string_split(Path,'_')
UNION ALL
SELECT t.value as val, lvl+1 as lvl
from cte_splitTable as c
cross apply
string_split(c.val,'/') as t
where CHARINDEX('/',val) > 0
)
select * from cte_splitTable
where PATINDEX('%[_/]%',val) = 0 and len(val) > 0
+-----------------+
| val |
+-----------------+
| CompanyCodeElig |
| MyHoldings |
| ONLINE |
| Adhoc |
| Rpt |
| 1064 |
+-----------------+

Related

Select Item in String List In SQL Based On Value in Column

I have a SQL table with a column (Grouped_Identifer) that has a string list. The items on the list are separated by commas. There is another column (Position) which has a value. I would like to create or return a column let's call it Position_Identifer that has the portion or item in the Grouped_Identifer which correspond to the value in the Position column.
So we start with the following table:
And would like to end up with a table that looks like the following:
The string list in the Grouped_Identifier column can vary in the number (up to 20) of items.
Unfortunately, SQL Server does not provide the position in string_split(). And, there is no guarantee on the ordering of results from the function.
If the string has no duplicates, you can use charindex() to find the position:
select t.*, s.value
from t outer apply
(select *
from (select s.value,
row_number() over (order by charindex(',' + s.value + ',', ',' + t.gi + ',')) as seqnum
from string_split(t.gi, ',') s
) s
where seqnum = t.pos
) s;
Here is a db<>fiddle.
The new column can be calculated with:
select substring(Grouped_Identifier,Position*15-14,14) as Position_Identifier from yourTable
you can achieve this using string_split if your sql version supports this as follows
with data
as (select '008300000;#61,008300000;#62,008300000;#63' as gi,'1' as pos union all
select '008300000;#61,008300000;#62,008300000;#63' as gi,'2' as pos union all
select '008300000;#61,008300000;#62,008300000;#63' as gi,'3' as pos
)
,cte_d
as(
select *
,row_number() over(partition by pos order by pos) as rn
from data d
cross apply string_split(d.gi,',') x
)
select *
from cte_d
where rn=pos
In older versions you may use this
with data
as (select '008300000;#61,008300000;#62,008300000;#63' as gi,'1' as pos union all
select '008300000;#61,008300000;#62,008300000;#63' as gi,'2' as pos union all
select '008300000;#61,008300000;#62,008300000;#63' as gi,'3' as pos
)
,cte_d
as(
SELECT a.gi
,a.pos
,split.a.value('.', 'VARCHAR(100)') AS split_val
,row_number() over(partition by pos order by pos) as rn
FROM (SELECT pos
,CAST ('<M>' + REPLACE(gi, ',', '</M><M>') + '</M>' AS XML) AS col
,gi
FROM data
) a
CROSS APPLY col.nodes ('/M') AS split(a)
)
select *
from cte_d
where rn=pos
+-------------------------------------------+-----+---------------+----+
| gi | pos | value | rn |
+-------------------------------------------+-----+---------------+----+
| 008300000;#61,008300000;#62,008300000;#63 | 1 | 008300000;#61 | 1 |
| 008300000;#61,008300000;#62,008300000;#63 | 2 | 008300000;#62 | 2 |
| 008300000;#61,008300000;#62,008300000;#63 | 3 | 008300000;#63 | 3 |
+-------------------------------------------+-----+---------------+----+
dbfiddle link
https://dbfiddle.uk/?rdbms=sqlserver_2019&fiddle=1f753ad7c97255351efc704ebdd966c3

Merge 2 table-valued-function into one table with different column

To start, I have a stringSplit() tabled-value function.
Suppose I have these as parameters:
var_1 = 'Apple.jpg,Carrot.png,Fruits.pdf'
var_2 = '9V0I1fab\CvaA5h,IV0asdkas//bVasA,Uasdl00/9asA' //Example base64 formats
How would I go so that after using stringSplit(var_1, ',') and stringSplit(var_2, ',')? It should merge as one table with different columns,
Say:
| name | b64 |
---------------------------------
| Apple.jpg | 9V0I1fab\CvaA5h |
| Carrot.png | IV0asdkas//bVasA |
| Fruits.pdf | Uasdl00/9asA |
Unfortunately, string_split() does not guarantee the ordering of the return values. Nor does it have an option for returning an index number.
If the elements in each string are unique, you can do:
select s1.value as name, s2.value as b64
from (select s.value,
row_number() over (order by charindex(',' + s.value + ',', ',' + #var_1 + ',')) as seqnum
from string_split(#var_1, ',') s
) s1 join
(select s.value,
row_number() over (order by charindex(',' + s.value + ',', ',' + #var_2 + ',')) as seqnum
from string_split(#var_2, ',') s
) s2
on s1.seqnum = s2.seqnum;
Here is a db<>fiddle.

String split on multiple fields to one result

I have a table that looks like the below set as a field for one value:
|---------------------|------------------|------------------|
| Colour | Amount | Size |
|---------------------|------------------|------------------|
| Black,Blue,Green | 1,2,2 | 100,100,100 |
|---------------------|------------------|------------------|
I need to do a string split on each of them and return it in one go.
I've currently got this and works for colour:
SELECT value as colour
FROM [table_name]
CROSS APPLY STRING_SPLIT(colour, ',')
I can't figure out how to do multiple string splits in one go. It should return it as this then:
|---------------------|------------------|------------------|
| Colour | Amount | Size |
|---------------------|------------------|------------------|
| Black | 1 | 100 |
|---------------------|------------------|------------------|
| Blue | 2 | 100 |
|---------------------|------------------|------------------|
| Green | 2 | 100 |
|---------------------|------------------|------------------|
Any help would be great!
Unfortunately, string_split() doesn't provide the option to preserve the order of the substrings it produces. Hence, that is very, very tricky to use in this case.
I prefer a recursive CTE (until the function gets fixed):
with cte as (
select convert(varchar(max), null) as color,
convert(varchar(max), null) as amount,
convert(varchar(max), null) as size,
convert(varchar(max), colors + ',') as rest_colors,
convert(varchar(max), amounts + ',') as rest_amounts ,
convert(varchar(max), sizes + ',') as rest_sizes,
0 as lev
from t
union all
select left(rest_colors, charindex(',', rest_colors) - 1),
left(rest_amounts, charindex(',', rest_amounts) - 1),
left(rest_sizes, charindex(',', rest_sizes) - 1),
stuff(rest_colors, 1, charindex(',', rest_colors), ''),
stuff(rest_amounts, 1, charindex(',', rest_amounts), ''),
stuff(rest_sizes, 1, charindex(',', rest_sizes), ''),
lev + 1
from cte
where rest_colors <> ''
)
select color, amount, size
from cte
where lev > 0;
Here is a db<>fiddle.
As Gordon mentioned, string_split() does not GTD sequence. That said, and if you are open to a Table-Value Function, consider the following where we UNPIVOT your data and then apply a final PIVOT. Note: The RN = ... could be replaced with your own ID (if you have one)
I adjusted the values to illustrate there is a proper sequencing.
Example
;with cte as (
Select RN = row_number() over (order by (select null))
,[Colour]
,[Amount]
,[Size]
From YourTable
)
Select *
From (
Select RN,Item='Colour',B.* From cte Cross Apply [dbo].[tvf-Str-Parse](Colour,',') B
Union All
Select RN,Item='Amount',B.* From cte Cross Apply [dbo].[tvf-Str-Parse](Amount,',') B
Union All
Select RN,Item='Size' ,B.* From cte Cross Apply [dbo].[tvf-Str-Parse](Size ,',') B
) src
Pivot ( max(RetVal) for Item in ([Colour],[Amount],[Size] ) ) pvt
Returns
RN RetSeq Colour Amount Size
1 1 Black 1 100
1 2 Blue 2 200
1 3 Green 3 300
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)
);

Split comma separated values in sql based on condition [duplicate]

This question already has answers here:
T-SQL split string
(27 answers)
Closed 5 years ago.
Hi all i am newbie in SQL i have a table in which there is a column named dilution_name in this column there are values coming in comma separated format like A,B,C etc. also these values may vary like in some row the values are A,B,C and in some case its like A,B,C,D i just want to separate these values and print them in multiple column if there is only 3 comma separated values then there should be 3 values in comma would be written rest should be null
I have tried
select ParsedData.*
from dilution_table mt
cross apply ( select str = mt.dilution_name + ',,' ) f1
cross apply ( select p1 = charindex( ',', str ) ) ap1
cross apply ( select p2 = charindex( ',', str, p1 + 1 ) ) ap2
cross apply ( select p3 = charindex( ',', str, p2 + 2 ) ) ap3
cross apply ( select p4 = charindex( ',', str, p3 + 3 ) ) ap4
cross apply ( select p5 = charindex( ',', str, p4 + 4 ) ) ap5
cross apply ( select p6 = charindex( ',', str, p5 + 5 ) ) ap6
cross apply ( select val1 = substring( str, 1, p1-1 )
, val2 = substring( str, p1+1, p2-p1-1 ),
val3 = substring( str, p2+1, p2-p1-1 ),
val4 = substring( str, p3+1, p2-p1-1 ),
val5 = substring( str, p4+1, p2-p1-1 ),
val6 = substring( str, p5+1, p2-p1-1 ),
val7 = substring( str, p6+1, p2-p1-1 )
) ParsedData
[sample data][1]
sample data
In SQL Server 2016+ you can use string_split() (though it has no ordinal number).
In SQL Server pre-2016, using a CSV Splitter table valued function by Jeff Moden:
declare #str varchar(128) = 'a,b,c,d'
select s.ItemNumber, s.Item
from dbo.delimitedsplit8k(#str,',') s;
rextester demo: http://rextester.com/EGZ24917
returns:
+------------+------+
| ItemNumber | Item |
+------------+------+
| 1 | a |
| 2 | b |
| 3 | c |
| 4 | d |
+------------+------+
To pivot the data after splitting, you can use conditional aggregation like so:
select
v1 = max(case when s.ItemNumber = 1 then s.Item end)
, v2 = max(case when s.ItemNumber = 2 then s.Item end)
, v3 = max(case when s.ItemNumber = 3 then s.Item end)
, v4 = max(case when s.ItemNumber = 4 then s.Item end)
, v5 = max(case when s.ItemNumber = 5 then s.Item end)
from dbo.delimitedsplit8k(#str,',') s;
returns:
+----+----+----+----+------+
| v1 | v2 | v3 | v4 | v5 |
+----+----+----+----+------+
| a | b | c | d | NULL |
+----+----+----+----+------+
splitting strings reference:
Tally OH! An Improved SQL 8K “CSV Splitter” Function - Jeff Moden
Splitting Strings : A Follow-Up - Aaron Bertrand
Split strings the right way – or the next best way - Aaron Bertrand
string_split() in SQL Server 2016 : Follow-Up #1 - Aaron Bertrand
Ordinal workaround for **string_split()** - Solomon Rutzky

Group count values in comma separated field in SQL Server 2008

I have a users table with a column product.
I would like to count how many products are in my table
Users table
+----------+
| Products |
+----------+
| A |
| B |
| A,c |
| C,B,A |
| D |
+----------+
i.e. count for A is: 3, count for B is: 2, count for C is: 2, count for D is: 1
Please try:
SELECT Products, COUNT(Products)
FROM(
SELECT
Split.a.value('.', 'VARCHAR(100)') AS Products
FROM
(
SELECT
CAST ('<M>' + REPLACE(Products, ',', '</M><M>') + '</M>' AS XML) AS CVS
from YourTable
) AS A CROSS APPLY CVS.nodes ('/M') AS Split(a)
)x GROUP BY Products
Use recursive queries - step of recursion splits on 2 columns - l - that contains entry without comma and r - tail of Products, after that make GROUP BY by l column:
WITH expandProd as(
SELECT
CASE
WHEN charindex(',', Products) < 1 THEN Products
ELSE LEFT(Products, charindex(',', Products)-1)
END as l, -- the column without comma
CASE
WHEN charindex(',', Products) < 1 THEN NULL
ELSE RIGHT(Products, LEN(Products) - charindex(',', Products))
END as r -- the column with tail
FROM prods
UNION ALL --recursive query that enters again to itself
SELECT
CASE
WHEN charindex(',', r) < 1 THEN r
ELSE LEFT(r, charindex(',', r)-1)
END as l,
CASE
WHEN charindex(',', r) < 1 THEN NULL
ELSE RIGHT(r, LEN(r) - charindex(',', r))
END as r
FROM expandProd
WHERE r is not null --small optimization
)
SELECT l, COUNT(l)
FROM expandProd
GROUP BY l