How to replace occurrence only on the start of the string in Oracle SQL? - oracle11g

I have a source column and I want to search for string values starting with 05, 5 971971 and 97105 to be replaced by 9715. As showin in output table.
SOURCE OUTPUT
0514377920 971514377920
544233920 971544233920
971971511233920 971511233920
9710511233920 971511233920
I tried following which works for first case.
SELECT REGEXP_REPLACE ('0544377905', '^(\05*)', '9715')FROM dual;
But following is not working, for second case:
SELECT REGEXP_REPLACE ('544377905', '^(\5*)', '9715')FROM dual;
Something is wrong with my regular expression. As I am getting: ORA-12727: invalid back reference in regular expression.

You can provide your four patterns using alternation; that is, in parentheses with a vertical bar between them:
with t(source) as (
select '0514377920' from dual
union all select '544233920' from dual
union all select '971971511233920' from dual
union all select '9710511233920' from dual
)
SELECT source, REGEXP_REPLACE (source, '^(05|5|9719715|97105)', '9715') as output
FROM t;
SOURCE OUTPUT
--------------- --------------------
0514377920 971514377920
544233920 971544233920
971971511233920 971511233920
9710511233920 971511233920
Depending on your data and any other restrictions you have, you may be able to make it as simple as replacing the first part of any string that has a 5 in it, which works for your small sample:
SELECT source, REGEXP_REPLACE (source, '^.[^5]?5', '9715') as output
FROM t;
That matches zero or more characters that are not 5, followed by a 5. That may be too simplistic for your real situation though.

Related

How to extract specific string until blank space/next line from a text in Oracle?

I am trying to extract the following from the text field using Regrex in Oracle.
For example
"This is example,
and this really a example :h,j,j,j,j,
l //Updated question , as this letter is on the next line
now this is a disease:yes"
I am expecting a result as h,j,j,j,j,l, but if I use
REGEXP_SUBSTR(text_field,'example :[^:]+,') AS Result
I am getting example:h,j,j,j,j
But I am not getting the last letter 'l' like above and I am guessing that's because it's on the next line.Also, if I want the string "disease:yes" only, that will be so helpful as well. Thank you much!
The result you are getting is because your pattern includes the word 'example' and ends with a comma, leaving out the ending 'l'. Try this form instead. Note the example is shown using a Common table Expression (CTE). The WITH statement creates the table called tbl which just sets up test data, kind of like a temp table. This is also a great way to set up data when asking a question. This form of the REGEXP_SUBSTR() function uses a captured group, which is the set of characters after the string 'example:' until the end of that line in the multi-line field. From this you should be able to get the other string you are after. Give it a go.
WITH tbl(text_field) AS (
SELECT 'This is example,
and this really a example :h,j,j,j,j,l
now this is a disease:yes' FROM dual
)
SELECT REGEXP_SUBSTR(text_field,'example :(.*)', 1, 1, NULL, 1) AS Result
FROM tbl;
RESULT
-----------
h,j,j,j,j,l
1 row selected.
Edit based on new info. Since that last letter could be on it's own line, you'll need to allow for the newline. Use the 'n' flag to REGEXP_REPLACE() which allows the newline to match in the usage of the dot (match any character) symbol in regex. We switch to REGEXP_REPLACE as we'll need to return multiple capture groups. Here the WITH sets up 2 rows, one with an embedded newline in the data and one without. The capture groups are (going left to right) 1-the data after "example :" and ending in a comma, 2-the optional newline and 3-the next single character. Then replace the entire data with captured groups 1 and 3 (leaving out the newline).
NOTE this is very specific to the case of only 1 character on the following line.
WITH tbl(ID, text_field) AS (
SELECT 1, 'This is example,
and this really a example :h,j,j,j,j,
l
now this is a disease:yes' FROM dual UNION ALL
SELECT 2, 'This is example,
and this really a example :h,j,j,j,j,l
now this is a disease:yes' FROM dual
)
SELECT ID,
REGEXP_REPLACE(text_field, '.*example :(.*,)('||CHR(10)||')?(.).*', '\1\3', 1, 1, 'n') AS Result
FROM tbl;
ID RESULT
---------- ------------
1 h,j,j,j,j,l
2 h,j,j,j,j,l
2 rows selected.

