select regexp_replace('aaa_bbb', '(_.)', upper('\1')) from dual - sql

Using Oracle 11g
select regexp_replace('aaa_bbb', '(_.)', upper('\1')) from dual;
I want 'aaa_Bbb'.
But, it returns 'aaa_bbb'.
Why can't replace?

Upper execute earlier Regexp
select regexp_replace('aaa_bbb', '(_.)', upper(regexp_substr('aaa_bbb', '(_.)'))) from dual

The problem with
select regexp_replace('aaa_bbb', '(_.)', upper('\1')) from dual;
is that upper is executed before regexp_replace, effectively making it:
select regexp_replace('aaa_bbb', '(_.)', '\1') from dual;
So you need a different approach.
See the answer to oracle regexp replace uppercase replacement string

If it doesn't have to be regular expressions approach, then substr + instr + initcap might help.
SQL> with test (col) as
2 (select 'aaa_bbb' from dual)
3 select substr(col, 1, instr(col, '_')) || initcap(substr(col, instr(col, '_') + 1)) result
4 from test;
RESULT
-------
aaa_Bbb
SQL>

I don't think there's a direct method to convert by using regexp_replace but it's possible by regexp_substr in such a way :
with t(str) as
(
select 'aaa_bbb' from dual
)
select concat( regexp_substr(str, '.*[_^]'), initcap( regexp_substr(str, '[^_]+$') ) )
as "Formatted String"
from t;
Formatted String
----------------
aaa_Bbb
Demo

Related

How Update / change part of string in Oracle Database

