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
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
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
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
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.
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