Grabbing Multiple Substrings Between Specific Characters in SQL - sql-server

I want to extract the four digit number between the last "-" and last "," into its own column. And then I want to extract the month and year into two more columns.
Below is an example of the data I get from the column that I want to push out into 3 additional columns:
"Captured Credit Card: Visa, xxxx-xxxx-xxxx-1234, 12/20. Set as Default."
I want the output to be:
CC: 1234
Month: 12
Year: 20
Any help with this is appreciated.

I tried this using REVERSE() to flip the string then used CHARINDEX() to find the char location from the end of the string.
Then, using LEN() and a subtraction, the position of the last char is where it would be located in the original (un-flipped) string.
DECLARE #temp varchar(1000) = 'Captured Credit Card: Visa, xxxx-xxxx-xxxx-1234, 12/20. Set as Default.'
SELECT 'CC: '
+ SUBSTRING(#temp
, LEN(#temp) - CHARINDEX('-', REVERSE(#temp), 0) + 2
, LEN(#temp) - CHARINDEX(',', REVERSE(#temp), 0) --find the last ','
-(LEN(#temp) - CHARINDEX('-', REVERSE(#temp), 0)) --find the last '-')
-1
)
+ ' Month: '
+ SUBSTRING(#temp, CHARINDEX('/', #temp, 0) -2, 2)
+ ' Year: '
+ SUBSTRING(#temp, CHARINDEX('/', #temp, 0) + 1, 2)
AS [output]
I had to add or subtract 1 or 2 for the correct index used in the functions.
Produces Output:
output
------
CC: 1234 Month: 12 Year: 20

If your string is going to be in a consistent format as above then try below.
DECLARE #string VARCHAR(MAX) = 'Captured Credit Card: Visa, xxxx-xxxx-xxxx-1234, 12/20. Set as Default.'
SELECT Column1 = 'CC:' + RIGHT(REPLACE(#string, RIGHT(#string, CHARINDEX(',', REVERSE(#string))),''), CHARINDEX('-', REVERSE(REPLACE(#string, RIGHT(#string, CHARINDEX(',', REVERSE(#string))),''))) -1),
Column2 = 'Month: ' + SUBSTRING(#string,CHARINDEX('/', #string)-2,2),
Column3 = 'Year: ' + SUBSTRING(#string,CHARINDEX('/', #string)+1,2)

Related

'January-2016' string date in SQL Server - increment by one month

January-2016
August-2017
November-2018
January-2018
August-2018
November-2018
This is my date format how to increment month by 1 ?
DECLARE #Str VARCHAR(20) = 'January-2016'
SELECT FORMAT(DATEADD(MONTH , 1 ,
DATEFROMPARTS(RIGHT(#Str, 4) , DATEPART (MONTH, REPLACE(#Str , '-' , ' 01 ')) , 1)
)
, 'MMMM-yyyy')
Result: February-2016
You can just replace the dash with ' 1 ', as long as you explicitly use English:
SET LANGUAGE us_english;
DECLARE #s varchar(67) = 'January-2016';
SELECT TRY_CONVERT(date, REPLACE(#s, '-', ' 1 '));
So then add a month:
SELECT DATEADD(MONTH, 1, <try_convert expression>);

Split Delimited String into multiple Columns in SQL Server

I want to split a string in a column that is separated by space into multiple column is SQL.
I used the query below, but I get NULL values
select
PARSENAME(REPLACE(FX_RAW_DATA, ' ', '.'), 1) AS Country_Code,
PARSENAME(REPLACE(FX_RAW_DATA, ' ', '.'), 2) AS iso_Code,
PARSENAME(REPLACE(FX_RAW_DATA, ' ', '.'), 3) AS status,
PARSENAME(REPLACE(FX_RAW_DATA, ' ', '.'), 5) AS date,
PARSENAME(REPLACE(FX_RAW_DATA, ' ', '.'), 6) AS rate,
PARSENAME(REPLACE(FX_RAW_DATA, ' ', '.'), 7) AS fx
FROM process.FX_RAW_DATA_OUT;
GO
You don't state what version of SQL Server you are running. If STRING_SPLIT is not available on your version, you can do as follows (I think you are trying to split column FX_RAW_DATA on table process). Basically, you apply the calculation of each space one after the other, then substring each:
SELECT
Country_Code = SUBSTRING(p.FX_RAW_DATA, 1, v1.chr - 1),
iso_Code = SUBSTRING(p.FX_RAW_DATA, v1.chr + 1, v2.chr - v1.chr - 1),
status = SUBSTRING(p.FX_RAW_DATA, v2.chr + 1, v3.chr - v2.chr - 1),
date = SUBSTRING(p.FX_RAW_DATA, v3.chr + 1, v4.chr - v3.chr - 1),
rate = SUBSTRING(p.FX_RAW_DATA, v4.chr + 1, v5.chr - v4.chr - 1),
fx = SUBSTRING(p.FX_RAW_DATA, v5.chr + 1, LEN(p.FX_RAW_DATA))
FROM process p
CROSS APPLY (VALUES(CHARINDEX(' ', p.FX_RAW_DATA)) v1(chr)
CROSS APPLY (VALUES(CHARINDEX(' ', p.FX_RAW_DATA, v1.chr + 1)) v2(chr)
CROSS APPLY (VALUES(CHARINDEX(' ', p.FX_RAW_DATA, v2.chr + 1)) v3(chr)
CROSS APPLY (VALUES(CHARINDEX(' ', p.FX_RAW_DATA, v3.chr + 1)) v4(chr)
CROSS APPLY (VALUES(CHARINDEX(' ', p.FX_RAW_DATA, v4.chr + 1)) v5(chr);
The first substring always starts at 1 and ends at the first break, the last substring you can just pass the full length to get the remainder of the string.
It's not very clear from your example sample data and expected results what your data looks like. I have made some assumptions and example below should be easy to change to match your data as required.
DECLARE #stringarray varchar(max), --String holding your data
#Splitcharc char(1), --split character, space in your example
#X xml
set #stringarray = '01AED AUD M 30122020 .3541000 11' --example data line
set #Splitcharc = '' -- this can be changed if needed
-- We start off by formatting your string into an XML format
set #X = CONVERT(xml,' <Table> <Row> <col>' +
REPLACE(#stringarray,#Splitcharc,'</col> <col>') + '</col></Row> </Table> ')
-- The below show how you "query" each of the "columns" returned
SELECT
Tbl.cl.value('col[1]','VARCHAR(20)') country_code,
Tbl.cl.value('col[2]','VARCHAR(20)') ISO_code,
Tbl.cl.value('col[3]','VARCHAR(20)') [status],
Tbl.cl.value('col[4]','VARCHAR(20)') [date],
Tbl.cl.value('col[5]','VARCHAR(20)') rate,
Tbl.cl.value('col[6]','VARCHAR(20)') fx
FROM #X.nodes('/Table/Row') as Tbl(cl)

How to insert a character into a string every 3 characters from the right

I have a string made up of numbers. The length of the string ranges anywhere from 1 character to 9 characters.
I want to insert a dash (-) every three characters from the right. This will only be relevant for strings with upwards of three characters.
This will just be a select statement, as I do not want to actually update the string itself.
For example,
8 should return 8
476 should return 476
4767 should return 4-767
45907392 should return 45-907-392
845907392 should return 845-907-392
This should work:
select
*,
replace(format(n,'#,#'),',','-')
from (values (8),(476),(4767),(45907392),(845907392)) x (n)
Change , to . if it's thousand separator in your system, or provide a culture as third parameter in FORMAT function so it always works the same.
Since SQL Server 2012 you can use the FORMAT function as described in the other answers. In case you want to format a string value like 'abcde' too you can use only string functions like this:
DECLARE #str VARCHAR(100) = '845907392';
-- 845-907-392
SELECT REVERSE(SUBSTRING(REVERSE(#str), 7, 3))
+ CASE WHEN LEN(#str)>6 THEN '-' ELSE '' END
+ REVERSE(SUBSTRING(REVERSE(#str), 4, 3))
+ CASE WHEN LEN(#str)>3 THEN '-' ELSE '' END
+ REVERSE(SUBSTRING(REVERSE(#str), 1, 3))
You can also create a function:
CREATE FUNCTION dbo.GetFormatTripleDash (#str varchar(255))
RETURNS VARCHAR(255)
AS
BEGIN
DECLARE #retStr VARCHAR(255) = REVERSE(SUBSTRING(REVERSE(#str), 7, 3))
+ CASE WHEN LEN(#str)>6 THEN '-' ELSE '' END
+ REVERSE(SUBSTRING(REVERSE(#str), 4, 3))
+ CASE WHEN LEN(#str)>3 THEN '-' ELSE '' END
+ REVERSE(SUBSTRING(REVERSE(#str), 1, 3))
RETURN(#retStr)
END
You can use this funtion like this:
-- 845-907-392
SELECT dbo.GetFormatTripleDash('845907392')
A more flexible solution using the function. Now you can use a much longer string value and you can define the part size separated by - character.
CREATE FUNCTION dbo.GetFormatDash (#str varchar(255), #partSize INT = 3)
RETURNS VARCHAR(255)
AS
BEGIN
DECLARE #startSize INT = 0;
DECLARE #retStr VARCHAR(255) = '';
WHILE #startSize < LEN(#str)
BEGIN
SET #retStr = REVERSE(SUBSTRING(REVERSE(#str), #startSize + 1, #partSize)) + CASE WHEN #startSize > 0 THEN '-' ELSE '' END + #retStr;
SET #startSize = #startSize + #partSize;
END
RETURN(#retStr)
END
You can use this improved function like this:
-- 12-345-678-901-234-567-890
SELECT dbo.GetFormatDash('12345678901234567890', DEFAULT)
SELECT dbo.GetFormatDash('12345678901234567890', 3)
-- 12345-67890-12345-67890
SELECT dbo.GetFormatDash('12345678901234567890', 5)
demo on dbfiddle.uk
Its a bit gross but it works! Try i
t and let me know if you agree
SELECT
CASE
WHEN LEN(yourColumn)>6 THEN format(CAST(YourColumn AS NUMERIC), '###-###-###')
WHEN LEN(YourColumn)>3 THEN format(CAST(YourColumn AS NUMERIC), '###-###')
ELSE YourColumn
END
Here is another way to do it, albeit more typing
SELECT
CASE
WHEN LEN(yourColumn)>6 THEN SUBSTRING(YourColumn, 1, 3) +'-'+ SUBSTRING(YourColumn, 4, 3)
WHEN LEN(YourColumn)>3 THEN REVERSE(SUBSTRING(REVERSE(YourColumn), 1, 3) +'-'+ SUBSTRING(REVERSE(YourColumn), 4, 3))
ELSE YourColumn
END
Try this. I'm sure it can also be extended to allow for any number of characters with a bit of effort
declare #input nvarchar(100) = '845907392'
declare #separator char(1) = '-'
--option1 - CTE
;with dash1 as (
select isnull(stuff(reverse(#input), 1+3, 0, #separator), reverse(#input)) as v
)
, dash2 as(
select isnull(stuff(v, 1+7, 0, #separator), v) as v from dash1
)
select reverse(v) from dash2
--option2 - Non CTE
select reverse(isnull(stuff(isnull(stuff(reverse(#input), 1+3, 0, #separator), reverse(#input)), 1+7, 0, #separator), isnull(stuff(reverse(#input), 1+3, 0, #separator), reverse(#input))))
Try this using format() function.
Select Replace(format(8, '#,##0'), ',', '-')
Select Replace(format(476, '#,##0'), ',', '-')
Select Replace(format(45907392, '#,##0'), ',', '-')
Select Replace(format(845907392, '#,##0'), ',', '-')
Live db<>fiddle demo.
A solution with a recursive CTE:
with cte as (
select col, len(col) - 3 pos from tablename
union all
select
cast(left(col, pos) + '-' + right(col, len(col) - pos) as varchar(100)),
pos - 3
from cte
where pos > 0
)
select col from cte
where pos <= 0
See the demo.
Results:
> | col |
> | :---------- |
> | 8 |
> | 476 |
> | 845-907-392 |
> | 45-907-392 |
> | 4-767 |

How to select a variable length string from between two known strings in SQL Server in a VARCHAR(MAX) where some columns don't have applicable strings

Using SQL Server 2012, I need to get from this example
ColumnName
--------------------------------
Enroll to: Carol Goals are many and varied
characters that don't include desired results
Enroll to: Jan Levinson Goals will be discussed at first encounter
Enroll to: Stephon-Anderson Goals none
NULL
Enroll to: David Goals --Note uneven spaces, Need David
to extract the column to look like:
Name
-----------
Carol
NULL
Jan Levinson
Stephon-Anderson
NULL
David
This code got me pretty close to the results I was looking for, but sometimes trimmed the name incorrectly.
Select
CASE WHEN AssignedTo like '%Enroll To:%' THEN SUBSTRING(AssignedTo, CHARINDEX('%Enroll To:%', AssignedTo) + LEN('%Enroll To:%')
,CHARINDEX('Goals', AssignedTo) - CHARINDEX('%Enroll To:%', AssignedTo) + LEN('Goals'))
ELSE 'None'
END AS 'Name'
FROM
(
Select
CASE WHEN ColumnName like '%Enroll To:%' THEN SUBSTRING (ColumnName, CHARINDEX('Enroll To:', ColumnName), 40)
ELSE 'None'
END AS 'AssignedTo'
FROM TABLE ) A
I cannot thank you enough!
This produced the desired result and seems to deal with variable length of the target string. Hope it helps someone.
DECLARE #pretext as NVARCHAR(100) = 'Enroll to:'
DECLARE #posttext as NVARCHAR(100) = 'Goals'
Select
,CASE When CHARINDEX(#posttext, ColumnName) - (CHARINDEX(#pretext, ColumnName) + len(#pretext)) < 0 THEN NULL
Else
SUBSTRING(ColumnName, CHARINDEX(#pretext, ColumnName) + len(#pretext)
,CHARINDEX(#posttext, ColumnName) - (CHARINDEX(#pretext, ColumnName) + len(#pretext)) )
END as betweentext
FROM TABLE
You can use apply and string functions:
select left(v.s1, charindex(' ', s1) - 1)
from t cross apply
(values (stuff(t.col, 1, 11, '')) v(s1)
Here is an alternative to Gordon's answer:
SELECT
SUBSTRING(ColumnName,
CHARINDEX(':', ColumnName) + 2,
CHARINDEX(' ', ColumnName, CHARINDEX(':', ColumnName) + 2) -
CHARINDEX(':', ColumnName) - 2) AS Name
FROM yourTable;
Demo
Here is your data to test for in table form:
declare #goals table (string nvarchar(255));
insert #goals values
('Enroll to: Carol Goals are many and varied characters that don''t include desired results'),
('Enroll to: Jan Levinson Goals will be discussed at first encounter'),
('Enroll to: Stephon-Anderson Goals none'),
(NULL),
('Enroll to: David Goals '), --Note uneven spaces, Need David
(' '); -- I (psw) added this
And the following code seems to do what you desire without error. But it is assuming that your sentence after the name will always start with "Goals".
select *,
result =
case
when isValid = 1 then
ltrim(rtrim(
substring(string, colonPos + 1, goalsPos - colonPos - 1)
))
end
from #goals
cross apply (select
colonPos = charindex(':', string),
goalsPos = patIndex('%goals%', string)
) positions
cross apply (select
isValid =
case
when colonPos = 0 or goalsPos = 0 or colonPos > goalsPos then 0
else 1
end
) validity

Cut a part of value in SQL Server using substring?

I have this data in my column:
32-HC-100-10001-G03P2-N-1-1001
The problem is my value doesn't have a fixed length. What I need to do is split this value into 2 columns 32-HC-100-10001-G03P2-N and 1 - the numbers after last - don't important
Another example
4-G-100-10029-F23S-S-2-1001
should be split into 4-G-100-10029-F23S-S and 2. I have used SUBSTRING([Line No#], 0, 21) but because of the length it didn't work.
Try this way
declare #str varchar(100)=reverse('4-G-100-10029-F23S-S-2-1001')
select reverse(substring(#str,charindex('-',#str)+1,len(#str))) as first_col,
left(substring(#str,charindex('-',#str)+1,len(#str)),charindex('-',substring(#str,charindex('-',#str)+1,len(#str)))-1) as second_col
May not be the shortest method but should get the job done
Note : I did not hard-code any length here
As long as the last part(1-1001,2-2002...) have the same number of values,this will work..
declare #string varchar(max)
set #string='32-HC-100-10001-G03P2-N-1-1001'
select replace(#string, right(#string,7),''),substring(right(#string,6),1,1)
Output:
32-HC-100-10001-G03P2-N 1
When doing complex string operations in SQL Server, one method uses outer apply to simplify the calculations:
select t.col, s2.firstpart, s2.secondpart
from t outer apply
(select left(col, len(col) - charindex('-', reverse(col)) as s1
-- remove the last number
) s1 outer apply
(select left(s1, len(s1) - charindex('-', reverse(s1)) as firstpart,
right(s1, charindex('-', reverse(s1)) -1) as secondpart
) s2;
I find the calculations easier to construct, follow, and debug.
You can try this:
DECLARE #string nvarchar(max) = '32-HC-100-10001-G03P2-N-1-1001'
SELECT REVERSE(STUFF(SUBSTRING(REVERSE(#string),CHARINDEX('-',REVERSE(#string))+1,LEN(#string)),1,CHARINDEX('-',SUBSTRING(REVERSE(#string),CHARINDEX('-',REVERSE(#string))+1,LEN(#string))),'')),
REVERSE(LEFT(SUBSTRING(REVERSE(#string),CHARINDEX('-',REVERSE(#string))+1,LEN(#string)),CHARINDEX('-',SUBSTRING(REVERSE(#string),CHARINDEX('-',REVERSE(#string)),LEN(#string)))))
Output:
32-HC-100-10001-G03P2-N 1
If it is always comes as a 7th part you can use XML:
DECLARE #string nvarchar(max) = '32-HC-100-10001-G03P2-N-1-1001',
#xml xml
SELECT #xml = CAST('<d>'+REPLACE(#string,'-','</d><d>') +'</d>' as xml)
SELECT t.v.value('/d[1]','nvarchar(10)') + '-' +
t.v.value('/d[2]','nvarchar(10)') + '-' +
t.v.value('/d[3]','nvarchar(10)') + '-' +
t.v.value('/d[4]','nvarchar(10)') + '-' +
t.v.value('/d[5]','nvarchar(10)') + '-' +
t.v.value('/d[6]','nvarchar(10)'),
t.v.value('/d[7]','nvarchar(10)')
FROM #xml.nodes('/') as t(v)
Output:
32-HC-100-10001-G03P2-N 1

Resources