How to replace numbers in an alphanumeric string with words - sql

I have a table named department_details with a column dept_id which contains values like
10_prod
20_r&d
80_sales
etc. I want a query which will give me output like
ten_prod
twenty_r&d
eighty_sales
etc.

Here's one option:
SQL> with test (col) as
2 (select '10_prod' from dual union all
3 select '20_r&d' from dual union all
4 select '80_sales' from dual
5 )
6 select col,
7 regexp_substr(col, '^\d+') num,
8 to_char(to_date(substr(col, 1, instr(col, '_') - 1), 'j'), 'jsp') wrd,
9 --
10 to_char(to_date(substr(col, 1, instr(col, '_') - 1), 'j'), 'jsp') ||
11 substr(col, instr(col, '_')) result
12 from test;
COL NUM WRD RESULT
-------- -------------------------------- ---------- --------------------
10_prod 10 ten ten_prod
20_r&d 20 twenty twenty_r&d
80_sales 80 eighty eighty_sales
SQL>
What does it do (step-by-step, so that you could follow it):
lines #1 - 5: sample data
line #7: one way to extract the number from the beginning of the string (using regular expressions)
line #8: another way (using substr + instr; probably better). It - additionally - converts it to date using the 'J' format and to character using the JSP format. This is the usual way of spelling numbers
lines #10 - 11: combine spelled number (line #10) with the rest of the string (line #11)

You could do something like this:
with list1 as
(select '10_prod' as val from dual union
select '20_randd' as val from dual union
select '80_sales' as val from dual)
select a.*,
to_char(to_date(substr(val,1,2),'j'), 'jsp')||substr(val,3,20) as text_val
from list1 a

Related

Oracle query to replace same character with different characters based on position

I have a string in Oracle DB in the format 'a|b|c' , '|' being the separator between characters. Want to write an SQL query to transform it into a string in the format 'a,b&c'. First occurrence of '|' to ',' 2nd occurrence to '&'.
If suppose the string comes in the format 'a|b' then output should be 'a&b'.
I'm using multiple regex_replace queries to achieve this right now.
select REGEXP_REPLACE ('a|b|c', '[|]', ',', 1, 1)
from dual
Is there any other solution using one single query?
Nested replaces (see line #6):
SQL> with test (col) as
2 (select 'a|b|c' from dual union all
3 select 'a|b' from dual
4 )
5 select col,
6 regexp_replace(regexp_replace(col, '\|', ',', 1, 1), '\|', '&', 2, 1) result
7 from test;
COL RESULT
----- --------------------
a|b|c a,b&c
a|b a,b
SQL>
If you want to replace the last | in the list with & and all the preceding |s with , then you can use:
SELECT value,
REPLACE(
REGEXP_REPLACE( value, '\|([^|]*)$', '&\1' ),
'|',
','
) AS replaced
FROM table_name;
Which, for the sample data:
CREATE TABLE table_name (value) AS
SELECT 'A|B|C|D' FROM DUAL UNION ALL
SELECT 'A|B|C' FROM DUAL UNION ALL
SELECT 'A|B' FROM DUAL UNION ALL
SELECT 'A' FROM DUAL;
Outputs:
VALUE
REPLACED
A|B|C|D
A,B,C&D
A|B|C
A,B&C
A|B
A&B
A
A
fiddle

Display all the names of the employees whose names having letter A as 2nd occurrence without using like operator

For the above query, I am using regexp_count but in SQL command line I am getting regexp_count :invaild identifier;
select * from table_name WHERE (REGEXP_COUNT(column_name, 'A')) >2;
Is this query works?
I'm not sure what you really want; is it to return names whose 2nd letter is "a", or names that have two or more letters "a" within.
Anyway, pick the one you find appropriate.
SQL> create table test as
2 (select 'saritha' col from dual union all
3 select 'mamatha' from dual union all
4 select 'vaisnavi' from dual union all
5 select 'sai' from dual union all
6 select 'vijaya' from dual union all
7 select 'kumar' from dual
8 );
Table created.
2 or more letters "a":
SQL> select col
2 from test
3 where regexp_count(col, 'a') >= 2;
COL
--------
saritha
mamatha
vaisnavi
vijaya
2nd letter is "a":
SQL> select col
2 from test
3 where substr(col, 2, 1) = 'a';
COL
--------
saritha
mamatha
vaisnavi
sai
SQL>
On Oracle 10g, which doesn't support REGEXP_COUNT function, one option is to replace all letters a with an empty string (basically, you'd remove all letters a) and fetch rows whose difference of full column length and "replaced" column length is >= 2. Something like this:
SQL> select col
2 from test
3 where length(col) - length(replace(col, 'a', '')) >= 2;
COL
--------
saritha
mamatha
vaisnavi
vijaya
SQL>

