Oracle REGEXP_REPLACE for both space and "%" at the same time - sql

I have following Oracle SQL code:
SELECT TO_NUMBER(TRIM(REGEXP_REPLACE(per_growth, '(%)(\s)')),
'FM99999999999999999990D099999999999999999',
'NLS_NUMERIC_CHARACTERS = '', ''') AS per_growth
FROM sometable;
This code supposed to look for percentage sign first then space and exclude them from the result. However, it is showing
ORA-01722: invalid number
error. I am learning sql yet and do not know exact cause. Is it something went wrong with (%)(\s)? The value in the table is 50%

You can use TRANSLATE to get rid of all instances of unwanted characters:
SELECT TO_NUMBER(
TRANSLATE(per_growth, '0% ', '0'),
'FM99999999999999999990D099999999999999999',
'NLS_NUMERIC_CHARACTERS = '', '''
) as per_growth
FROM sometable;
Note: TRANSLATE(expr, from_string, to_string) works by swapping all instances of the characters in from_string with the corresponding characters in to_string and if there are more characters in from_string than to_string then the remaining characters are removed. It is faster than using regular expressions and on a par with using REPLACE but it can handle multiple replacements at once, which REPLACE cannot.
If you did want to use the slower REGEXP_REPLACE then you can replace all whitespace characters and all percent characters, whether together or not, using:
SELECT TO_NUMBER(
REGEXP_REPLACE(per_growth, '[%[:space:]]'),
'FM99999999999999999990D099999999999999999',
'NLS_NUMERIC_CHARACTERS = '', '''
) as per_growth
FROM sometable;
Which, for the sample data:
CREATE TABLE sometable (per_growth) AS
SELECT '1%' FROM DUAL UNION ALL
SELECT '%2' FROM DUAL UNION ALL
SELECT '3 %' FROM DUAL UNION ALL
SELECT '4% ' FROM DUAL UNION ALL
SELECT '5,0%' FROM DUAL UNION ALL
SELECT '%%% 123 456 789,0123456 %' FROM DUAL;
Both output:
PER_GROWTH
1
2
3
4
5
123456789.0123456
db<>fiddle here

Did you try good, old REPLACE?
select replace(replace(per_growth, '%', ''), ' ', '') as result
from your_table

You can use
REGEXP_REPLACE(per_growth, '( )(%)')
in order to get rid of % sign and whitespace(s) together
or
TRIM(REPLACE(per_growth,'%'))
to get rid of % sign first, and then leading and trailing spaces next,
before numerical conversion.

Related

How to get first string after character Oracle SQL

I'm trying to get first string after a character.
Example is like
ABCDEF||GHJ||WERT
I need only
GHJ
I tried to use REGEXP but i couldnt do it.
Can anyone help me with please?
Thank you
Somewhat simpler:
SQL> select regexp_substr('ABCDEF||GHJ||WERT', '\w+', 1, 2) result from dual;
^
RES |
--- give me the 2nd "word"
GHJ
SQL>
which reads as: give me the 2nd word out of that string. Won't work properly if GHJ consists of several words (but that's not what your example suggests).
Something like I interpret with a separator in place, In this case it is || or | example is with oracle database
-- pattern -- > [^] represents non-matching character and + for says one or more character followed by ||
-- 3rd parameter --> starting position
-- 4th parameter --> nth occurrence
WITH tbl(str) AS
(SELECT 'ABCDEF||GHJ||WERT' str FROM dual)
SELECT regexp_substr(str
,'[^||]+'
,1
,2) output
FROM tbl;
I think the most general solution is:
WITH tbl(str) AS (
SELECT 'ABCDEF||GHJ||WERT' str FROM dual UNION ALL
SELECT 'ABC|DEF||GHJ||WERT' str FROM dual UNION ALL
SELECT 'ABClDEF||GHJ||WERT' str FROM dual
)
SELECT regexp_replace(str, '^.*\|\|(.*)\|\|.*', '\1')
FROM tbl;
Note that this works even if the individual elements contain punctuation or a single vertical bar -- which the other solutions do not. Here is a comparison.
Presumably, the double vertical bar is being used for maximum flexibility.
You should use regexp_substr function
select regexp_substr('ABCDEF||GHJ||WERT ', '\|{2}([^|]+)', 1, 1, 'i', 1) str
from dual;
STR
---
GHJ

Remove 2 characters in oracle sql

