How To Split Separate Strings in 2 Different Columns in SQL Server - sql

I have 2 columns of pipe delimited data that I need to break out into rows but the columns must stay together. Here's what my data looks like:
Plan Name: ABC|DEF|GHI|JKL
Plan Type: HMO|POS|HMO|PPO
I need to end up with 4 rows that look like this:
1 - ABC HMO
2 - DEF POS
3 - GHI HMO
4 - JKL PPO
I know how to separate each column individually using the STUFF function but how do I keep the first value from column 1 with the first value from column 2, etc? Don't know where to start. Appreciate any help!
p.s. - I am not on SQL Server 2016 so can't use STRING_SPLIT

One method is a recursive CTE:
with t as (
select *
from (values ('ABC|DEF|GHI|JKL', 'HMO|POS|HMO|PPO')) v(plannames, plantypes)
),
cte as (
select convert(varchar(max), left(plannames, charindex('|', plannames + '|') - 1)) as planname,
convert(varchar(max), left(plantypes, charindex('|', plantypes + '|') - 1)) as plantype,
convert(varchar(max), stuff(plannames, 1, charindex('|', plannames + '|'), '')) as planname_rest,
convert(varchar(max), stuff(plantypes, 1, charindex('|', plantypes + '|'), '')) as plantype_rest,
1 as lev
from t
union all
select convert(varchar(max), left(planname_rest, charindex('|', planname_rest + '|') - 1)) as planname,
convert(varchar(max), left(plantype_rest, charindex('|', plantype_rest + '|') - 1)) as plantype,
convert(varchar(max), stuff(planname_rest, 1, charindex('|', planname_rest + '|'), '')) as planname_rest,
convert(varchar(max), stuff(plantype_rest, 1, charindex('|', plantype_rest + '|'), '')) as plantype_rest,
lev + 1
from cte
where planname_rest <> ''
)
select *
from cte;
Here is a db<>fiddle.

Using delimitedsplit8k_lead you could do:
SELECT CONVERT(varchar(3), itemnumber) + ' - ' + PN.item + ' ' + PT.item
FROM YourTable YT
CROSS APPLY dbo.delimitedsplit8k_lead(YT.PlanName,'|') PN
CROSS APPLY dbo.delimitedsplit8k_lead(YT.PlanType,'|') PT
WHERE PN.ItemNumber = PT.ItemNumber;
This assumes PlanName and PlanType have the same number of elements.

Related

How to achieve the following using functions/stored procedure in SQL Server database