PL/SQL: Creating a Function: invalid identifier

I have an oracle 19c ee database build via their docker image on Oracles github (https://github.com/oracle/docker-images/tree/master/OracleDatabase/SingleInstance). I am trying to follow their example as to how to create a function from here.
I have copied their example exactly. Setup table and data:
CREATE TABLE orders (
customer_id number(10),
order_total NUMBER(11,2)
);
INSERT INTO orders (customer_id, order_total) VALUES (1, 200.01)
The actual function:
CREATE FUNCTION get_bal(acc_no IN NUMBER)
RETURN NUMBER
IS acc_bal NUMBER(11,2);
BEGIN
SELECT order_total
INTO acc_bal
FROM orders
WHERE customer_id = acc_no;
RETURN(acc_bal);
END;
However I keep running into this error when I try and create the function
Query 2 ERROR: ORA-06550: line 5, column 27:
PL/SQL: ORA-00904: "ACC_NO": invalid identifier
ORA-06550: line 2, column 7:
PL/SQL: SQL Statement ignored
ORA-06550: line 6, column 7:
PLS-00372: In a procedure, RETURN statement cannot contain an expression
ORA-06550: line 6, column 7:
PL/SQL: Statement ignored
What am I doing wrong?
The example works for me. You must have mistyped something. Are you sure your function is exactly the same as the one in the manual?
ORA-00904: "ACC_NO": invalid identifier
suggests the declaration acc_bal NUMBER(11,2); is missing or different.
PLS-00372: In a procedure, RETURN statement cannot contain an expression
indicates that your code is a procedure, not a function.
SQL> CREATE TABLE orders (
2 customer_id number(10),
3 order_total NUMBER(11,2)
4 );
Table created
SQL> INSERT INTO orders (customer_id, order_total) VALUES (1, 200.01);
1 row inserted
SQL> CREATE FUNCTION get_bal(acc_no IN NUMBER)
2 RETURN NUMBER
3 IS acc_bal NUMBER(11,2);
4 BEGIN
5 SELECT order_total
6 INTO acc_bal
7 FROM orders
8 WHERE customer_id = acc_no;
9 RETURN(acc_bal);
10 END;
11 /
Function created
SQL> select get_bal(1) from dual;
GET_BAL(1)
----------
200.01
As an aside, while I'm a big fan of the Oracle documentation in general, and this example does neatly illustrate how to create a PL/SQL function, I think it could be improved:
For readability, it's better to give each declaration its own line, so line 3 would be better split into two with acc_bal NUMBER(11,2); on its own line.
The IS and AS keywords are interchangeable here, but surely create ... as (similar to what you might use to create a table or a view) reads better than create ... is.
Understandably, the author didn't want to complicate the example by introducing %type before it had been explained, but a more advanced version would use acc_bal orders.order_total%type; to make acc_bal inherit its datatype from the table column rather than hard-coding it. This goes for all three values used in the function.
The parameter and variable names are OK - they are at least clear - but there is a danger when using the same naming pattern for parameters and variables as for table columns. One day you will type WHERE c.customer_id = customer_id and wonder why you're getting more rows back than you expected. Again it's understandable that the author didn't want to get into that whole discussion in the first example, but it's something to think about. You might use get_bal.acc_no within the function, or use camelCase for parameters and variables, or prefix them with p_ for 'parameter' etc.
A basic rule of layout is that opening and closing keywords such as if/else and begin/end should be left-aligned. The END at line 10 is misaligned under its opening BEGIN. I suppose indenting the entire thing after the first line is a valid personal layout choice, but to me it doesn't add anything.
It's a good idea to leave blank lines around each SQL statement, to avoid a solid wall of text. Personally, I'd prefer a blank line before the RETURN at line 9.
A RETURN clause doesn't require any brackets. The compiler is ignoring the redundant brackets at line 9. I'd lose them.
It's good practice (though optional) to include the procedure/function name in the closing END, so line 10 would become END get_bal;
The COBOL-style uppercase habit is widespread in the industry, but there is no need for it. (PL/SQL's syntax is famously based on Ada, though some also point to ALGOL and PL/1 - nobody ever wrote those in uppercase.) I would improve readability by lowercasing the whole thing.
With these changes, I get this:
create or replace function get_bal
( inAccNo in orders.customer_id%type )
return orders.order_total%type
as
accBal orders.order_total%type;
begin
select order_total into accBal
from orders
where customer_id = inAccNo;
return accBal;
end;

Why is Oracle SQL function regexp_substr not returning all matching characters?

Could anyone (with extensive experience in regular-expression matching) please clarify for me why the following query returns (what I consider) unexpected results in Oracle 12?
select regexp_substr('My email: test#tes6t.test', '[^#:space:]+#[^#:space:]+')
from dual;
Expected result: test#tes6t.test
Actual result: t#t
Another example:
select regexp_substr('Beneficiary email: super+test.media.beneficiary1#gmail.com', '[^#:space:]+#[^#:space:]+')
from dual;
Expected result: super+test.media.beneficiary1#gmail.com
Actual result: ry1#gm
EDIT:
I double-checked and this is not related to Oracle SQL, but the same behaviour applies to any regex engine.
Even when simplifying the regex to [^:space:]+#[^:space:]+ the results are the same.
I am curious to know why it does not match all the non-whitespace characters before and after the # sign. And why sometimes it matches 1 character, other times 2 or 3 or more characters, but not all.
The POSIX shortcut you are trying to use is incorrect, you need square brackets around it:
SELECT REGEXP_SUBSTR('Beneficiary email: super+test.media.beneficiary1#gmail.com', '[^#[:space:]]+#[^#[:space:]]+')
FROM dual;
or even simpler, assuming you only want to validate by checking for an '#' and the email address is always at the end of the string, after the last space:
WITH tbl(str) AS (
SELECT 'My email: test#tes6t.test' FROM dual UNION ALL
SELECT 'Beneficiary email: super+test.media.beneficiary1#gmail.com' FROM dual
)
SELECT REGEXP_REPLACE(str, '.* (.*#.*)', '\1')
from tbl
;
Note: REGEXP_REPLACE() will return the original string if the match is not found, where REGEXP_SUBSTR() will return NULL. Keep that in mind and handle no match found accordingly. Always expect the unexpected!
The REGEX is not correct in your SQL code. Try
select regexp_substr('Beneficiary email: super+test.media.beneficiary1#gmail.com', '\b[A-Za-z0-9._%+-]+#[A-Za-z0-9.-]+\.[A-Za-z]{2,6}\b')
from dual;
select regexp_substr('My email: test#tes6t.test', '\b[A-Za-z0-9._%+-]+#[A-Za-z0-9.-]+\.[A-Za-z]{2,6}\b')
from dual;
It gives the result that you expected.

How to convert a number with decimal values to a float in PL/SQL?

The issue is that I need to insert this number into json, and because the number contains a comma, json becomes invalid. A float would work because it contains a period not a comma.
I have tried using replace(v_decimalNumber,',','.') and it kind of works, except that the json property is converted to a string. I need it to remain some type of a numerical value.
How can this be achieved?
I am using Oracle 11g.
You just need to_number() function.
select to_number(replace('1,2', ',', '.')) float_nr from dual;
Result:
FLOAT_NR
1.2
Note that if your number has .0 like 1.0, the function will remove it and leave it only 1
The data type of v_decimalNumber is some type of character format as it can contain commas (,). Your contention is that it contains a number once the commas are removed. However there is NO SUCH THING until that contention has been validated since being character I can put any character(s) I want into it subject to any length restriction. As an example a spreadsheet column that should contain numeric data. However, it that doesn't apply users will often put N/A into telling themselves that it doesn't apply. Oracle will happily load this into your v_decimalNumber. (And that's 1 of many many ways non-numeric data can get into your column.) So before attempting to process as a numeric value you must validate it is in fact valid numeric data. The following demonstrates one such way.
with some_numbers (n) as
( select '123,4456,789.00' from dual union all
select '987654321.00' from dual union all
select '1928374655' from dual union all
select '1.2' from dual union all
select '.1' from dual union all
select '1..1' from dual union all
select 'N/A' from dual
)
, rx as (select '^[0-9]*\.?[0-9]*$' regexp from dual)
select n
, case when regexp_like(replace(n,',',null), regexp)
then to_number(replace(n,',',null))
else null
end Num_value
, case when regexp_like(replace(n,',',null), regexp)
then null
else 'Not valid number'
end msg
from some_numbers,rx ;
Take away: Never trust a character type column to contain specific data requirements except random characters. Always validate then put the data into the appropriately defined columns.

Oracle PL/SQL - Check number formats and masks

After a long time without doing PL/SQL...
I need a suggestion from the community, for something that apparently is trivial, but I am a little bit stuck on this.
I am building a load from an CSV file, and there we have a column with the amount.
The CSVs come from different suppliers, and each one can send the amount in different formats.
So I should reject lines from the CSV with amount that are not in correct number format (999,999,999,999.00), because can be an incorrect amount reported by the supplier and should be fixed.
Coming with the formats 999.999.999,00 or 999999999,99, I can do some treatments through PL/SQL to convert.
But I am having problems with values with different formats as 999,9,9,9 or whatever...
I am trying to use common functions (TO_NUMBER, TO_CHAR). But not having much success...
SELECT TO_NUMBER('999,9,9,9') FROM DUAL; or SELECT TO_NUMBER('999,9,9,9','99G990G990G990G990G990D00') FROM DUAL;
The result is ORA-01722: invalid number, and that is brilliant! However, it would reject other formats that seem correct, like 9,999.99
SELECT TO_NUMBER('999,9,9,9','99G999G999G999G999G999D00') FROM DUAL;
Using a format mask, the value 999,9,9,9 is converted to 999999 - and it is not fine. However using the format mask works fine for 9,999.99, for example.
Do you know any other function provided by Oracle that can help solve my issue?
Or any suggestion on how can I do this?
Thank you very much.
Att.,
Guilherme
You can use a regular expression to determine a valid number or not. Then if valid replace comma with nulls and thee convert that to a number. The queries below use the following regular expression:
'^(\d{1,3})(\,\d{3})*(\.\d{2}|\.?)$'
It breaks down as follows
^(\d{1,3}) --- at beginning of string 1 to 3 digits
(\,\d{3})* --- followed by 0 or more sets of comma followed by 3 digits
(.\d{2}|.?) --- that followed by decimal point followed by 2 digits OR (|) optional decimal point
$ --- end of string
Demo:
with test (num, expected)
as (select '999,999,999,999.00', 'valid' from dual union all
select '999,99,999,999.00', 'invalid' from dual union all
select '999.00', 'valid' from dual union all
select '99', 'valid' from dual union all
select '9,999.', 'valid' from dual union all
select '9,999..0', 'invalid' from dual union all
select '999,99999,999.00', 'invalid' from dual
)
select num
, expected
, case when regexp_like(num,'^(\d{1,3})(\,\d{3})*(\.\d{2}|\.?)$')
then to_char(to_number(replace(num,',',null)))
else 'Not Valid Number'
end converted
from test;
In a live setting you would not want the "to_char(to_number ..." structure). This was used for demonstration/testing as both then and else of the case statement must result in same data type. A live version would appear something like:
with test (num)
as (select '999,999,999,999.00' from dual union all
select '999,99,999,999.00' from dual union all
select '999.00' from dual union all
select '99' from dual union all
select '9,999.' from dual union all
select '9,999..0' from dual union all
select '999,99999,999.00' from dual
)
select to_number(replace(num, ',', null))
from test
where regexp_like(num,'^(\d{1,3})(\,\d{3})*(\.\d{2}|\.?)$');

Resources