I have a column that contains 12 digits but user wants only to generate a 10 digits.
I tried the trim, ltrim function but nothing work. Below are the queries I tried.
ltrim('10', 'column_name')
ltrim('10', column_name)
ltrim(10, column_name)
For example I have a column that contains a 12 digit number
100000000123
100000000456
100000000789
and the expected result I want is
0000000123
0000000456
0000000789
To extract the last 10 characters of an input string, regardless of how long the string is (so this will work if some inputs have 10 characters, some 12, and some 15 characters), you could use negative starting position in substr:
substr(column_name, -10)
For example:
with
my_table(column_name) as (
select '0123401234' from dual union all
select '0001112223334' from dual union all
select '12345' from dual union all
select '012345012345' from dual
)
select column_name, substr(column_name, -10) as substr
from my_table;
COLUMN_NAME SUBSTR
------------- ----------
0123401234 0123401234
0001112223334 1112223334
12345
012345012345 2345012345
Note in particular the third example. The input has only 5 digits, so obviously you can't get a 10 digit number from it. The result is NULL (undefined).
Note also that if you use something like substr(column_name, 3) you will get just '345' in that case; most likely not the desired result.
try to use SUBSTR(column_name, 2)

Query to remove all non-digit but only keep last period/dot

Struggle to design a regular expression to filter field value from varchar2 to number, so that it can remove all non-digit and only left the last period in the string, so that
"about 1,000.00" return 1000.00 or 1000
"3,000,000.000" return 300000.000 or 3000000
"3.000.000.000" return return 3000000.000 or 3000000
"a^*3^%*(C4.5d*9" return 34.59
Any method just change the string into accurate convertible string that can be converted by to_number()
I use
SELECT REGEXP_REPLACE(field_value, '[^0-9\.]+', '') from dual;
but can't resolve the 3rd case....
Because the regex in oracle are somewhat limited I don't think it's possible only using regexp_replace. You could do a workaround like this:
SELECT
CASE
WHEN last_dot < 2 THEN digits_and_dots
ELSE REPLACE(SUBSTR(digits_and_dots, 1, last_dot - 1), '.') ||
SUBSTR(digits_and_dots, last_dot)
END
FROM (
SELECT
INSTR(digits_and_dots, '.', -1) last_dot,
digits_and_dots
FROM (
SELECT
REGEXP_REPLACE(field_value, '[^0-9\.]+', '') digits_and_dots
FROM DUAL
) t
) o
Here's a way to do it, assuming there is one decimal character. The value you are working with is a string so I think of the decimal that we want to keep as a separator of the string and split it into 2 parts based on that. The first part is all characters leading up to but not including the last decimal, the second part is the last decimal and all characters after it. Then apply the replace, getting rid of everything that is not a number from the first part, and everything that is not a number or a decimal from the second part, then concatenate them together. Needs more testing with varied inputs but you get the idea. All these regular expressions are kind of expensive though so I doubt this will be the fastest solution.
with tbl(str) as (
select 'about 1,000.00' from dual union
select '3,000,000.000' from dual union
select '3.000.000.000' from dual union
select 'a^*3^%*(C4.5d*9' from dual
)
select str original,
regexp_replace(regexp_substr(str, '^(.*)\.', 1, 1, NULL, 1), '[^0-9]+', '') ||
regexp_replace(regexp_substr(str, '.*(\..*)$', 1, 1, NULL, 1), '[^0-9\.]+', '') Converted
from tbl;
SQL> /
ORIGINAL CONVERTED
--------------- ---------------
3,000,000.000 3000000.000
3.000.000.000 3000000.000
a^*3^%*(C4.5d*9 34.59
about 1,000.00 1000.00
SQL>
Shortest way is as follows:
select regexp_substr('a^*3^%*(C4.5d*9s','\d+\.\d+') from dual;
or
select regexp_replace('a^*3^%*(C4.5d*9s', '[^0.0-9]', '') from dual;

How to remove specific value from comma separated string in oracle

I want remove specific value from comma separated sting using oracle.
Sample Input -
col
1,2,3,4,5
Suppose i want to remove 3 from the string.
Sample Output -
col
1,2,4,5
Please suggest how i can do this using oracle query.
Thanks.
Here is a solution that uses only standard string functions (rather than regular expressions) - which should result in faster execution in most cases; it removes 3 only when it is the first character followed by comma, the last character preceded by comma, or preceded and followed by comma, and it removes the comma that precedes it in the middle case and it removes the comma that follows it in the first and third case.
It is able to remove two 3's in a row (which some of the other solutions offered are not able to do) while leaving in place consecutive commas (which presumably stand in for NULL) and do not disturb numbers like 38 or 123.
The strategy is to first double up every comma (replace , with ,,) and append and prepend a comma (to the beginning and the end of the string). Then remove every occurrence of ,3,. From what is left, replace every ,, back with a single , and finally remove the leading and trailing ,.
with
test_data ( str ) as (
select '1,2,3,4,5' from dual union all
select '1,2,3,3,4,4,5' from dual union all
select '12,34,5' from dual union all
select '1,,,3,3,3,4' from dual
)
select str,
trim(both ',' from
replace( replace(',' || replace(str, ',', ',,') || ',', ',3,'), ',,', ',')
) as new_str
from test_data
;
STR NEW_STR
------------- ----------
1,2,3,4,5 1,2,4,5
1,2,3,3,4,4,5 1,2,4,4,5
12,34,5 12,34,5
1,,,3,3,3,4 1,,,4
4 rows selected.
Note As pointed out by MT0 (see Comments below), this will trim too much if the original string begins or ends with commas. To cover that case, instead of wrapping everything within trim(both ',' from ...) I should wrap the rest within a subquery, and use something like substr(new_str, 2, length(new_str) - 2) in the outer query.
Here is one method:
select trim(both ',' from replace(',' || '1,2,3,4,5' || ',', ',' || '3' || ',', ','))
That said, storing comma-delimited strings is a really, really bad idea. There is almost no reason to do such a thing. Oracle supports JSON, XML, and nested tables -- all of which are better alternatives.
The need to remove an element suggests a poor data design.
You can convert the list rows using an XMLTABLE, filter to remove the unwanted rows and then re-aggregate them:
SELECT LISTAGG( x.value.getStringVal(), ',' ) WITHIN GROUP ( ORDER BY idx )
FROM XMLTABLE(
( '1,2,3,4,5' )
COLUMNS value XMLTYPE PATH '.',
idx FOR ORDINALITY
) x
WHERE x.value.getStringVal() != 3;
For a simple filter this is probably not worth it and you should use something like (based on #mathguy's solution):
SELECT SUBSTR( new_list, 2, LENGTH( new_list ) - 2 ) AS new_list
FROM (
SELECT REPLACE(
REPLACE(
',' || REPLACE( :list, ',', ',,' ) || ',',
',' || :value_to_replace || ','
),
',,',
','
) AS new_list
FROM DUAL
)
However, if the filtering is more complicated then it might be worth converting the list to rows, filtering and re-aggregating.
I do not knwo how to do this in Oracle, but with SQL-Server I'd use a trick:
convert the list to XML by replacing the comma with tags
use XQuery to filter the data
reconcatenate
This is SQL Server syntax but might point you the direction:
declare #s varchar(100)='1,2,2,3,3,4';
declare #exclude int=3;
WITH Casted AS
(
SELECT CAST('<x>' + REPLACE(#s,',','</x><x>') + '</x>' AS XML) AS TheXml
)
SELECT x.value('.','int')
FROM Casted
CROSS APPLY TheXml.nodes('/x[text()!=sql:variable("#exclude")]') AS A(x)
UPDATE
I just found this answer which seems to show pretty well how to start...
I agree with Gordon regarding the fact that storing comma delimited data in a column is a really bad idea.
I just preceed the csv with a ',', then use the replace function followed by a left trim function to clean-up the preceeding ','.
SCOTT#tst>VAR b_number varchar2(5);
SCOTT#tst>EXEC :b_number:= '3';
PL/SQL procedure successfully completed.
SCOTT#tst>WITH srce AS (
2 SELECT
3 ',' || '3,1,2,3,3,4,5,3' col
4 FROM
5 dual
6 ) SELECT
7 ltrim(replace(col,',' ||:b_number),',') col
8 FROM
9 srce;
COL
1,2,4,5

Using REGEXP_SUBSTR with Strings Qualifier

Getting Examples from similar Stack Overflow threads,
Remove all characters after a specific character in PL/SQL
and
How to Select a substring in Oracle SQL up to a specific character?
I would want to retrieve only the first characters before the occurrence of a string.
Example:
STRING_EXAMPLE
TREE_OF_APPLES
The Resulting Data set should only show only STRING_EXAM and TREE_OF_AP because PLE is my delimiter
Whenever i use the below REGEXP_SUBSTR, It gets only STRING_ because REGEXP_SUBSTR treats PLE as separate expressions (P, L and E), not as a single expression (PLE).
SELECT REGEXP_SUBSTR('STRING_EXAMPLE','[^PLE]+',1,1) from dual;
How can i do this without using numerous INSTRs and SUBSTRs?
Thank you.
The problem with your query is that if you use [^PLE] it would match any characters other than P or L or E. You are looking for an occurence of PLE consecutively. So, use
select REGEXP_SUBSTR(colname,'(.+)PLE',1,1,null,1)
from tablename
This returns the substring up to the last occurrence of PLE in the string.
If the string contains multiple instances of PLE and only the substring up to the first occurrence needs to be extracted, use
select REGEXP_SUBSTR(colname,'(.+?)PLE',1,1,null,1)
from tablename
Why use regular expressions for this?
select substr(colname, 1, instr(colname, 'PLE')-1) from...
would be more efficient.
with
inputs( colname ) as (
select 'FIRST_EXAMPLE' from dual union all
select 'IMPLEMENTATION' from dual union all
select 'PARIS' from dual union all
select 'PLEONASM' from dual
)
select colname, substr(colname, 1, instr(colname, 'PLE')-1) as result
from inputs
;
COLNAME RESULT
-------------- ----------
FIRST_EXAMPLE FIRST_EXAM
IMPLEMENTATION IM
PARIS
PLEONASM