I have the following table with following data as
Tab1
FutureMISBoundaryVersion CurrentMISBoundaryVersion FutureHAMBoundaryVersion CurrentHAMBoundaryVersion
2:21,5:50,4:55,7:80,9:33 2:12,5:40,4:35,7:60,9:87 2:52,5:90,4:75,7:30,9:57 2:42,5:60,4:95,7:70,9:37
This key value pair has to be split into and the value of each key has to be inserted into another table in the following fashion
FutureMIS-OAKVersion |FutureMIS-HAMVersion |FutureMIS-DURVersion | FutureMIS-BURVersion| FutureMIS-YRTVersion |DeviceMIS-OAKVersion|DeviceMIS-HAMVersion |DeviceMIS-DURVersion| DeviceMIS-BURVersion| DeviceMIS-YRTVersion
33 | 80 | 21 | 55 | 50 | 87 | 60 |12 |35 | 40
i,e: when it finds column 'FutureMISBoundaryVersion' in tab1 then its value
'2:21,5:50,4:55,7:80,9:33' will be split and its value is inserted in such a way that the corresponding value of key 2 i,e:21 will be inserted into FutureMIS-DURVersion column.
Similarly key 5's value 50 will be inserted into FutureMIS-BURVersion column and so on for other keys
when it finds column 'CurrentMISBoundaryVersion' then
'2:12,5:40,4:35,7:60,9:87' will be split and its value is inserted in such a way that the corresponding value of key 2 i,e:12 will be inserted into CurrentMIS-DURVersion column similarly key 5's 40 value will be inserted into DeviceMIS-YRTVersion column and so on for other columns of the source table.
The table structure may extend as I have shown only 4 source table column but logic for all the columns remain same
Very funky requirements to be honest.
Please note solution below will work only in SQL Server 2016+ as I'm using JSON to parse the data. However you can write your own parser, in this case code will work in almost all versions of SQL Server.
Parse function:
CREATE FUNCTION dbo.ParseIt(#Type NVARCHAR(255),#Value NVARCHAR(MAX))
RETURNS #Parsed TABLE (Code NVARCHAR(255),Value NVARCHAR(255))
AS
BEGIN
INSERT INTO #Parsed(Code,Value)
SELECT #Type + '-' + m.Code + 'Version' AS [Code],p.[1] AS [Value]
FROM (
SELECT j.[key] AS [ID],i.[key],i.value
FROM OPENJSON('["' + REPLACE(#Value,',','","') + '"]') j
CROSS APPLY OPENJSON('[' + REPLACE(j.[value],':',',') + ']') i
) a
PIVOT(MAX(a.value) FOR a.[key] IN ([0],[1])) p
INNER JOIN ( VALUES
(2,'DUR')
,(4,'BUR')
,(5,'YRT')
,(7,'HAM')
,(9,'OAK')
) m(ID, Code) ON m.ID = p.[0]
;
RETURN;
END
Initial data:
DECLARE #Table TABLE (FutureMISBoundaryVersion NVARCHAR(MAX), CurrentMISBoundaryVersion NVARCHAR(MAX),FutureHAMBoundaryVersion NVARCHAR(MAX),CurrentHAMBoundaryVersion NVARCHAR(MAX));
INSERT INTO #Table(FutureMISBoundaryVersion,CurrentMISBoundaryVersion,FutureHAMBoundaryVersion,CurrentHAMBoundaryVersion)VALUES
('2:21,5:50,4:55,7:80,9:33','2:12,5:40,4:35,7:60,9:87','2:52,5:90,4:75,7:30,9:57','2:42,5:60,4:95,7:70,9:37')
;
The code:
SELECT COALESCE(p.[FutureMIS-OAKVersion],'') AS [FutureMIS-OAKVersion]
,COALESCE(p.[FutureMIS-HAMVersion],'') AS [FutureMIS-HAMVersion]
,COALESCE(p.[FutureMIS-DURVersion],'') AS [FutureMIS-DURVersion]
,COALESCE(p.[FutureMIS-BURVersion],'') AS [FutureMIS-BURVersion]
,COALESCE(p.[FutureMIS-YRTVersion],'') AS [FutureMIS-YRTVersion]
,COALESCE(p.[DeviceMIS-OAKVersion],'') AS [DeviceMIS-OAKVersion]
,COALESCE(p.[DeviceMIS-HAMVersion],'') AS [DeviceMIS-HAMVersion]
,COALESCE(p.[DeviceMIS-DURVersion],'') AS [DeviceMIS-DURVersion]
,COALESCE(p.[DeviceMIS-BURVersion],'') AS [DeviceMIS-BURVersion]
,COALESCE(p.[DeviceMIS-YRTVersion],'') AS [DeviceMIS-YRTVersion]
FROM (
SELECT f.Code,f.Value FROM #Table t CROSS APPLY dbo.ParseIt('FutureMIS',t.FutureMISBoundaryVersion) f
UNION ALL
SELECT f.Code,f.Value FROM #Table t CROSS APPLY dbo.ParseIt('DeviceMIS',t.CurrentMISBoundaryVersion) f
) a
PIVOT(MAX(a.Value) FOR a.Code IN ([DeviceMIS-BURVersion],[DeviceMIS-DURVersion],[DeviceMIS-HAMVersion],[DeviceMIS-OAKVersion]
,[DeviceMIS-YRTVersion],[FutureMIS-BURVersion],[FutureMIS-DURVersion],[FutureMIS-HAMVersion],[FutureMIS-OAKVersion]
,[FutureMIS-YRTVersion])) p
;
The following query will parse the comma separated string 2:21,5:50,4:55,7:80,9:33 into individual component 2:21, 5:30 etc. From there you can use similar method to extract bb from aa:bb.
Since the key-value pair is in format aa:bb, you can use datepart(hour, 'aa:bb') and datepart(minute, 'aa:bb') to extract aa and bb
; with
Tab1 as
(
select val = '2:21,5:50,4:55,7:80,9:33'
)
select t.*, k1.k, k2.k, k3.k, k4.k, k5.k
from Tab1 t
cross apply
(
select i = charindex(',', t.val),
k = substring(t.val, 1, charindex(',', t.val + ',', 1) - 1)
) k1
cross apply
(
select i = charindex(',', t.val, k1.i + 1),
k = substring(t.val, k1.i + 1, charindex(',', t.val + ',', k1.i + 1) - k1.i - 1)
) k2
cross apply
(
select i = charindex(',', t.val, k2.i + 1),
k = substring(t.val, k2.i + 1, charindex(',', t.val + ',', k2.i + 1) - k2.i - 1)
) k3
cross apply
(
select i = charindex(',', t.val, k3.i + 1),
k = substring(t.val, k3.i + 1, charindex(',', t.val + ',', k3.i + 1) - k3.i - 1)
) k4
cross apply
(
select i = charindex(',', t.val, k4.i + 1),
k = substring(t.val, k4.i + 1, charindex(',', t.val + ',', k4.i + 1) - k4.i - 1)
) k5
This is a pain in SQL Server. You can do this with recursive CTEs:
with cte as (
select convert(varchar(max), left(FutureMISBoundaryVersion, charindex(',', FutureMISBoundaryVersion) - 1)) as FutureMISBoundaryVersion,
convert(varchar(max), left(CurrentMISBoundaryVersion, charindex(',', CurrentMISBoundaryVersion) - 1)) as CurrentMISBoundaryVersion,
convert(varchar(max), left(FutureHAMBoundaryVersion, charindex(',', FutureHAMBoundaryVersion) - 1)) as FutureHAMBoundaryVersion,
convert(varchar(max), left(CurrentHAMBoundaryVersion, charindex(',', FutureMISBoundaryVersion) - 1)) as CurrentHAMBoundaryVersion,
stuff(FutureMISBoundaryVersion, 1, charindex(',', FutureMISBoundaryVersion), '') + ',' as FutureMISBoundaryVersion_rest,
stuff(CurrentMISBoundaryVersion, 1, charindex(',', CurrentMISBoundaryVersion), '') + ',' as CurrentMISBoundaryVersion_rest,
stuff(FutureHAMBoundaryVersion, 1, charindex(',', FutureHAMBoundaryVersion), '') + ',' as FutureHAMBoundaryVersion_rest,
stuff(CurrentHAMBoundaryVersion, 1, charindex(',', CurrentHAMBoundaryVersion), '') + ',' as CurrentHAMBoundaryVersion_rest,
1 as lev
from t
union all
select convert(varchar(max), left(FutureMISBoundaryVersion_rest, charindex(',', FutureMISBoundaryVersion_rest) - 1)) as FutureMISBoundaryVersion,
convert(varchar(max), left(CurrentMISBoundaryVersion_rest, charindex(',', CurrentMISBoundaryVersion_rest) - 1)) as CurrentMISBoundaryVersion,
convert(varchar(max), left(FutureHAMBoundaryVersion_rest, charindex(',', FutureHAMBoundaryVersion_rest) - 1)) as FutureHAMBoundaryVersion,
convert(varchar(max), left(CurrentHAMBoundaryVersion_rest, charindex(',', CurrentHAMBoundaryVersion_rest) - 1)) as CurrentHAMBoundaryVersion,
stuff(FutureMISBoundaryVersion_rest, 1, charindex(',', FutureMISBoundaryVersion_rest), '') as FutureMISBoundaryVersion_rest,
stuff(CurrentMISBoundaryVersion_rest, 1, charindex(',', CurrentMISBoundaryVersion_rest), '') as CurrentMISBoundaryVersion_rest,
stuff(FutureHAMBoundaryVersion_rest, 1, charindex(',', FutureHAMBoundaryVersion_rest), '') as FutureHAMBoundaryVersion_rest,
stuff(CurrentHAMBoundaryVersion_rest, 1, charindex(',', CurrentHAMBoundaryVersion_rest), '') as CurrentHAMBoundaryVersion_rest,
lev + 1
from cte
where FutureMISBoundaryVersion_rest like '%,%'
)
select FutureMISBoundaryVersion, CurrentMISBoundaryVersion, FutureHAMBoundaryVersion, CurrentHAMBoundaryVersion, lev
from cte;
Here is a db<>fiddle.

Finding matching values in a field, that a seperated by spaces

In SQL Server I have a field that has delimited data (by space) in it.
E.g.
recid| Delimited data field
1| 1 2 3 4 5
2| 1 2 3 3 5
3| 1 1 1 1 1
I need to loop through all the records in the DB and interrogate the delimited data field and compare the third and fourth parts of data against each other and if they match, return the recid and the whole delimited field.
So from my example records 2 and 3 have matching data parts, so it would return:-
2|1 2 3 3 5
3|1 1 1 1 1
Because 3 3 matches, as does 1 1.
Thanks.
If it is always 1 digit and same format, you can try like following.
select * from #table
where SUBSTRING([data], 5, 1) = SUBSTRING([data], 7, 1)
If not (Numbers are not single digit), you can try like following.
;WITH cte
AS (SELECT F1.recid,
F1.[data],
O.splitdata,
Row_number()
OVER(
partition BY recid
ORDER BY (SELECT 1)) rn
FROM (SELECT *,
Cast('<X>' + Replace(F.data, ' ', '</X><X>') + '</X>' AS
XML)
AS
xmlfilter
FROM #table F)F1
CROSS apply (SELECT fdata.d.value('.', 'varchar(50)') AS
splitdata
FROM f1.xmlfilter.nodes('X') AS fdata(d)) O)
SELECT c1.recid,
c1.data
FROM cte c1
INNER JOIN cte c2
ON c1.recid = c2.recid
AND c1.rn = 3
AND c2.rn = 4
AND c1.splitdata = c2.splitdata
GROUP BY c1.recid,
c1.data
Online Demo
Need to split the data, give the row number and then compare.
Schema:
SELECT * INTO #TAB FROM (
SELECT 1, '1 2 3 4 5' UNION ALL
SELECT 2, '1 2 3 3 5' UNION ALL
SELECT 3, '1 1 1 1 1'
)A (recid , Delimited_data_field)
Solution :
;WITH CTE
AS (
SELECT recid
,Delimited_data_field
,ROW_NUMBER() OVER (PARTITION BY recid ORDER BY (SELECT 1)) RNO
,splt.X.value('.', 'INT') VAL
FROM (
SELECT recid
,Delimited_data_field
,CAST('<M>' + REPLACE(Delimited_data_field, ' ', '</M><M>') + '</M>' AS XML) DATA
FROM #TAB
) A
CROSS APPLY A.DATA.nodes('/M') splt(x)
)
SELECT C.recid
,C2.Delimited_data_field
FROM CTE C
INNER JOIN CTE C2 ON C.recid = C2.recid AND C.RNO = 3 AND C2.RNO = 4
AND C.VAL = C2.VAL
Result :
recid Delimited_data_field
2 1 2 3 3 5
3 1 1 1 1 1
Your question has two parts, find nth split and then compare. Your first approach should be to break the problem until you find built in functions that can do the job.
here is one method inner query return result after split and outer compares:
SELECT recid,Delimited from (
SELECT recid,Delimited, SUBSTRING(Delimited,
charindex(' ', Delimited, (charindex(' ', Delimited, 1))+2)+1,1)
third, SUBSTRING(Delimited, charindex(' ',Delimited,
(charindex(' ', Delimited, 1))+3)+1,1)
fourth FROM YourTable) tr
WHERE third = fourth
See simple substring and charindex can do the job.
Here is one more solution to that.
I tweaked the split function in this link (T-SQL: Opposite to string concatenation - how to split string into multiple records) a bit to make it usefule in your scenario.
Here is the function.
CREATE FUNCTION dbo.SplitAndGetNumberAt (#sep char(1), #s varchar(512), #pos int)
RETURNS INT
BEGIN
declare #val as varchar(10);
WITH Pieces(pn, start, stop) AS (
SELECT 1, 1, CHARINDEX(#sep, #s)
UNION ALL
SELECT pn + 1, stop + 1, CHARINDEX(#sep, #s, stop + 1)
FROM Pieces
WHERE stop > 0
)
SELECT #val = SUBSTRING(#s, start, CASE WHEN stop > 0 THEN stop-start ELSE 512 END)
FROM Pieces where pn = #pos;
RETURN #val
END
Now you can use this function to get 3rd and 4th position of numbers and compare easily.
select recid, deldata
from so1
where dbo.SplitAndGetNumberAt (' ', deldata, 3) = dbo.SplitAndGetNumberAt (' ', deldata, 4)
Hope it will help.
If you have SQL Server 2016 or higher, you may try one approach using OPENJSON() to split your input data. The important part here is the fact, that when OPENJSON parses a JSON array the indexes of the elements in the JSON text are returned as keys (0-based).
Input:
CREATE TABLE #Table (
RecId int,
Data varchar(max)
)
INSERT INTO #Table
(RecId, Data)
VALUES
(1, '1 2 3 4 5'),
(2, '1 2 3 3 5'),
(3, '1 1 1 1 1')
Statement:
SELECT
t.RecId,
t.Data
FROM #Table t
CROSS APPLY (SELECT [value] FROM OPENJSON('["' + REPLACE(t.Data,' ','","') + '"]') WHERE [key] = 2) j3
CROSS APPLY (SELECT [value] FROM OPENJSON('["' + REPLACE(t.Data,' ','","') + '"]') WHERE [key] = 3) j4
WHERE j3.[value] = j4.[value]
Output:
RecId Data
2 1 2 3 3 5
3 1 1 1 1 1
Just for fun, sort of crazy coding:
DECLARE #Table Table (
recid INT,
DelimitedDataField VARCHAR(32)
)
INSERT #Table (recid, DelimitedDataField)
VALUES
(1, '1 2 3 4 5'),
(2, '1 2 3 3 5'),
(3, '1 1 1 1 1')
SELECT *
FROM #Table
WHERE
SUBSTRING (
STUFF(
STUFF(
DelimitedDataField + ' - - -',
1,
CHARINDEX(' ', DelimitedDataField + ' - - -'),
''
),
1,
CHARINDEX(' ', STUFF(
DelimitedDataField + ' - - -',
1,
CHARINDEX(' ', DelimitedDataField + ' - - -'), '')
),
''),
1,
CHARINDEX(' ', STUFF(
STUFF(
DelimitedDataField + ' - - -',
1,
CHARINDEX(' ', DelimitedDataField + ' - - -'),
''
),
1,
CHARINDEX(' ', STUFF(
DelimitedDataField + ' - - -',
1,
CHARINDEX(' ', DelimitedDataField + ' - - -'), '')
),
'')
)
) =
SUBSTRING (
STUFF(
STUFF(
STUFF(
DelimitedDataField + ' - - -',
1,
CHARINDEX(' ', DelimitedDataField + ' - - -'),
''
),
1,
CHARINDEX(' ', STUFF(
DelimitedDataField + ' - - -',
1,
CHARINDEX(' ', DelimitedDataField + ' - - -'), '')
),
''),
1,
CHARINDEX(' ', STUFF(
STUFF(
DelimitedDataField + ' - - -',
1,
CHARINDEX(' ', DelimitedDataField + ' - - -'),
''
),
1,
CHARINDEX(' ', STUFF(
DelimitedDataField + ' - - -',
1,
CHARINDEX(' ', DelimitedDataField + ' - - -'), '')
),
'')
),
''
),
1,
CHARINDEX(' ', STUFF(
STUFF(
STUFF(
DelimitedDataField + ' - - -',
1,
CHARINDEX(' ', DelimitedDataField + ' - - -'),
''
),
1,
CHARINDEX(' ', STUFF(
DelimitedDataField + ' - - -',
1,
CHARINDEX(' ', DelimitedDataField + ' - - -'), '')
),
''),
1,
CHARINDEX(' ', STUFF(
STUFF(
DelimitedDataField + ' - - -',
1,
CHARINDEX(' ', DelimitedDataField + ' - - -'),
''
),
1,
CHARINDEX(' ', STUFF(
DelimitedDataField + ' - - -',
1,
CHARINDEX(' ', DelimitedDataField + ' - - -'), '')
),
'')
),
''
))
)
AND SUBSTRING (
STUFF(
STUFF(
DelimitedDataField + ' - - -',
1,
CHARINDEX(' ', DelimitedDataField + ' - - -'),
''
),
1,
CHARINDEX(' ', STUFF(
DelimitedDataField + ' - - -',
1,
CHARINDEX(' ', DelimitedDataField + ' - - -'), '')
),
''),
1,
CHARINDEX(' ', STUFF(
STUFF(
DelimitedDataField + ' - - -',
1,
CHARINDEX(' ', DelimitedDataField + ' - - -'),
''
),
1,
CHARINDEX(' ', STUFF(
DelimitedDataField + ' - - -',
1,
CHARINDEX(' ', DelimitedDataField + ' - - -'), '')
),
'')
)
) <>'-'

Pad Zero before first hypen and remove spaces and add BA and IN

I have data as below
98-45.3A-22
104-44.0A-23
00983-29.1-22
01757-42.5A-22
04968-37.3A2-23
Output Looking for output as below in SQL Server
00098-BA45.3A-IN-22
00104-BA44.0A-IN-23
00983-BA29.1-IN-22
01757-BA42.5A-IN-22
04968-BA37.3A2-IN-23
I splitted parts to cope with tricky data templates. This should work even with non-dash-2-digit tail:
WITH Src AS
(
SELECT * FROM (VALUES
('98-45.3A-22'),
('104-44.0A-23'),
('00983-29.1-22'),
('01757-42.5A-22'),
('04968-37.3A2-23')
) T(X)
), Parts AS
(
SELECT *,
RIGHT('00000'+SUBSTRING(X, 1, CHARINDEX('-',X, 1)-1),5) Front,
'BA'+SUBSTRING(X, CHARINDEX('-',X, 1)+1, 2) BA,
SUBSTRING(X, PATINDEX('%.%',X), LEN(X)-CHARINDEX('-', REVERSE(X), 1)-PATINDEX('%.%',X)+1) P,
SUBSTRING(X, LEN(X)-CHARINDEX('-', REVERSE(X), 1)+1, LEN(X)) En
FROM Src
)
SELECT Front+'-'+BA+P+'-IN'+En
FROM Parts
It returns:
00098-BA45.3A-IN-22
00104-BA44.0A-IN-23
00983-BA29.1-IN-22
01757-BA42.5A-IN-22
04968-BA37.3A2-IN-23
Try this,
DECLARE #String VARCHAR(100) = '98-45.3A-22'
SELECT ISNULL(REPLICATE('0',6 - CHARINDEX('-',#String)),'') -- Add leading Zeros
+ STUFF(
STUFF(#String,CHARINDEX('-',#String),1,'-BA'), -- Add 'BA'
CHARINDEX('-',#String,CHARINDEX('-',#String)+1)+2, -- 2 additional for the character 'BA'
1,'-IN') -- Add 'IN'
What if I have more than 6 digit number before first hyphen and want to remove the leading zeros to make it 6 digits.
DECLARE #String VARCHAR(100) = '0000098-45.3A-22'
SELECT CASE WHEN CHARINDEX('-',#String) <= 6
THEN ISNULL(REPLICATE('0',6 - CHARINDEX('-',#String)),'') -- Add leading Zeros
+ STUFF(
STUFF( #String,CHARINDEX('-',#String),1,'-BA'), -- Add 'BA'
CHARINDEX('-',#String,CHARINDEX('-',#String)+1)+2, -- 2 additional for the character 'BA'
1,'-IN') -- Add 'IN'
ELSE STUFF(
STUFF(
STUFF(#String,CHARINDEX('-',#String),1,'-BA'), -- Add 'BA'
CHARINDEX('-',#String,CHARINDEX('-',#String)+1)+2, -- 2 additional for the character 'BA'
1,'-IN'), -- Add 'IN'
1, CHARINDEX('-',#String) - 6, '' -- remove extra leading Zeros
)
END
Making assumptions that the format is consistent (e.g. always ends with "-" + 2 characters....)
DECLARE #Data TABLE (Col1 VARCHAR(100))
INSERT #Data ( Col1 )
SELECT Col1
FROM (
VALUES ('98-45.3A-22'), ('104-44.0A-23'),
('00983-29.1-22'), ('01757-42.5A-22'),
('04968-37.3A2-23')
) x (Col1)
SELECT RIGHT('0000' + LEFT(Col1, CHARINDEX('-', Col1) - 1), 5)
+ '-BA' + SUBSTRING(Col1, CHARINDEX('-', Col1) + 1, CHARINDEX('.', Col1) - CHARINDEX('-', Col1))
+ SUBSTRING(Col1, CHARINDEX('.', Col1) + 1, LEN(Col1) - CHARINDEX('.', Col1) - 3)
+ '-IN-' + RIGHT(Col1, 2)
FROM #Data
It's not ideal IMO to do this string manipulation all the time in SQL. You could shift it out to your presentation layer, or store the pre-formatted value in the db to save the cost of this every time.
Use REPLICATE AND CHARINDEX:
Replicate: will repeat given character till reach required count specify in function
CharIndex: Finds the first occurrence of any character
Declare #Data AS VARCHAR(50)='98-45.3A-22'
SELECT REPLICATE('0',6-CHARINDEX('-',#Data)) + #Data
SELECT
SUBSTRING
(
(REPLICATE('0',6-CHARINDEX('-',#Data)) +#Data)
,0
,6
)
+'-'+'BA'+ CAST('<x>' + REPLACE(#Data,'-','</x><x>') + '</x>' AS XML).value('/x[2]','varchar(max)')
+'-'+ 'IN'+ '-' + CAST('<x>' + REPLACE(#Data,'-','</x><x>') + '</x>' AS XML).value('/x[3]','varchar(max)')
In another way by using PARSENAME() you can use this query:
WITH t AS (
SELECT
PARSENAME(REPLACE(REPLACE(s, '.', '###'), '-', '.'), 3) AS p1,
REPLACE(PARSENAME(REPLACE(REPLACE(s, '.', '###'), '-', '.'), 2), '###', '.') AS p2,
PARSENAME(REPLACE(REPLACE(s, '.', '###'), '-', '.'), 1) AS p3
FROM yourTable)
SELECT RIGHT('00000' + p1, 5) + '-BA' + p2 + '-IN-' + p3
FROM t;

sql split 5 values on delimiter

So i have this code that splits exactly 4 values on double delimiter , it works perfectly , but i cant get around how to split 5 values
Here is my string
#string = 'Value1|Value2|Value3|Value4|Value5, OtherVal1|OtherVal2|OtherVal3|OtherVal4|OtherVal5'
Here is code i have for spliting 4 values
WITH cte
AS (SELECT Replace(Rtrim(Ltrim(split.a.value('.', 'VARCHAR(100)'))), '|','.') AS split_data
FROM (SELECT Cast ('<M>' + Replace(#string, ',', '</M><M>')
+ '</M>' AS XML) AS Data) AS A
CROSS apply data.nodes ('/M') AS Split(a))
SELECT COLUMN2= Parsename(split_data, 4),
COLUMN3=Parsename(split_data, 3),
COLUMN4= Parsename(split_data, 2),
COLUMN5=Parsename(split_data, 1)
FROM cte
How do i split it on 5 vals ?
On the first look...
Seems, you have to use CTE twice. First one - to get "rows" (split data by ,) and the second one - to get "colums" (split data by |). Then you have to PIVOT data to display them in tabular form ;)
Have a look at example:
DECLARE #string VARCHAR(MAX) = 'Value1|Value2|Value3|Value4|Value5, OtherVal1|OtherVal2|OtherVal3|OtherVal4|OtherVal5'
;WITH MyRows AS
(
SELECT 1 AS RowNo, LEFT(#string, CHARINDEX(', ', #string)-1) AS MyVals, RIGHT(#string, LEN(#string) - CHARINDEX(', ', #string)) AS Remainder
WHERE CHARINDEX(', ', #string)>0
UNION ALL
SELECT RowNo + 1 AS RowNo, LEFT(Remainder, CHARINDEX(', ', Remainder)) AS MyVals, RIGHT(Remainder, LEN(Remainder) - CHARINDEX(', ', Remainder)) AS Remainder
FROM MyRows
WHERE CHARINDEX(', ', Remainder)>0
UNION ALL
SELECT RowNo + 1 AS RowNo, Remainder AS MyVals, NULL AS Remainder
FROM MyRows
WHERE CHARINDEX(', ', Remainder)=0
),
MyCols AS
(
SELECT RowNo, 1 AS ColNumber, LEFT(MyVals, CHARINDEX('|', MyVals)-1) AS SingleValue, RIGHT(MyVals, LEN(MyVals) - CHARINDEX('|', MyVals)) AS Remainder
FROM MyRows
WHERE CHARINDEX('|', MyVals)>0
UNION ALL
SELECT RowNo, ColNumber + 1 AS ColNumber, LEFT(Remainder, CHARINDEX('|', Remainder)-1) AS SingleValue, RIGHT(Remainder, LEN(Remainder) - CHARINDEX('|', Remainder)) AS Remainder
FROM MyCols
WHERE CHARINDEX('|', Remainder)>0
UNION ALL
SELECT RowNo, ColNumber + 1 AS ColNumber, Remainder AS SingleValue, NULL AS Remainder
FROM MyCols
WHERE CHARINDEX('|', Remainder)=0
)
SELECT RowNo, [1],[2],[3],[4],[5]
FROM (
SELECT ColNumber, SingleValue, RowNo
FROM MyCols ) AS DT
PIVOT(Max(SingleValue) FOR ColNumber IN([1],[2],[3],[4],[5])) AS PT
Result:
RowNo 1 2 3 4 5
1 Value1 Value2 Value3 Value4 Value5
2 OtherVal1 OtherVal2 OtherVal3 OtherVal4 OtherVal5

Extract string between after second / and before -

I have a field that holds an account code. I've managed to extract the first 2 parts OK but I'm struggling with the last 2.
The field data is as follows:
812330/50110/0-0
812330/50110/BDG001-0
812330/50110/0-X001
I need to get the string between the second "/" and the "-" and after the "-" .Both fields have variable lengths, so I would be looking to output 0 and 0 on the first record, BDG001 and 0 on the second record and 0 and X001 on the third record.
Any help much appreciated, thanks.
You can use CHARINDEX and LEFT/RIGHT:
CREATE TABLE #tab(col VARCHAR(1000));
INSERT INTO #tab VALUES ('812330/50110/0-0'),('812330/50110/BDG001-0'),
('812330/50110/0-X001');
WITH cte AS
(
SELECT
col,
r = RIGHT(col, CHARINDEX('/', REVERSE(col))-1)
FROM #tab
)
SELECT col,
r,
sub1 = LEFT(r, CHARINDEX('-', r)-1),
sub2 = RIGHT(r, LEN(r) - CHARINDEX('-', r))
FROM cte;
LiveDemo
EDIT:
or even simpler:
SELECT
col
,sub1 = SUBSTRING(col,
LEN(col) - CHARINDEX('/', REVERSE(col)) + 2,
CHARINDEX('/', REVERSE(col)) -CHARINDEX('-', REVERSE(col))-1)
,sub2 = RIGHT(col, CHARINDEX('-', REVERSE(col))-1)
FROM #tab;
LiveDemo2
EDIT 2:
Using PARSENAME SQL SERVER 2012+ (if your data does not contain .):
SELECT
col,
sub1 = PARSENAME(REPLACE(REPLACE(col, '/', '.'), '-', '.'), 2),
sub2 = PARSENAME(REPLACE(REPLACE(col, '/', '.'), '-', '.'), 1)
FROM #tab;
LiveDemo3
...Or you can do this, so you only go from left side to right, so you don't need to count from the end in case you have more '/' or '-' signs:
SELECT
SUBSTRING(columnName, CHARINDEX('/' , columnName, CHARINDEX('/' , columnName) + 1) + 1,
CHARINDEX('-', columnName) - CHARINDEX('/' , columnName, CHARINDEX('/' , columnName) + 1) - 1) AS FirstPart,
SUBSTRING(columnName, CHARINDEX('-' , columnName) + 1, LEN(columnName)) AS LastPart
FROM table_name
One method way is to download a split() function off the web and use it. However, the values end up in separate rows, not separate columns. An alternative is a series of nested subqueries, CTEs, or outer applies:
select t.*, p1.part1, p12.part2, p12.part3
from table t outer apply
(select t.*,
left(t.field, charindex('/', t.field)) as part1,
substring(t.field, charindex('/', t.field) + 1) as rest1
) p1 outer apply
(select left(p1.rest1, charindex('/', p1.rest1) as part2,
substring(p1.rest1, charindex('/', p1.rest1) + 1, len(p1.rest1)) as part3
) p12
where t.field like '%/%/%';
The where clause guarantees that the field value is in the right format. Otherwise, you need to start sprinkling the code with case statements to handle misformated data.