How can I get a natural numeric sort order in Oracle?

I have a column with a letter followed by either numbers or letters:
ID_Col
------
S001
S1001
S090
SV911
SV800
Sfoofo
Szap
Sbart
How can I order it naturally with the numbers first (ASC) then the letters alphabetically? If it starts with S and the remaining characters are numbers, sort by the numbers. Else, sort by the letter. So SV911should be sorted at the end with the letters since it also contains a V. E.g.
ID_Col
------
S001
S090
S1001
Sbart
Sfoofo
SV800
SV911
Szap
I see this solution uses regex combined with the TO_NUMBER function, but since I also have entries with no numbers this doesn't seem to work for me. I tried the expression:
ORDER BY
TO_NUMBER(REGEXP_SUBSTR(ID_Col, '^S\d+$')),
ID_Col
/* gives ORA-01722: invalid number */
Would this help?
SQL> with test (col) as
2 (select 'S001' from dual union all
3 select 'S1001' from dual union all
4 select 'S090' from dual union all
5 select 'SV911' from dual union all
6 select 'SV800' from dual union all
7 select 'Sfoofo' from dual union all
8 select 'Szap' from dual union all
9 select 'Sbart' from dual
10 )
11 select col
12 from test
13 order by substr(col, 1, 1),
14 case when regexp_like(col, '^[[:alpha:]]\d') then to_number(regexp_substr(col, '\d+$')) end,
15 substr(col, 2);
COL
------
S001
S090
S1001
Sbart
Sfoofo
SV800
SV911
Szap
8 rows selected.
SQL>

pls help me with this oracle sql query

I have to write a oracle SQL query where I have to check that length of string is 64 or not if it's 64 like in this string
'Lst021_23-FehlerDatenprotokoll.AenDienst2019.06.11_08.48.42.tx' then extension of the string should be txt and we have to remove the part of string upto - also
So for the string
'Lst021_23-FehlerDatenprotokoll.AenDienst2019.06.11_08.48.42.tx' output will be
FehlerDatenprotokoll.AenDienst2019.06.11_08.48.42.txt
i am trying to write this query
SELECT 'Lst021_23-Fehler-Datenprotokoll.AenDienst.2019.06.11_08.48.42.tx'FROM Dual;
CASE
WHEN LENGTH('Lst021_23-Fehler-Datenprotokoll.AenDienst.2019.06.11_08.48.42.tx')=64 THEN CONCAT('SUBSTR('Lst021_23-Fehler-Datenprotokoll.AenDienst.2019.06.11_08.48.42.tx',11,53)','t')
END
CASE;
You have syntax issue in the script. Try this way
SELECT
CASE
WHEN LENGTH('Lst021_23-Fehler-Datenprotokoll.AenDienst.2019.06.11_08.48.42.tx') = 64
THEN CONCAT(SUBSTR('Lst021_23-Fehler-Datenprotokoll.AenDienst.2019.06.11_08.48.42.tx',11,53),'t')
ELSE 'Lst021_23-Fehler-Datenprotokoll.AenDienst.2019.06.11_08.48.42.tx'
END
FROM Dual;
Prefer writing your string only once :
with t( str ) as
(
select 'Lst021_23-Fehler-Datenprotokoll.AenDienst.2019.06.11_08.48.42.tx' from dual
)
select decode(length(str), 64, concat(str,'t')) as "File Name"
from t;
File Name
-----------------------------------------------------------------
Lst021_23-Fehler-Datenprotokoll.AenDienst.2019.06.11_08.48.42.txt
Demo
How about this? If length is 64, then:
take a substring from the first hyphen (-) to the end of the string
apply regexp_replace and substitute the last "word" (\w+$) with txt (as you said that extension has to be txt)
Here we go:
SQL> with test (id, col) as
2 (select 1, 'Lst021_23-Fehler-Datenprotokoll.AenDienst.2019.06.11_08.48.42.tx' from dual
3 union all
4 select 2, 'blabla-Something-else.Which_is_64_characters_long.2019.06.12.txt' from dual
5 union all
6 select 3, 'This will not be "fixed" as it is shorter than 64 chars.exe' from dual
7 union all
8 select 4, 'Yet another nice string-Kein_Fehler.Mitwoch.2019.06.13_22.22.com' from dual
9 )
10 select
11 id,
12 case when length(col) = 64 then
13 regexp_replace(substr(col, instr(col, '-') + 1), '\w+$', 'txt')
14 else col
15 end result
16 from test;
ID RESULT
--- -----------------------------------------------------------------
1 Fehler-Datenprotokoll.AenDienst.2019.06.11_08.48.42.txt
2 Something-else.Which_is_64_characters_long.2019.06.12.txt
3 This will not be "fixed" as it is shorter than 64 chars.exe
4 Kein_Fehler.Mitwoch.2019.06.13_22.22.txt
SQL>

