Oracle regular expression using hyphen in pattern - oracle11g

Why is this query returning the value 4 (I expected 0)?
select regexp_instr ('123abc','[A-Z]')
from dual;
I think [] should indicate a character list, and A-Z includes all upper letters?

This is affected by your session's NLS_SORT setting, and you will get a result of 4 if you have case-insensitive sorting enabled:
alter session set nls_sort=binary;
select regexp_instr ('123abc','[A-Z]')
from dual;
REGEXP_INSTR('123ABC','[A-Z]')
------------------------------
0
alter session set nls_sort=binary_ci;
select regexp_instr ('123abc','[A-Z]')
from dual;
REGEXP_INSTR('123ABC','[A-Z]')
------------------------------
4
You can read more in the documentation; and you may find this answer useful too.

Related

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.

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

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.

Is this the correct syntax for SQLite for making nulls appear last?

select * from table1
order by case Language when null then 1 else 0 end, Language
No matter which way I play around with it, it always displays null values first. Is there a standard way to allow non null values to take ordering precedence?
Thanks guys
You don't need WHEN:
SELECT * FROM table1
ORDER BY Language IS NULL, Language
Operator IS will return 1 on true, 0 otherwise (Operators).
EDIT: Dealing with empty TEXT, also:
SELECT * FROM table1
ORDER BY Language IS NULL OR Language='', Language
ORDER BY clause uses two fields:
Language IS NULL OR Language=''. That's a boolean expression (resulting in 0 (false) or 1 (true) in SQLite), same as (Language IS NULL) OR (Language='')
When first field has same results, second fiels is used: Language.
This way, whenever Language is NULL or empty TEXT, first field will be 1, relying those results after other results, evaluated to 0. Then, second field is used to sort all results which Language has content.
You have to use the is operator when checking for null
select * from table1
order by case when Language is null then 1 else 0 end,
Language
This is an extension of LS_dev's answer to UNIONs. Not a nice way, but I can't figure out any other way to make it work, since the documentation says:
if the SELECT is a compound SELECT, then ORDER BY expressions that are not aliases to output columns must be exactly the same as an expression used as an output column.
select * from (
SELECT * FROM table1 -- but this select can have union in it
) ORDER BY Language IS NULL or Language = '', Language

Resources