Selecting text between 2nd and 3rd occurrence of delimiter - sql

I'm trying to select the text between the second and third occurance of a delimeter (-) in SQL server.
For example, if I have the string aaa-bbbb-cccc-dddd I would like to return cccc, but I can't understand how to make a substring work when I have more than 2 of the delimeters.
Thanks for any help

If you always the same number of elements you could leverage PARSENAME like this.
select parsename(replace('aaa-bbbb-cccc-dddd', '-', '.'), 2)
But if your real data is not that consistent you need to use a real splitter.

If parsename() (+1) is not a valid option, perhaps a little XML.
Here are two illustrations, both return the same results
Example
Declare #YourTable table (SomeCol varchar(500))
Insert Into #YourTable values
('aaa-bbbb-cccc-dddd')
Select SomeCol
,Pos2 = cast('<x>' + replace(A.SomeCol,'-','</x><x>')+'</x>' as xml).value('/x[2]','varchar(50)')
,Pos3 = cast('<x>' + replace(A.SomeCol,'-','</x><x>')+'</x>' as xml).value('/x[3]','varchar(50)')
From #YourTable A
Select SomeCol
,B.*
From #YourTable A
Cross Apply (
Select Pos2 = XMLData.value('/x[2]','varchar(50)')
,Pos3 = XMLData.value('/x[3]','varchar(50)')
From (values (cast('<x>' + replace(A.SomeCol,'-','</x><x>')+'</x>' as xml))) B1(XMLData)
) B
Returns
SomeCol Pos2 Pos3
aaa-bbbb-cccc-dddd bbbb cccc

Related

SQL - text functions not working (Reverse/Left/Substring/LTrim) - gotta be easy