REGEXP_REPLACE back-reference with function call

Can I use some function call on REGEXP_REPLACE back-reference value?
For example I want to call chr() or any other function on back-reference value, but this
SELECT REGEXP_REPLACE('a 98 c 100', '(\d+)', ASCII('\1')) FROM dual;
just returns ASCII value of '\':
'a 92 c 92'
I want that the last parameter (replacement string) to be evaluated first and then to replace string. So result would be:
'a b c d'
Just for fun really, you could do the tokenization, conversion of numbers to characters, and aggregation using XPath:
select *
from xmltable(
'string-join(
for $t in tokenize($s, " ")
return if ($t castable as xs:integer) then codepoints-to-string(xs:integer($t)) else $t,
" ")'
passing 'a 98 c 100' as "s"
);
Result Sequence
--------------------------------------------------------------------------------
a b c d
The initial string value is passed in as $s; tokenize() splits that up using a space as the delimiter; each $t that generates is evaluated to see if it's an integer, and if it is then it's converted to the equivalent character via codepoints-to-string, otherwise it's left alone; then all the tokens are recombined with string-join().
If the original has runs of multiple spaces those will collapse to a single space (as they will with Littlefoot's regex).
I'm not that smart to do it using one regular expression, but - step-by-step, something like this might help. It splits the source string into rows, checks whether part of it is a number and - if so - selects CHR of it. Finally, everything is aggregated back to a single string.
SQL> with test (col) as
2 (select 'a 98 c 100' from dual),
3 inter as
4 (select level lvl,
5 regexp_substr(col, '[^ ]+', 1, level) c_val
6 from test
7 connect by level <= regexp_count(col, ' ') + 1
8 ),
9 inter_2 as
10 (select lvl,
11 case when regexp_like(c_val, '^\d+$') then chr(c_val)
12 else c_val
13 end c_val_2
14 from inter
15 )
16 select listagg(c_val_2, ' ') within group (order by lvl) result
17 from inter_2;
RESULT
--------------------
a b c d
SQL>
It can be shortened for one step (I intentionally left it as is so that you could execute one query at a time and check the result, to make things clearer):
SQL> with test (col) as
2 (select 'a 98 c 100' from dual),
3 inter as
4 (select level lvl,
5 case when regexp_like(regexp_substr(col, '[^ ]+', 1, level), '^\d+$')
6 then chr(regexp_substr(col, '[^ ]+', 1, level))
7 else regexp_substr(col, '[^ ]+', 1, level)
8 end c_val
9 from test
10 connect by level <= regexp_count(col, ' ') + 1
11 )
12 select listagg(c_val, ' ') within group (order by lvl) result
13 from inter;
RESULT
--------------------
a b c d
SQL>
[EDIT: what if input looks differently?]
That is somewhat simpler. Using REGEXP_SUBSTR, extract digits: ..., 1, 1 returns the first one, ... 1, 2 the second one. Pure REPLACE then replaces numbers with their CHR values.
SQL> with test (col) as
2 (select 'a98c100e' from dual)
3 select
4 replace(replace(col, regexp_substr(col, '\d+', 1, 1), chr(regexp_substr(col, '\d+', 1, 1))),
5 regexp_substr(col, '\d+', 1, 2), chr(regexp_substr(col, '\d+', 1, 2))) result
6 from test;
RESULT
--------------------
abcde
SQL>