SQL regexp - replace dots between two numbers - oracle

I need to change this;
J S49-1:7.5-190 d.
to this;
J S49-1:7,5-190 d.
by replacing all dots with commas, but only between numbers.

With capture groups within REGEXP_REPLACE;
select REGEXP_REPLACE('J S49-1:7.5-190 d.', '(.*\d)\.(\d.*)', '\1,\2') from dual;
will return;
J S49-1:7,5-190 d.
But this will not work if there are multiple occurrences of periods between digits.
In case there are more than one occurrence of period between digits
I was able to trigger multi replace with capture groups;
select REGEXP_REPLACE('J S49-1:7.557.8-190 d.', '(\d)\.(\d)', '\1,\2', 1, 0) from dual;
will return;
J S49-1:7,557,8-190 d.
The last two parameters are; start_position and nth_appearance, to replace all occurrences, nth_appearance value must be 0
But still there is a problem when two capture groups share a digit, this is due to overlapping (same issue as this), and needs lookahead to resolve, but can't use it in oracle, hmmm.
In case there are more than one occurrence of period between digits, and same digit is shared between two occurrence
One solution would be to apply the secondary REGEXP_REPLACE I've shared twice, so any overlapping cases would be covered in the second run;
select REGEXP_REPLACE(
REGEXP_REPLACE('J S49-1:7.5.8.7-190 d.', '(\d)\.(\d)', '\1,\2', 1, 0),
'(\d)\.(\d)', '\1,\2', 1, 0) from dual;
will be the correct result;
J S49-1:7,5,8,7-190 d.
For more detail on REGEXP_REPLACE, check here

One way using REGEXP_SUBSTR:
select REGEXP_SUBSTR('J S49-1:7.5-190 d','[^.]+',1,1)||','||REGEXP_SUBSTR('J S49-1:7.5-190 d','[^.]+',1,2) from dual;
Here is with regexp_replce
select REGEXP_REPLACE('J S49-1:7.5-190 d','[.*]',',') from dual

Related

