Substring, Charindex - sql

I'm trying to extract a code that varies in length that exists after the first two underscores and before the third underscore in a field in a table. The table looks something like this. There are greater than 30 codes and I need to extract the code as part of a query.
code
====
XX_YYY_CODE1_ZZZ
XX_YYY_CODE2_ZZZ
XX_YYY_CODE3_ZZZ
XX_YYY_CODE4_ZZZ
...
I've tried using this code however this gives me YYY rather than the CODE I'm after and I can't work out how to re-engineer it to do what I want it to do.
select
left(SUBSTRING(code,
CHARINDEX('_', code) + 1, len(code)),
CHARINDEX('_',SUBSTRING(code,CHARINDEX('_', code) + 2, LEN(code))))

if only four underscore,Use below code
select parsename(replace('1_2_3_4','_','.'),2)

Try this expression instead:
SELECT SUBSTRING
(code,
CHARINDEX('_', code, CHARINDEX('_', code) + 1) + 1,
LEN(code) -
(CHARINDEX('_', code, CHARINDEX('_', code) + 1) + 1) -
CHARINDEX('_',REVERSE(code))
)

Use below code,I hope it is give your expected result :
CREATE TABLE #Table(GvnString VARCHAR(100))
INSERT INTO #Table( GvnString )
SELECT 'XX_YYY_Code1_ZZZ' UNION ALL
SELECT 'XX_YYY_Code2_ZZZ' UNION ALL
SELECT 'XX_YYY_Code3_ZZZ' UNION ALL
SELECT 'XX_YYY_Code4_ZZZ'
SELECT SUBSTRING(GvnString, CHARINDEX('_', GvnString, CHARINDEX('_',
GvnString) + 1) + 1, LEN(GvnString) - CHARINDEX('_', GvnString,
CHARINDEX('_', GvnString) + 1))
FROM #Table

I am reversing the string and doing the substring operation.
CREATE TABLE #MyTable(Code VARCHAR(50))
INSERT INTO #MyTable( Code)
SELECT 'XX_YYY_Code1_ZZZ' UNION ALL
SELECT 'XX_YYY_Code2_ZZZ' UNION ALL
SELECT 'XX_YYY_Code3_ZZZ' UNION ALL
SELECT 'XX_YYY_Code4_ZZZ'
select Reverse(Substring
(substring (Reverse(code),charindex('_',Reverse(code))+1,Len(code)),0,
CHARINDEX('_',substring (Reverse(code),charindex('_',Reverse(code))+1,Len(code)))))
from #Table

Just modified your code. It should work.
select
SUBSTRING(
substring(substring(code,CHARINDEX('_', code)+1,len(code))
,CHARINDEX('_', substring(code
,CHARINDEX('_', code)+1,len(code)))+1
,len(code)),1,
LEN(substring(substring(code,CHARINDEX('_', code)+1,len(code))
,CHARINDEX('_', substring(code
,CHARINDEX('_', code)+1,len(code)))+1
,len(code)))
- len(substring(substring(substring(code,CHARINDEX('_', code)+1,len(code))
,CHARINDEX('_', substring(code
,CHARINDEX('_', code)+1,len(code)))+1
,len(code)),CHARINDEX('_', substring(substring(code,CHARINDEX('_', code)+1,len(code))
,CHARINDEX('_', substring(code
,CHARINDEX('_', code)+1,len(code)))+1
,len(code)))+1,
LEN(substring(substring(code,CHARINDEX('_', code)+1,len(code))
,CHARINDEX('_', substring(code
,CHARINDEX('_', code)+1,len(code)))+1
,len(code)))))-1)

A slightly modified answer from dotNET does what I needed. Examples below;
SELECT SUBSTRING
('XX_YYY_LongCode_ZZZ',
CHARINDEX('_', 'XX_YYY_LongCode_ZZZ', CHARINDEX
('_', 'XX_YYY_LongCode_ZZZ') + 1) + 1,
LEN('XX_YYY_LongCode_ZZZ') -
(CHARINDEX('_', 'XX_YYY_LongCode_ZZZ', CHARINDEX
('_', 'XX_YYY_LongCode_ZZZ') + 1)) -
CHARINDEX('_',REVERSE('XX_YYY_LongCode_ZZZ'))
)
Or another example
SELECT SUBSTRING
('XX_YYY_otherCode_ZZZ',
CHARINDEX('_', 'XX_YYY_otherCode_ZZZ', CHARINDEX
('_', 'XX_YYY_otherCode_ZZZ') + 1) + 1,
LEN('XX_YYY_otherCode_ZZZ') -
(CHARINDEX('_', 'XX_YYY_otherCode_ZZZ', CHARINDEX
('_', 'XX_YYY_otherCode_ZZZ') + 1)) -
CHARINDEX('_',REVERSE('XX_YYY_otherCode_ZZZ'))
)

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

