Retrieve number from string most effective way - sql

I'm wondering if there is better,more optimal way to retrieve a number from string
eg.
"O5_KK/text1/1239312/006_textrandom"
"O5_KK/text1/1239315/0109_textrandom123"
"O5_KK/text1/1239318/0110_textrandom432"
'O5_KK/text1' - hardcoded, never change.
1239312,1239315,1239318 - random number, unique within row
textrandom,textrandom123,textrandom432 - random string
as output I would like to get only numbers:
006
0109
0110
I know how to do it by using instr,substr,replace function. But it looks terrible to read. I'm looking for other solution, any hints ?
Thanks

You can use regexp_subtr():
select regexp_substr(val, '/[0-9]+_', 1, 1)
And then remove the extra characters:
select replace(replace(regexp_substr(val, '/[0-9]+_', 1, 1), '/', ''), '_', '')

This is simply the part after the third slash before the second underscore:
substr(str, instr(str, '/', 1, 3) + 1, instr(str, '_', 1, 2) - instr(str, '/', 1, 3) - 1)

Assuming the number you need is always between the last slash(/) and the last underscore (_), with no characters in between - as in your sample - the best solution uses just substr and instr. Note that regular expressions are slower than straight substr and instr; there are cases when the only solution is regexpr (or regexpr is much easier to write and maintain), but this is not one of those cases.
select substr(val, instr(val,'/',-1)+1, instr(val,'_',-1)-instr(val,'/',-1)-1)

Related

How to get the substring before the last occurrence of a character?

In oracle I have a string that looks like this:
\A\B\C\D
Now I would like to get the substring before the last occurrence of \. I don't know how long the string in total will be, as A, B, C and D can differ in length, and the number of \ might vary as well.
What is the most efficient way to get \A\B\C\?
You could use a combination of instr (with -1) and substr:
with data as (select '\A\B\C\D' str from dual)
select str, substr(str, 1, instr(str, '\', -1))
from data;
You can use regexp_replace():
select regexp_replace(str, '[^\\]+$', '')
Here is a db<>fiddle.
A solution through a Regex function might be
SELECT REGEXP_REPLACE(str, '[^\\]+$')
FROM t
Demo
if it is desired that that last occurrence of the \ character be skipped also, then this might work.
REGEXP_SUBSTR (str,'(.*)(\.*$)',
1,
1,
NULL,
1)
this was learned from this post https://stackoverflow.com/a/63458155/18021603

SQL Server - Splitting a string

I have the below string which is currently in one column, called Column1, in a table called dbo.faresnumb
Column1
512.65USD/52.65USD/0167025354825
I want to create three new columns from this string called Fare1, Fare2, Number1 like below
Fare1 Fare2 Number1
512.65USD 52.65USD 0167025354825
So the delimter is the / and the character count will not always be the exact same. For example, Fare1 could be 54.00USD rather than 512.65USD and I still need to capture everything before the first /. I know is SAS there is a way to scan a substring to find a delimiter and assign new variables, but I am new to SQL. Any help would be much appreciated!
Thanks,
Bennett
Such string operations are a pain in SQL Server. You can do:
select parts.part1 as fare1,
left(rest, charindex('/', rest) - 1) as fare2,
stuff(rest, 1, charindex('/', rest), '') as number1
from t cross apply
(values (left(col, charindex('/', col) - 1),
stuff(col, 1, charindex('/', col), '')
)
) parts(part1, rest);
Here is a rextester.
Sadly, SQL Server 2016 offers split_string() which makes this simpler. The sad part is that it does not return the position of the substring in the string. That is needed to ensure that the three extracted values are in the right order.
One more way to achieve the same using a bit of XML -
select cast(concat('<x>', REPLACE(Column1, '/', '</x><x>'), '</x>') as xml).value('/x[1]','varchar(100)'),
cast(concat('<x>', REPLACE(Column1, '/', '</x><x>'), '</x>') as xml).value('/x[2]','varchar(100)'),
cast(concat('<x>', REPLACE(Column1, '/', '</x><x>'), '</x>') as xml).value('/x[3]','varchar(100)') from dbo.faresnumb

Removal of first characters in a string oracle sql

It may be very simple question, but I have run out of ideas.
I would like to remove first 5 characters from string.
Example string will look like:
1Y40K100R
I would like to display only digits that are after '%K' which in this case should give me result of 100R.
Please note that number after 'K' can have different amount of digits. It can be 4 digit number or 2 digit number.
Just use substr():
select substr(col, 6)
This returns all characters starting at the sixth.
There are multiple ways to return all characters after the k. If you know the string has a k, then use instr():
select substr(col, instr(col, 'K') + 1)
You can use regexp_substr
select regexp_substr('1Y40K100R', '(K)(.*)', 1, 1, 'i', 2) from dual
A way without regexp:
select substr('1Y40K100R', instr('1Y40K100R', 'K') +1) from dual
This may appear not so elegant, but it usually performs better than the regexp way.

Splitting a string with regex whitespace delimitor

I have a string with this delimitor ' - ' (hyphen surrounded by spaces).
I have no problem getting each part of the string:
select regexp_substr(val, '[^-]+', 1, 1),
regexp_substr(val, '[^-]+', 1, 2)
from (select 'first - second' as val from dual) t
However I would like to exclude spaces between each term and the hyphen. I tried this:
regexp_substr(val, '[^\s-]+', 1, 1)
regexp_substr(val, '[^-\s]+', 1, 2)
But it doesn't seem to work. THe first expression returns fir,
1) I wonder why?
Here is the link to Fiddle:
http://www.sqlfiddle.com/#!4/d41d8/41570
2) How can I solve this?
If you want to split your string so you don't need Negated Character Classes,you can split with following pattern:
'[\s-]+'
And if you just want to find the words you can use :
\b[a-zA-Z]+\b
If you want to remove spaces from the beginning and end, then I think the simplest approach is to use trim():
select trim(regexp_substr(val, '[^-]+', 1, 1)),
trim(regexp_substr(val, '[^-]+', 1, 2))
from (select 'first - second' as val from dual) t
Capture your target in a group and have the function return that:
select regexp_substr(val, '(.*) *- *', 1, 1, '', 1),
regexp_substr(val, ' *- *(.*)', 1, 1, '', 1)
from (select 'first - second' as val from dual) t
See SQLFiddle
This allows you to express more precisely your delimiter. This technique is the next best thing to using a look ahead expression, which would be the approach most favoured in regex engines that support it and which Oracle does not support.
To explain what's going on, the target is captured using brackets and it returned using a back reference - the last parameter - naming group 1 (the first group of the expression) to be returned.
FYI group 0 is the whole match.