how to trim leading zero in oracle sql from concatenation text (text:number-number-number

how to trim leading zero in oracle sql from concatenation text
(text:number-number-number)
example(word:number-number-number) word can have text or double zero
but always has char before it after word, max digits separated by '-'
all time max 3 digits i want to keep zeros in first part. and after
that if remove leading 0 in sequence but keep it if it's only one 0
MachineAbc00:1-0-03 = MachineAbc00:1-0-3
MachineAbc00:1-001-02 = MachineAbc00:1-1-2
tried many combination, not successful , like
REGEXP_REPLACE ('MachineO00:1-0-03*', '0+(?!$)', '-')
REGEXP_REPLACE ('MTROPQFMO00:1-0-03*', '(-0){1,}', '-')
If all the input strings are in the exact format you said they are, then something like this should work:
with
sample_strings (str) as (
select 'MachineAbc00:1-0-03' from dual union all
select 'MachineAbc00:1-001-02' from dual union all
select 'MachineZzzyx:200-020-002' from dual union all
select 'machineCX032:0-000-0' from dual
)
select str as old_str,
regexp_replace(str, '([:-])0*(\d+)', '\1\2') as new_str
from sample_strings
;
OLD_STR NEW_STR
------------------------ ------------------------
MachineAbc00:1-0-03 MachineAbc00:1-0-3
MachineAbc00:1-001-02 MachineAbc00:1-1-2
MachineZzzyx:200-020-002 MachineZzzyx:200-20-2
machineCX032:0-000-0 machineCX032:0-0-0
The regular expression function finds any occurrence of (colon or dash) followed by (zero or more 0 characters/digits) followed by at least one more digit. The "zero or more 0 digits" is maximal with the property that there must be at least one more digit AFTER that match (even if that extra digit hapens to be a zero - see my last test string, which I added precisely in order to test that this works correctly). The function replaces each such occurrence with the first and third fragments, removing the middle one (the zeros you must remove from your string). The references \1 and \2 refer to the first and the second parenthesized sub-expressions - the punctuation mark (colon or dash) and, respectively, the final digits (excluding the leading zeros that must be removed).

FInd if the fifth position is a letter and not a number using ORACLE

How can I find if the fifth position is a letter and thus not a number using Oracle ?
My last try was using the following statement:
REGEXP_LIKE (table_column, '([abcdefghijklmnopqrstuvxyz])');
Perhaps you'd rather check whether 5th position contains a number (which means that it is not something else), i.e. do the opposite of what you're doing now.
Why? Because a "letter" isn't only ASCII; have a look at the 4th row in my example - it contains Croatian characters and these aren't between [a-z] (nor [A-Z]).
SQL> with test (col) as
2 (select 'abc_3def' from dual union all
3 select 'A435D887' from dual union all
4 select '!#$%&/()' from dual union all
5 select 'ASDĐŠŽĆČ' from dual
6 )
7 select col,
8 case when regexp_like(substr(col, 5, 1), '\d+') then 'number'
9 else 'not a number'
10 end result
11 from test;
COL RESULT
------------- ------------
abc_3def number
A435D887 not a number
!#$%&/() not a number
ASDĐŠŽĆČ not a number
SQL>
Anchor to the start of the string else you may get unexpected results. This works, but remove the caret (start of string anchor) and it returns 'TRUE'! Note it uses the case-insensitive flag of 'i'.
select 'TRUE'
from dual
where regexp_like('abcd4fg', '^.{4}[A-Z]', 'i');
Yet another way to do it:
regexp_like(table_column, '^....[[:alpha:]]')
Using the character class [[:alpha:]] will pick up all letters upper case, lower case, accented and etc. but will ignore numbers, punctuation and white space characters.
If what you care about is that the character is not a number, then use
not regexp_like(table_column, '^....[[:digit:]]')
or
not regexp_like(table_column, '^....\d')
Try:
REGEXP_LIKE (table_column, '^....[a-z]')
Or:
SUBSTR (table_column, 5, 1 ) BETWEEN 'a' AND 'z'

Oracle joining tables where the matching process starts after certain digits

I have joined two tables which contain words as well as values. The matching of the words works. The matching of the values do not. The reason is that the values in the table "GATTUNGSDATEN" always start with 4 zeros whereas in the table BONDMAPPING not. E.g. 00001234 = 1234. What query could I use in order to have the matching process started after 4 digits?
select MET_SEC_INDEX_GATTUNGSDATEN.ranl, MET_SEC_INDEX_GATTUNGSDATEN.vvranlwx,
V_AR_BONDMAPPING.MUREXEXTERNALID
from V_AR_BONDMAPPING
RIGHT JOIN MET_SEC_INDEX_GATTUNGSDATEN
ON V_AR_BONDMAPPING.MUREXEXTERNALID = MET_SEC_INDEX_GATTUNGSDATEN.ranl IN
('%%%%')
where vvranlwi=34
If it's always four leading zeroes you can use SUBSTR():
from V_AR_BONDMAPPING
RIGHT JOIN MET_SEC_INDEX_GATTUNGSDATEN
ON V_AR_BONDMAPPING.MUREXEXTERNALID = substr(MET_SEC_INDEX_GATTUNGSDATEN.ranl, 5)
This trims the leading four characters from MET_SEC_INDEX_GATTUNGSDATEN.ranl
If you know the length of the identifiers you could pad the shorter identifier with leading zeroes
select lpad(1234, 8, '0') from dual
=>
00001234
select lpad('00001234', 8, '0') from dual
=>
00001234
This will implicitly convert the numbers into VARCHAR2s however, which on the other hand is what would happen anyways during the comparison

Oracle cursor removes leading zero

I have a cursor which selects date from column with NUMBER type containg floating point numbers. Numbers like 4,3433 are returned properly while numbers smaller then 1 have removed leading zero.
For example number 0,4513 is returned as ,4513.
When I execute select used in the cursor on the database, numbers are formatted properly, with leading zeros.
This is how I loop over the recors returned by the cursor:
FOR c_data IN cursor_name(p_date) LOOP
...
END LOOP;
Any ideas why it works that way?
Thank you in advance.
You're confusing number format and number value.
The two strings 0.123 and .123, when read as a number, are mathematically equals. They represent the same number. In Oracle the true number representation is never displayed directly, we always convert a number to a character to display it, either implicitly or explicitly with a function.
You assume that a number between 0 and 1 should be represented with a leading 0, but this is not true by default, it depends on how you ask this number to be displayed. If you don't want unexpected outcome, you have to be explicit when displaying numbers/dates, for example:
to_char(your_number, '9990.99');
It's the default number formatting that Oracle provides.
If you want to specify something custom, you shall use TO_CHAR function (either in SQL query or PL/SQL code inside the loop).
Here is how it works:
SQL>
SQL> WITH aa AS (
2 select 1.3232 NUM from dual UNION ALL
3 select 1.3232 NUM from dual UNION ALL
4 select 332.323 NUM from dual UNION ALL
5 select 0.3232 NUM from dual
6 )
7 select NUM, to_char(NUM, 'FM999990D9999999') FORMATTED from aa
8 /
NUM FORMATTED
---------- ---------------
1.3232 1.3232
1.3232 1.3232
332.323 332.323
.3232 0.3232
SQL>
In this example, 'FM' - suppresses extra blanks, '0' indicates number digit including leading/trailing zeros, and '9' indicates digit suppressing leading/trailing zeros.
You can find many examples here:
http://docs.oracle.com/cd/B19306_01/server.102/b14200/sql_elements004.htm#i34570

How to write the following pl/sql block without using Cursor?

I had written a cursor in a pl/sql block. This block taking lot of time if it has more records.
How to write this without a cursor or Is there any other alternative way that will reduce the time?
Is there any alternative query to perform insert into one table and delete from another table using a single query?
DECLARE
MDLCursor SYS_REFCURSOR;
BEGIN
open MDLCursor for
select dc.dest_id, dc.digits, dc.Effectivedate, dc.expirydate
from DialCodes dc
INNER JOIN MDL d
ON dc.Dest_ID = d.Dest_ID
AND d.PriceEntity = 1
join sysmdl_calltypes s
on s.call_type_id = v_CallType_ID
and s.dest_id = dc.Dest_ID
and s.call_type_id not in
(select calltype_id from ignore_calltype_for_routing)
order by length(dc.digits) desc, dc.digits desc;
loop
fetch MDLCursor
into v_mdldest_id, v_mdldigits, v_mdlEffectiveDate, v_mdlExpDate;
insert into tt_pendingcost_temp
(Dest_ID,
Digits,
CCASDigits,
Destination,
tariff_id,
NewCost,
Effectivedate,
ExpiryDate,
previous,
Currency)
select v_mdldest_id,
Digits,
v_mdldigits,
Destination,
tariff_id,
NewCost,
Effectivedate,
ExpiryDate,
previous,
Currency
FROM tt_PendingCost
where substr(Digits, 1, 2) = substr(v_MDLDigits, 1, 2)
and instr(Digits, v_MDLDigits) = 1
and v_mdlEffectiveDate <= effectivedate
and (v_mdlExpDate > effectivedate or v_mdlExpDate is null);
if SQL%ROWCOUNT > 0 then
delete FROM tt_PendingCost
where substr(Digits, 1, 2) = substr(v_MDLDigits, 1, 2)
and instr(Digits, v_MDLDigits) = 1
and v_mdlEffectiveDate <= effectivedate
and (v_mdlExpDate > effectivedate or v_mdlExpDate is null);
end if;
exit when MDLCursor%NOTFOUND;
end loop;
close MDLCursor;
END;
I don't have your tables and your data so I can only guess at a couple of things that would be slowing you down.
Firstly, the query used in your cursor has an ORDER BY clause in it. If this query returns a lot of rows, Oracle has to fetch them all and sort them all before it can return the first row. If this query typically returns a lot of results, and you don't particularly need it to return sorted results, you may find your PL/SQL block speeds up a bit if you drop the ORDER BY. That way, you can start getting results out of the cursor without needing to fetch all the results, store them somewhere and sort them first.
Secondly, the following is the WHERE clause used in your INSERT INTO ... SELECT ... and DELETE FROM ... statements:
where substr(Digits, 1, 2) = substr(v_MDLDigits, 1, 2)
and instr(Digits, v_MDLDigits) = 1
and v_mdlEffectiveDate <= effectivedate
and (v_mdlExpDate > effectivedate or v_mdlExpDate is null);
I don't see how Oracle can make effective use of indexes with any of these conditions. It would therefore have to do a full table scan each time.
The last two conditions seem reasonable and there doesn't seem a lot that can be done with them. I'd like to focus on the first two conditions as I think there's more scope for improvement with them.
The second of the four conditions is
instr(Digits, v_MDLDigits) = 1
This condition holds if and only if Digits starts with the contents of v_MDLDigits. A better way of writing this would be
Digits LIKE v_MDLDigits || '%'
The advantage of using LIKE in this situation instead of INSTR is that Oracle can make use of indexes when using LIKE. If you have an index on the Digits column, Oracle will be able to use it with this query. Oracle would then be able to focus in on those rows that start with the digits in v_MDLDigits instead of doing a full table scan.
The first of the four conditions is:
substr(Digits, 1, 2) = substr(v_MDLDigits, 1, 2)
If v_MDLDigits has length at least 2, and all entries in the Digits columns also have length at least 2, then this condition is redundant since it is implied by the previous one we looked at.
I'm not sure why you would have a condition like this. The only reason I can think why you might have this condition is if you have a functional index on substr(Digits, 1, 2). If not, I would be tempted to remove this substr condition altogether.
I don't think the cursor is what is making this procedure run slowly, and there's no single statement I know of that can insert into one table and delete from another. To make this procedure speed up I think you just need to tune the queries a bit.

Resources