Remove characters between ~~ sql

My data is showing as "abcdefghijklmno~123~pqrstuvwzyz"
I want to remove this part ~123~
I want to get data as "abcdefghijklmnopqrstuvwzyz" after remove this part ~123~
You could use substring operations here:
SELECT
col,
SUBSTRING(col, 1, CHARINDEX('~', col) - 1) +
SUBSTRING(col, CHARINDEX('~', col, CHARINDEX('~', col) + 1) + 1, LEN(col)) AS output
FROM yourTable;
Data:
WITH yourTable AS (
SELECT 'abcdefghijklmno~123~pqrstuvwzyz' AS col
)
Demo

Concatenating various characters

I have a series of ID numbers like this
ABC/12345/2012
DEF/67891/2013
GHI/23456/2014
KLM/78911/2014
I need to change them so they look like this
12-12345
13-67891
14-23456
14-78911
14-6634
The below works to a degree but I have a few that only have 4 numbers in, they should be proceeded by a zero.
SELECT RIGHT(ID, 2)+'-'+RIGHT(SUBSTRING(ID, CHARINDEX('/', ID, 1)-1, LEN(ID)-7), 5)
12-12345
13-67891
14-23456
14-78911
14-/6634
So I need 14-/6634 to look like 14-06634
Assuming your column name is ID, and length of each substrings between the '/' characters is not variable (ABC = 3, 12345 = 5, 2012 = 4):
SELECT RIGHT(ID, 2)+'-'+RIGHT(SUBSTRING(ID, CHARINDEX('/', ID, 1)-1, LEN(ID)-7), 5)
Based on your main post edit:
SELECT RIGHT(ID, 2)+'-'+REPLACE(RIGHT(SUBSTRING(ID, CHARINDEX('/', ID, 1)-1, LEN(ID)-7), 5), '/', '0')
Try this
declare #tmp varchar(50) = 'ABC/12345/2012'
select SUBSTRING(#tmp, len(#tmp) - 1, 2) + '-' + SUBSTRING(#tmp,CHARINDEX('/',#tmp)+1,LEN(#tmp))
it gives you
12-12345/2012
and now you have to remove /2012
If your data is fixed format, then you can use PARSENAME
Sample execution with sample data:
DECLARE #TestTable TABLE (TestData VARCHAR (50));
INSERT INTO #TestTable (TestData)
SELECT 'ABC/12345/2012' UNION
SELECT 'DEF/67891/2013' UNION
SELECT 'GHI/23456/2014' UNION
SELECT 'KLM/78911/2014'
SELECT RIGHT(PARSENAME(REPLACE(TestData, '/', '.'), 1), 2) + '-' +
PARSENAME(REPLACE(TestData, '/', '.'), 2) AS TestData
FROM #TestTable
Result:
TestData
--------
12-12345
13-67891
14-23456
14-78911

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.

how to separate text from integer

I have addresses:
ALKOŅU 3-20;
M.LUBŠNAS 16V-9;
STIEBRU 6-22;
ANDREJA UPĪĀA IELA 16-2;
MISNKAS 4 -115;
CISKADI,BAZNICAS 4;
How it is possible in sql to separate first text part (district) from integer (house and flat number)?
Assuming the break-point is ALWAYS the first digit, then
SELECT RTRIM(LEFT(col, PATINDEX('%[0-9]%', col + '0') -1)) as District,
STUFF(col, 1, PATINDEX('%[0-9]%', col + '0') -1, '') as HouseAndFlat
FROM ...
e.g.
with t(col) as (
select
'ALKOŅU 3-20' union all select
'M.LUBŠNAS 16V-9' union all select
'STIEBRU 6-22' union all select
'ANDREJA UPĪĀA IELA 16-2' union all select
'MISNKAS 4 -115' union all select
'CISKADI,BAZNICAS 4')
SELECT RTRIM(LEFT(col, PATINDEX('%[0-9]%', col + '0') -1)) as District,
STUFF(col, 1, PATINDEX('%[0-9]%', col + '0') -1, '') as HouseAndFlat
FROM t