SQL - Split Concatenated String into Columns after ; - sql

I really need help with SQL. I have a database where you can find values like: abc;defg;hi and this is in just 1 column. So I wanna create 2 more colums which inserts the splited values.
For example:
Before:
Value01: abc;defg;hi
After:
Value01: abc,
Value02: defg,
Value03: hi
--Another Example would be this:--
Before:
Value01: abcd;efg;
After:
Value01: abcd,
Value02: efg,
Value03: null
So always 3 new values were created. I hope you understand my question!
Greetings

You can use string_split():
select nullif(s.value, '')
from string_split(#value, ';') s

Use WITH
WITH CTE
AS
(
SELECT [xml_val] = CAST('<t>' + REPLACE(SomeValue,';','</t><t>') + '</t>' AS XML)
FROM #yourTable
)
SELECT [SomeValue] = col.value('.','VARCHAR(100)')
FROM CTE
CROSS APPLY [xml_val].nodes('/t') CA(col)

Here is a solution that should be fairly performant:
--==== Sample Data
Declare #testData Table (Value01 varchar(255));
Insert Into #testData (Value01)
Values ('abc;defg;hi'), ('abcd;efg;');
--==== Solution using sample data from above
Select *
, Value01 = substring(td.Value01, 1, p1.pos - 2)
, Value02 = substring(td.Value01, p1.pos, p2.pos - p1.pos - 1)
, Value03 = substring(td.Value01, p2.pos, p3.pos - p2.pos - 1)
From #testData td
Cross Apply (Values (concat(td.Value01, ';;;'))) As v(Value01) --Make sure we have delimiters
Cross Apply (Values (charindex(';', v.Value01, 1) + 1)) As p1(pos)
Cross Apply (Values (charindex(';', v.Value01, p1.pos) + 1)) As p2(pos)
Cross Apply (Values (charindex(';', v.Value01, p2.pos) + 1)) As p3(pos) --End of string/element
You could also create a function that parses out the elements and then just call the function using cross apply.

Related

How do I combine a substring and trim right in SQL

I am trying to extract the data between two underscore characters. In some situations, the 2nd underscore may not exist.
MyFld
P_36840
U_216137
C_203134_H
C_203134_W
I tried this:
substring(i.[MyFld],
CHARINDEX ('_',i.[MyFld])+1,len(i.[MyFld])
-CHARINDEX ('_',i.[MyFld])
) [DerivedPrimaryKey]
And I get this:
DerivedPrimaryKey
36840
216137
203134_H
203134_W
https://dbfiddle.uk/uPKC6oX4
I want to remove the second underscore and data that follows it. I'm trying to combine it with a trim right, but I'm unsure where to start.
How can I do this?
We can start by simplifying what you have so far. I will also add enough to make this a complete query, so we can see it in context for later steps:
SELECT
right(i.MyFld, len(i.MyFld) - charindex('_', i.MyFld)) [DerivedPrimaryKey]
FROM I
With this much done, we can now use it as the source for removing the trailing portion of the field:
SELECT
reverse(substring(reverse(step1)
, charindex('_', reverse(step1))+1
, len(step1)
)) [DerivedPrimaryKey]
FROM (
SELECT right(i.MyFld, len(i.MyFld) - charindex('_', i.MyFld)) [step1]
FROM I
) T
Notice the layer of nesting. You can, of course, remove the nesting, but it means replicating the entire inner expression every time you see step1 (good thing I took the time to simplify it):
SELECT
reverse(substring(reverse(right(i.MyFld, len(i.MyFld) - charindex('_', i.MyFld)))
, charindex('_', reverse(right(i.MyFld, len(i.MyFld) - charindex('_', i.MyFld))))+1
, len(right(i.MyFld, len(i.MyFld) - charindex('_', i.MyFld)))
))
FROM I
And now back to just the expression:
reverse(substring(reverse(right(i.MyFld, len(i.MyFld) - charindex('_', i.MyFld)))
, charindex('_', reverse(right(i.MyFld, len(i.MyFld) - charindex('_', i.MyFld))))+1
, len(right(i.MyFld, len(i.MyFld) - charindex('_', i.MyFld)))
))
See it work here:
https://dbfiddle.uk/nFO4Vwhm
There is also this alternate expression that saves one function call:
left( right(i.MyFld,len(i.MyFld)-charindex('_',i.MyFld)),
coalesce(
nullif(
charindex('_',
right(i.MyFld,len(i.MyFld)-charindex('_',i.MyFld))
) -1, -1,
),
len( right(i.MyFld,len(i.MyFld)-charindex('_',i.MyFld)) )
)
)
Just a two more options. One using parsename() provided your data does not have more than 4 segments. The second using a JSON array
Example
Declare #YourTable Table ([MyFld] varchar(50)) Insert Into #YourTable Values
('P_36840')
,('U_216137')
,('C_203134_H')
,('C_203134_W')
Select *
,UsingParseName = reverse(parsename(reverse(replace(MyFld,'_','.')),2))
,UsingJSONValue = json_value('["'+replace(MyFld,'_','","')+'"]','$[1]')
From #You
Results
MyFld UsingParseName UsingJSONValue
P_36840 36840 36840
U_216137 216137 216137
C_203134_H 203134 203134
C_203134_W 203134 203134
We can do this:
Declare #testData Table ([MyFld] varchar(50));
Insert Into #testData (MyFld)
Values ('P_36840')
, ('U_216137')
, ('C_203134_H')
, ('C_203134_W');
Select *
, second_element = substring(v.MyFld, p1.pos, p2.pos - p1.pos - 1)
From #testData As td
Cross Apply (Values (concat(td.MyFld, '__'))) As v(MyFld) -- Make sure we have at least 2 delimiters
Cross Apply (Values (charindex('_', v.MyFld, 1) + 1)) As p1(pos) -- First Position
Cross Apply (Values (charindex('_', v.MyFld, p1.pos) + 1)) As p2(pos) -- Second Position
If you actually have a fixed number of characters in the first element, then it could be simplified to:
Select *
, second_element = substring(v.MyFld, 3, charindex('_', v.MyFld, 4) - 3)
From #testData td
Cross Apply (Values (concat(td.MyFld, '_'))) As v(MyFld)
Often I try to fake out SQL if an expected character isn't always present and I don't need the resulting value:
SELECT SUBSTRING(field_Calculated, 1, CHARINDEX('_', field_Calculated) - 1)
FROM (SELECT SUBSTRING(MyFld, CHARINDEX('_', MyFld) + 1, LEN(MyFld)) + '_' As field_Calculated
FROM MyTable) T
I think this is clear, but I really like the ParseName solution #JohnCappalletti suggests.
If it's only ever one numeric value you can use string_split:
SELECT * FROM MyTable
CROSS APPLY string_split(MyFld, '_')
WHERE ISNUMERIC(value) = 1
Either way you have to be careful of the data before deciding the best approach.
your data
Declare #Table Table ([MyFld] varchar(100))
Insert Into #Table
([MyFld] ) Values
('P_36840')
,('U_216137')
,('C_203134_H')
,('C_203134_W')
use SubString,Left and PatIndex
select
Left(
SubString(
[MyFld],
PatIndex('%[0-9.-]%', [MyFld]),
8000
),
PatIndex(
'%[^0-9.-]%',
SubString(
[MyFld],
PatIndex('%[0-9.-]%', [MyFld]),
8000
) + 'X'
)-1
) as DerivedPrimaryKey
from
#Table

Some numbers are getting truncated. I would like to pull all numbers

I'm trying to parse only numbers from a string. My code must be pretty close, but something is off here, because several numbers in the last string are being truncated, although the first two strings seem fine.
Here is my code.
Drop Table SampleData
Create table SampleData
(id int, factor varchar(100))
insert into #source_Policy values (1 ,'AAA 1.058 (Protection Class)')
insert into #source_Policy values (2, 'BBB0.565 (Construction) ')
insert into #source_Policy values ( 3, 'CCCCC 1.04890616 (Building Limit Rel')
Select *
From SampleData
;with processTable as (
select id, factor, num
from SampleData
cross apply (
select (select C + ''
from (select N, substring(factor, N, 1) C from (values(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12)) Num(N) where N<=datalength(factor)) t
where PATINDEX('%[0-9.]%',C)> 0
order by N
for xml path(''))
) p0 (num)
)
SELECT id, factor, num
FROM processTable
This is the result that I get.
In the num column, instead of 1.04, I would like to see the full precision, so: 1.04890616
I would think something like this:
select s.*, v2.numstr
from sampledata s cross apply
(values (stuff(factor, 1, patindex('%[0-9]%', factor) - 1, ''))) v(str) cross apply
(values (left(v.str, patindex('%[^0-9.]%', v.str + 'x') - 1))) v2(numstr);
Here is a SQL Fiddle.

Find Substring in SQL

I have to find substring as follows.
Data as below
aaaa.bbb.ccc.dddd.eee.fff.ggg
qq.eeddde.rrr.t.hh.jj.jj.hh.hh
ee.r.t.y.u.i.ii.
I want output as-
bbb
eeeddde
r
challenge I am facing is all have (.) as separator so sub-string is tough to work.
SELECT SUBSTRING(string,CHARINDEX('.',string)+1,
(((LEN(string))-CHARINDEX('.', REVERSE(string)))-CHARINDEX('.',string))) AS Result
FROM [table]
bbb
eeeddde
r
looking substring between first and secound (.)
then it might be between second and third (.)
Here is one method:
select left(v.str1, charindex('.', v.str1 + '.') - 1)
from t cross apply
(values (stuff(t.string, 1, charindex('.', t.string + '.'), '')
) v(str1)
I assume (CHARINDEX) this is ms sql server.
CROSS APPLY is handy for intermediate calculations.
SELECT t.pos, t1.pos,
SUBSTRING(string, t.pos + 1, t1.pos - t.pos -1) AS Result
FROM [table]
CROSS APPLY ( VALUES(CHARINDEX('.',string)) ) t(pos)
CROSS APPLY ( VALUES(CHARINDEX('.',string, t.pos+1))) t1(pos)
Just another option is to use a little XML
Example
Declare #YourTable table (ID int,SomeColumn varchar(max))
Insert Into #YourTable values
(1,'aaaa.bbb.ccc.dddd.eee.fff.ggg')
,(2,'qq.eeddde.rrr.t.hh.jj.jj.hh.hh')
,(3,'ee.r.t.y.u.i.ii.')
Select ID
,SomeValue = convert(xml,'<x>' + replace(SomeColumn,'.','</x><x>')+'</x>').value('/x[2]','varchar(100)')
From #YourTable
Returns
ID SomeValue
1 bbb
2 eeddde
3 r
You can use left(), replace() and charindex() functions together :
select replace(
replace(
left(str,charindex('.',str,charindex('.',str)+1)),
left(str,charindex('.',str)),
''
),
'.'
,''
) as "Output"
from t;
Demo

How to work with reverse and split in a select in SQL Server 2008?

I found the below query very useful for an application I'm working on. However, I wanna replace the values for a select in a table.
WITH data AS
(
select Value, REVERSE(Value) AS ReverseValue from (values
('texttext/21/812/21a'), ('texttext/6/163/38a'), ('texttext/53/7a/a2'), ('text/t/e/xt/53/7a/a2')
)t(Value)
), split AS
(
select
Value, ReverseValue,
reverse(substring(ReverseValue, 1, P1.Pos - 1)) AS Forth,
reverse(substring(ReverseValue, P1.Pos + 1, P2.Pos - P1.Pos - 1)) AS Third,
reverse(substring(ReverseValue, P2.Pos + 1, P3.Pos - P2.Pos - 1)) AS Second,
reverse(substring(ReverseValue, p3.Pos + 1, len(ReverseValue))) AS First
from data
cross apply (select (charindex('/', ReverseValue))) as P1(Pos)
cross apply (select (charindex('/', ReverseValue, P1.Pos+1))) as P2(Pos)
cross apply (select (charindex('/', ReverseValue, P2.Pos+1))) as P3(Pos)
)
select Value, First + '-' + Forth + Third + Second AS NewValue from split
So instead of (values ('texttext/21/812/21a') (...) i want something like (select myfield from myutable). Any ideas of how to do that? Thanks.
Just replace the values in the first CTE:
WITH data AS (
select myfield, REVERSE(myfield) AS ReverseValue
from mytable
),
. . .

Spliting a column into multiple columns in SQL Server

I am working with a column called FullName which stores a lengthy value.
The format is something like
'Microsoft.SQL.Server.20XX.DBFile:ABC.edf.com;XXXX_XXX_XXX;master;1;1'
'SQLVersion.DBFile:Hostname;InstanceName;Dummy;Dummy;Dummy'
What I want is to split FullName into SQLVersion, Hostname and InstanceName.
I have searched a sort of threads about splitting values which separate by a dot or a colon, which is slightly different with my case.
You can use the following trick with cross apply. I think the code is self explanatory and doesn't need elaboration:
create table t(v varchar(200))
insert into t values
('Microsoft.SQL.Server.20XX.DBFile:ABC.edf.com;XXXX_XXX_XXX;master;1;1'),
('SQLVersion.DBFile:Hostname;InstanceName;Dummy;Dummy;Dummy')
select substring(v, 1, c1.i1 - 1) as SqlVersion,
substring(v, c1.i1 + 1, c2.i2 - c1.i1 - 1) as HostName,
substring(v, c2.i2 + 1, c3.i3 - c2.i2 - 1) as InstanceName
from t
cross apply(select charindex(':', t.v) as i1 ) c1
cross apply(select charindex(';', t.v, c1.i1 + 1) as i2) c2
cross apply(select charindex(';', t.v, c2.i2 + 1) as i3) c3
In case it is not clear. In first cross apply I am selecting index of symbol :. In second cross apply I am selecting index of symbol ; that is after the index of first cross apply. In third the index of symbol ; that is after the index of second cross apply. In main select I just use those indeces to grab the needed portions of string.
Fiddle here http://sqlfiddle.com/#!3/d79b45/16
I agree with the comment above that separate columns would be preferred, if possible. However, I believe this parsing logic is doing what you want:
declare
#test nvarchar(200)
, #versionStart int
, #versionLength int
, #hostStart int
, #hostLength int
, #instanceStart int
, #instanceLength int
SET #test = 'Microsoft.SQL.Server.20XX.DBFile:ABC.edf.com;XXXX_XXX_XXX;master;1;1'
SET #versionStart = PATINDEX('%Microsoft.SQL.Server.%', #test) + 21
SET #versionLength = CHARINDEX('.', #test, #versionStart) - #versionStart
SET #hostStart = PATINDEX('%.DBFile:%', #test) + 8
SET #hostLength = CHARINDEX(';', #test, #hostStart) - #hostStart
SET #instanceStart = CHARINDEX(';', #test, #hostStart + #hostLength) + 1
SET #instanceLength = CHARINDEX(';', #test, #instanceStart) - #instanceStart
select
SUBSTRING(#test, #versionStart, 4) AS Version
, SUBSTRING(#test, #hostStart, #hostLength) AS HostName
, SUBSTRING(#test, #instanceStart, #instanceLength) AS InstanceName