I've got a text field on a table that I'm trying to dissect into two separate columns in a select statement. I swear this worked for me last time I used it, but now it's throwing an error "Invalid length parameter". What am I doing wrong?
Splitting the data from a single column which is like this:
"CORP - DIVISION - REGION - TEAM - SUPERVISOR"
Into two columns like:
SUPERVISOR | TEAM
Here's what I had that I swear used to work, but it doesn't anymore and I can't figure it out!
Reverse(Left(Reverse(table.column),CHARINDEX(' ', Reverse(table.column))-1)) AS 'SUPERVISOR'
,LTRIM(LEFT(Substring(table.column,18,150),CHARINDEX(' - ', Substring(table.column,18,150))-1)) AS 'TEAM'
If you have a known or maximum number of items, consider a little XML. Perhaps a little easier to read and maintain.
Also, you could eliminate Pos1,Pos2,Pos3 if you are only interested in Team & Supervisor.
Example
Declare #YourTable Table ([ID] varchar(50),[SomeCol] varchar(50))
Insert Into #YourTable Values
(1,'CORP - DIVISION - REGION - TEAM - SUPERVISOR')
Select A.ID
,B.*
From #YourTable A
Cross Apply (
Select Pos1 = ltrim(rtrim(xDim.value('/x[1]','varchar(100)')))
,Pos2 = ltrim(rtrim(xDim.value('/x[2]','varchar(100)')))
,Pos3 = ltrim(rtrim(xDim.value('/x[3]','varchar(100)')))
,Pos4 = ltrim(rtrim(xDim.value('/x[4]','varchar(100)')))
,Pos5 = ltrim(rtrim(xDim.value('/x[5]','varchar(100)')))
,Pos6 = ltrim(rtrim(xDim.value('/x[6]','varchar(100)')))
,Pos7 = ltrim(rtrim(xDim.value('/x[7]','varchar(100)')))
,Pos8 = ltrim(rtrim(xDim.value('/x[8]','varchar(100)')))
,Pos9 = ltrim(rtrim(xDim.value('/x[9]','varchar(100)')))
From (Select Cast('<x>' + replace(SomeCol,'-','</x><x>')+'</x>' as xml) as xDim) as A
) B
Returns
ID Pos1 Pos2 Pos3 Pos4 Pos5 Pos6 Pos7 Pos8 Pos9
1 CORP DIVISION REGION TEAM SUPERVISOR NULL NULL NULL NULL
EDIT
If you have non XML safe characters (<,>,,...) use
...
From ( values (cast('<x>' + replace((Select replace(SomeCol,'-','§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>')+'</x>' as xml))) A(xDim)
...
From SQL Server 2016 you can use STRING_SPLIT() to do it.
The STRING_SPLIT() can help normalize the data by splitting these
multi-valued columns.
I also used TRIM() function (introduced with SQL Server 2017) in order to remove the spaces, CTE, ROW_NUMBER() and PIVOT.
Below the script:
—-1 Create a test table
CREATE TABLE #TestTable
(
TestColumn varchar(100)
)
—-2 Inserting your string into table
INSERT INTO #TestTable
VALUES ('0 - CORP - DIVISION - REGION - TEAM - SUPERVISOR')
--3 Final query
;WITH CTE_Table AS (
SELECT
TestColumn = TRIM(TestColumn)
FROM
#TestTable
)
,CTE_Table2 AS (
SELECT
S.Value
FROM
CTE_Table
CROSS APPLY STRING_SPLIT([TestColumn],'-') AS S
)
,CTE_FinalTable AS (
SELECT TOP 5
Value
,ROW_NUMBER() OVER (ORDER BY Value) AS RowNumber
FROM
CTE_Table2
ORDER BY
Value
)
SELECT
[1],[2],[3],[4],[5]
FROM
CTE_FinalTable
PIVOT
(MAX([value])
The FOR [RowNumber] IN ([1],[2],[3],[4],[5])
) AS P

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

Custom split email column names into multiple columns in SQL

I have an email column with 3-4 emails in each row which i want to split into one email per column:
Current columns looks like this:
Email_column
1. drone#gmail.com bob#yahoo.com drake#gmail.com
Expected output should be:
Email_1 Email_2 Email_3
1. drone#email.com bob#yahoo.com drake#gmail.com
With a CROSS APPLY and a little XML
Example
Declare #YourTable table (ID int,Email_column varchar(max))
Insert Into #YourTable values
(1,'drone#gmail.com bob#yahoo.com drake#gmail.com')
Select A.ID
,B.*
From #YourTable A
Cross Apply (
Select Pos1 = n.value('/x[1]','varchar(max)')
,Pos2 = n.value('/x[2]','varchar(max)')
,Pos3 = n.value('/x[3]','varchar(max)')
,Pos4 = n.value('/x[4]','varchar(max)')
From (Select Cast('<x>' + replace(A.Email_column,' ','</x><x>')+'</x>' as xml) as n) X
) B
Returns
ID Pos1 Pos2 Pos3 Pos4
1 drone#gmail.com bob#yahoo.com drake#gmail.com NULL

How to Extract in SQL string value between the 2nd set of '-' dashes in a field

*I am trying to pull values in between 2nd set of '-' for example
RNDC007-PS-160958205220402-1-A.pdf should pull 160958205220402
1883626-FA-2011978300304402-12-A.pdf should pull 2011978300304402
Below is my code I have but I am getting values with the - on each side of the value.
For example: -2011978300304402-
Also when I run for all records against the table I get the error:
Invalid length parameter passed to the LEFT or SUBSTRING function.
Which I thought the Case statement would address but its not.
Thanks in Advance for the help!
My Code:
SELECT [ID]
,[T_File]
,[OrderNumber]
,[CustOrdNumber]
,[LineSeqNum]
,[SKU]
,[WebLineNum]
,CASE WHEN [T_File] LIKE '%.pdf%' THEN SUBSTRING([T_File],CHARINDEX('-', [T_File], CHARINDEX('-', [T_File]) + 1) -0,LEN([T_File]) - CHARINDEX('-', [T_File], CHARINDEX('-', [T_File]) + 1) - CHARINDEX('-', REVERSE(Rtrim([T_File])))) ELSE '' END AS PulledString
FROM [Portal].[dbo].[PA_URL]
Here is one way to do it using Charindex and NULLIF
SELECT Substring(string, scd + 1, NULLIF(trd, 0) - NULLIF(scd, 0) - 1) AS result
FROM (SELECT string,
Charindex('-', string) AS fst
FROM (VALUES('RNDC007-PS-160958205220402-1-A.pdf'),
('1883626-FA-2011978300304402-12-A.pdf'),
('1883626-FA') -- bad data
)tc(string)) a
CROSS apply(VALUES (Charindex('-', string, NULLIF(fst, 0) + 1))) b (scd)
CROSS apply(VALUES (Charindex('-', string, NULLIF(scd, 0) + 1))) c (trd)
For the bad records result will be NULL
Result :
result
---------------
160958205220402
2011978300304402
NULL
Another option is with a CROSS APPLY with a little XML. By default, the XML will parse 5 positions, however, as you can see, it it is easy to expand or contract -- essentially could be reduced to Pos2 = ...
Declare #YourTable table (ID int,T_File varchar(250))
Insert Into #YourTable values
(1,'RNDC007-PS-160958205220402-1-A.pdf should pull "160958205220402"'),
(2,'1883626-FA-2011978300304402-12-A.pdf should pull "2011978300304402"')
Select A.ID
,B.*
From #YourTable A
Cross Apply (
Select Pos1 = xDim.value('/x[1]','varchar(max)')
,Pos2 = xDim.value('/x[2]','varchar(max)')
,Pos3 = xDim.value('/x[3]','varchar(max)')
,Pos4 = xDim.value('/x[4]','varchar(max)')
,Pos5 = xDim.value('/x[5]','varchar(max)')
From (Select Cast('<x>' + replace((Select T_File as [*] For XML Path('')),'-','</x><x>')+'</x>' as xml) as xDim) as A
) B
Returns

How to format a string in a particular way in Sql

I have a SQL (SQL Server 2016) column which represents a version. It is NVARCHAR. I want to display the column in a consistent format. I did some research on FORMAT but couldn't find a solution. Any pointers please?
The output should always be of the form: XX.XX.XXXX
You can assume that there are two digits before the first decimal point (I was able to use CASE for fixing that)
Sample Input
============
13.0.1221.00
11.00.1111
Desired Output
==============
13.00.1221
11.00.1111
Declare #YourTable table (SomeField varchar(25))
Insert Into #YourTable values
('13.0.1221.00'),
('11.00.1111')
Select A.*
,Format(Pos1,'00')+'.'+Format(Pos2,'00')+'.'+Format(Pos3,'0000')
From #YourTable A
Cross Apply (
Select Pos1 = xDim.value('/x[1]','int')
,Pos2 = xDim.value('/x[2]','int')
,Pos3 = xDim.value('/x[3]','int')
From (Select Cast('<x>' + Replace(A.SomeField,'.','</x><x>')+'</x>' as XML) as xDim) A
) B
Returns
SomeField (No column name)
13.0.1221.00 13.00.1221
11.00.1111 11.00.1111
The output should always be of the form: XX.XX.XXXX
Forcing three digits into two would end up bad I guess, but if those are the requirements:
Declare #YourTable table (s varchar(25))
Insert Into #YourTable values
('13.0.1221.00'),
('11.00.1111'),
('189.256.0000001'),
('7'),
('.19.'),
('13.4.'),
('..11'),
('A..B'),
(null),
('......')
;WITH rs AS (SELECT s, REVERSE('0' + s) AS rs FROM #YourTable)
SELECT REVERSE(LEFT(ISNULL(PARSENAME(rs, 3), '') + '0000', 4) + '.' +
LEFT(ISNULL(PARSENAME(rs, 2), '') + '00' , 2) + '.' +
LEFT(ISNULL(PARSENAME(rs, 1), '') + '00' , 2)), s
FROM rs
Result:
13.00.1221 13.0.1221.00
11.00.1111 11.00.1111
89.56.0001 189.256.0000001
07.00.0000 7
00.19.0000 .19.
13.04.0000 13.4.
00.00.0011 ..11
0A.00.000B A..B
00.00.0000 NULL
00.00.0000 ......