I must Update Oracle database with remove part of string, problem is that this part can be in multiple place in this string. Example:
I must remove part and Update database where clolumn_name like ('%,aaa%') from
'bbb,aaa,ccc,ddd' or 'bbb,ccc,aaa,ddd' or from 'bbb,ccc,eee,fff,aaa,ddd'
Please help me :)
To replace complete terms using simple string functions (which is much quicker than regular expressions) is:
SELECT TRIM(
BOTH ',' FROM
REPLACE(','||value||',', ',aaa,', ',')
) AS replaced_value
FROM table_name
Which, for the sample data:
CREATE TABLE table_name ( value ) AS
SELECT 'aaa' FROM DUAL UNION ALL
SELECT 'aaa,bbb' FROM DUAL UNION ALL
SELECT 'ccc,aaa' FROM DUAL UNION ALL
SELECT 'ddd,aaa,eee' FROM DUAL UNION ALL
SELECT 'fff,aaa,ggg,aaa,hhh' FROM DUAL UNION ALL
SELECT 'aaa,aaa,aaa' FROM DUAL;
Outputs:
REPLACED_VALUE
null
bbb
ccc
ddd,eee
fff,ggg,hhh
aaa
Note: if you can have multiple sequential terms, as per the last example, then using simple string functions will not work; but in other cases when ther are non-sequential repeated terms it will work.
If you can have multiple sequential repeated terms then you can use REGEXP_REPLACE:
SELECT TRIM(
BOTH ',' FROM
REGEXP_REPLACE(','||value||',', '(,aaa)+,', ',')
) AS replaced_value
FROM table_name
Which outputs:
REPLACED_VALUE
null
bbb
ccc
ddd,eee
fff,ggg,hhh
null
fiddle
As for your comment:
I must update database where column_name like ('%,aaa%,)
If you want to remove aaa from the start of each term (and remove empty terms) then:
UPDATE table_name
SET value = TRIM(
BOTH ',' FROM
REGEXP_REPLACE(
REGEXP_REPLACE(value, '(^|,)aaa', '\1'),
',{2,}',
','
)
)
WHERE value LIKE '%,aaa%'
OR value LIKE 'aaa%'
fiddle
You may use a regex replacement approach here:
SELECT val, TRIM(BOTH ',' FROM REGEXP_REPLACE(val, 'aaa,|,aaa,|,aaa', ',')) AS val_out
FROM yourTable;
Demo
If you require an update, then use:
UPDATE yourTable
SET val = TRIM(BOTH ',' FROM REGEXP_REPLACE(val, 'aaa,|,aaa,|,aaa', ','))
WHERE ',' || val || ',' LIKE '%,aaa,%';

how to split a string which is having comma and colon

I have a following query like this
SELECT REGEXP_SUBSTR('SARAH;10,JOE;1D,KANE;1A,SDF:1a', '[^,;]+', 1, level)
FROM dual
CONNECT BY REGEXP_SUBSTR('SARAH;10,JOE;1D,KANE;1A,SDF:1a',
'[^,;]+',
1,
level) IS NOT NULL;
I am trying to get o/p as SARAH,JOE,KANE,SDF
If there's only one row of data, then you can use
WITH t(str) AS
(
SELECT 'SARAH;10,JOE;1D,KANE;1A,SDF:1a' FROM dual
), t2 AS
(
SELECT level AS lvl, REGEXP_SUBSTR(str, '[^,;:]+', 1, level) AS str
FROM t
CONNECT BY REGEXP_SUBSTR(str,
'[^,;]+',
1,
level) IS NOT NULL
)
SELECT LISTAGG(str,',') WITHIN GROUP (ORDER BY lvl) AS result
FROM t2
WHERE NOT REGEXP_LIKE(str,'^(\d)')
in order to filter the extracted substrings which don't start with an integer through use of REGEXP_LIKE() like above
Don't split the string and re-aggregate. Just replace the string from each ; or : until the next , or then end-of-the-string:
SELECT REGEXP_REPLACE(
'SARAH;10,JOE;1D,KANE;1A,SDF:1a',
'[;:].*?(,|$)',
'\1'
) AS replaced_value
FROM DUAL;
Which outputs:
REPLACED_VALUE
SARAH,JOE,KANE,SDF
db<>fiddle here
Update
If your delimiter can be any one of the ;:, characters until the next ;:, character or the end-of-the-string then:
SELECT value,
RTRIM(REGEXP_REPLACE(value, '[;:,].*?([;:,]|$)', ','), ',')
AS replaced_value
FROM table_name;
Which, for the sample data:
CREATE TABLE table_name (value) AS
SELECT 'SARAH;10,JOE;1D,KANE;1A,SDF:1a' FROM DUAL UNION ALL
SELECT 'SARAH,10;JOE,1D;KANE,1A;SDF:1a' FROM DUAL;
Outputs:
VALUE
REPLACED_VALUE
SARAH;10,JOE;1D,KANE;1A,SDF:1a
SARAH,JOE,KANE,SDF
SARAH,10;JOE,1D;KANE,1A;SDF:1a
SARAH,JOE,KANE,SDF
db<>fiddle here

Escaping special characters for JSON output

I have a column that contains data that I want to escape in order to use it as JSON output, to be more precise am trying to escape the same characters listed here but using Oracle 11g: Special Characters and JSON Escaping Rules
I think it can be solved using REGEXP_REPLACE:
SELECT REGEXP_REPLACE(my_column, '("|\\|/)|(' || CHR(9) || ')', '\\\1') FROM my_table;
But I am lost about replacing the other characters (tab, new line, backspace, etc), in the previous example I know that \1 will match and replace the first group but I am not sure how to capture the tab in the second group and then replace it with \t. Somebody could give me a hint about how to do the replacement?
I know I can do this:
SELECT REGEXP_REPLACE( REGEXP_REPLACE(my_column, '("|\\|/)', '\\\1'), '(' || CHR(9) || ')', '\t')
FROM my_table;
But I would have to nest like 5 calls to REGEXP_REPLACE, and I suspect I should be able to do it in just one or two calls.
I am aware about other packages or libraries for JSON but I think this case is simple enough that it can be solved with the functions that Oracle offers out-of-the-box.
Thank you.
Here's a start. Replacing all the regular characters is easy enough, it's the control characters that will be tricky. This method uses a group consisting of a character class that contains the characters you want to add the backslash in front of. Note that characters inside of the class do not need to be escaped. The argument to REGEXP_REPLACE of 1 means start at the first position and the 0 means to replace all occurrences found in the source string.
SELECT REGEXP_REPLACE('t/h"is"'||chr(9)||'is a|te\st', '([/\|"])', '\\\1', 1, 0) FROM dual;
Replacing the TAB and a carriage return is easy enough by wrapping the above in REPLACE calls, but it stinks to have to do this for each control character. Thus, I'm afraid my answer isn't really a full answer for you, it only helps you with the regular characters a bit:
SQL> SELECT REPLACE(REPLACE(REGEXP_REPLACE('t/h"is"'||chr(9)||'is
2 a|te\st', '([/\|"])', '\\\1', 1, 0), chr(9), '\t'), chr(10), '\n') fixe
3 FROM dual;
FIXED
-------------------------
t\/h\"is\"\tis\na\|te\\st
SQL>
EDIT: Here's a solution! I don't claim to understand it fully, but basically it creates a translation table that joins to your string (in the inp_str table). The connect by, level traverses the length of the string and replaces characters where there is a match in the translation table. I modified a solution found here: http://database.developer-works.com/article/14901746/Replace+%28translate%29+one+char+to+many that really doesn't have a great explanation. Hopefully someone here will chime in and explain this fully.
SQL> with trans_tbl(ch_frm, str_to) as (
select '"', '\"' from dual union
select '/', '\/' from dual union
select '\', '\\' from dual union
select chr(8), '\b' from dual union -- BS
select chr(12), '\f' from dual union -- FF
select chr(10), '\n' from dual union -- NL
select chr(13), '\r' from dual union -- CR
select chr(9), '\t' from dual -- HT
),
inp_str as (
select 'No' || chr(12) || 'w is ' || chr(9) || 'the "time" for /all go\od men to '||
chr(8)||'com' || chr(10) || 'e to the aid of their ' || chr(13) || 'country' txt from dual
)
select max(replace(sys_connect_by_path(ch,'`'),'`')) as txt
from (
select lvl
,decode(str_to,null,substr(txt, lvl, 1),str_to) as ch
from inp_str cross join (select level lvl from inp_str connect by level <= length(txt))
left outer join trans_tbl on (ch_frm = substr(txt, lvl, 1))
)
connect by lvl = prior lvl+1
start with lvl = 1;
TXT
------------------------------------------------------------------------------------------
No\fw is \tthe \"time\" for \/all go\\od men to \bcom\ne to the aid of their \rcountry
SQL>
EDIT 8/10/2016 - Make it a function for encapsulation and reusability so you could use it for multiple columns at once:
create or replace function esc_json(string_in varchar2)
return varchar2
is
s_converted varchar2(4000);
BEGIN
with trans_tbl(ch_frm, str_to) as (
select '"', '\"' from dual union
select '/', '\/' from dual union
select '\', '\\' from dual union
select chr(8), '\b' from dual union -- BS
select chr(12), '\f' from dual union -- FF
select chr(10), '\n' from dual union -- NL
select chr(13), '\r' from dual union -- CR
select chr(9), '\t' from dual -- HT
),
inp_str(txt) as (
select string_in from dual
)
select max(replace(sys_connect_by_path(ch,'`'),'`')) as c_text
into s_converted
from (
select lvl
,decode(str_to,null,substr(txt, lvl, 1),str_to) as ch
from inp_str cross join (select level lvl from inp_str connect by level <= length(txt))
left outer join trans_tbl on (ch_frm = substr(txt, lvl, 1))
)
connect by lvl = prior lvl+1
start with lvl = 1;
return s_converted;
end esc_json;
Example to call for multiple columns at once:
select esc_json(column_1), esc_json(column_2)
from your_table;
Inspired by the answer above, I created this simpler "one-liner" function:
create or replace function json_esc (
str IN varchar2
) return varchar2
begin
return REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(str, chr(8), '\b'), chr(9), '\t'), chr(10), '\n'), chr(12), '\f'), chr(13), '\r');
end;
Please note, both this and #Gary_W's answer above are not escaping all control characters as the json.org seems to indicate.
in sql server you can use STRING_ESCAPE() function like below:
SELECT
STRING_ESCAPE('['' This is a special / "message" /'']', 'json') AS
escapedJson;

How to trim/substr from varchar when decimal place found in oracle?

How to trim after decimal(.) from varchar2 in oracle?
Example 1: '2999.89' should be return '2999'
Example 1: 'V59.00' should be return 'V59'
You could use SUBSTR and INSTR.
For example,
SQL> WITH DATA(str) AS(
2 SELECT '2999.89' FROM dual UNION ALL
3 SELECT 'V59.00' FROM dual
4 )
5 SELECT str, SUBSTR(str, 1, instr(str, '.', 1, 1)-1) new_str FROM DATA;
STR NEW_STR
------- -------
2999.89 2999
V59.00 V59
SQL>
You can use instr to find the index of . and then substr to return the string up to that position:
SELECT SUBSTR (col, 1, INSTR(col, '.') - 1)
FROM mytable
WITH DATA(str) AS(
SELECT '2999.89' FROM dual UNION ALL
SELECT 'V59.00' FROM dual
)
SELECT str, REGEXP_SUBSTR(str,'^([^.]*)',1) new_str FROM DATA;

How to tokenize a string in Oracle and convert each token to NUMBER to use them in a query as part of IN clause?

Suppose I have a string '1,2,3'
I want to tokenize the string and convert each of the tokens into NUMBER. So the above string will be tokenized into :
1 NUMBER
2 NUMBER
3 NUMBER
The final intention is to use them in a query as part of IN clause as below :
select * from sample where type in (1,2,3) ;
How can I achieve this ? One important point here is the string can have different number of tokens in different situations. So it can be either '1,2,3' or '1,2' or '1,2,3,4' or even '1'.
Please help me out guys.
Thanks in advance.
Please try:
with test as
(
select '1,2,3' str from dual
)
select * from sample
where type in(
select regexp_substr (str, '[^,]+', 1, rownum) split
from test
connect by level <= length (regexp_replace (str, '[^,]+')) + 1);
Depending on what you are doing, it might be faster to convert the id to a String and look for it in your String. Just add a comma to the beginning and the end of your list.
SELECT id
FROM (SELECT 1 AS id FROM DUAL
UNION
SELECT 2 FROM DUAL
UNION
SELECT 3 FROM DUAL) idtable
WHERE ',' || '1,3,4,5' || ',' LIKE '%,' || idtable.id || ',%'