Split String by delimiter position using oracle SQL

I have a string and I would like to split that string by delimiter at a certain position.
For example, my String is F/P/O and the result I am looking for is:
Therefore, I would like to separate the string by the furthest delimiter.
Note: some of my strings are F/O also for which my SQL below works fine and returns desired result.
The SQL I wrote is as follows:
SELECT Substr('F/P/O', 1, Instr('F/P/O', '/') - 1) part1,
Substr('F/P/O', Instr('F/P/O', '/') + 1) part2
FROM dual
and the result is:
Why is this happening and how can I fix it?
Therefore, I would like to separate the string by the furthest delimiter.
I know this is an old question, but this is a simple requirement for which SUBSTR and INSTR would suffice. REGEXP are still slower and CPU intensive operations than the old subtsr and instr functions.
SQL> WITH DATA AS
2 ( SELECT 'F/P/O' str FROM dual
3 )
4 SELECT SUBSTR(str, 1, Instr(str, '/', -1, 1) -1) part1,
5 SUBSTR(str, Instr(str, '/', -1, 1) +1) part2
6 FROM DATA
7 /
PART1 PART2
----- -----
F/P O
As you said you want the furthest delimiter, it would mean the first delimiter from the reverse.
You approach was fine, but you were missing the start_position in INSTR. If the start_position is negative, the INSTR function counts back start_position number of characters from the end of string and then searches towards the beginning of string.
You want to use regexp_substr() for this. This should work for your example:
select regexp_substr(val, '[^/]+/[^/]+', 1, 1) as part1,
regexp_substr(val, '[^/]+$', 1, 1) as part2
from (select 'F/P/O' as val from dual) t
Here, by the way, is the SQL Fiddle.
Oops. I missed the part of the question where it says the last delimiter. For that, we can use regex_replace() for the first part:
select regexp_replace(val, '/[^/]+$', '', 1, 1) as part1,
regexp_substr(val, '[^/]+$', 1, 1) as part2
from (select 'F/P/O' as val from dual) t
And here is this corresponding SQL Fiddle.