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

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>

Related

how to get the number after '-' in Oracle

I have some strings in my table. They are like 1101-1, 1101-2, 1101-10, 1101-11 pulse, shock, abc, 1104-2, 1104-11, 2201-1, 2202-4. I tried to sort them like below:
1101-1
1101-2
1101-10
1101-11
1104-2
1104-11
2201-1
2202-4
abc
pulse
shock
But I can't get the sort correctly. Below is my codes:
select column from table
order by regexp_substr(column, '^\D*') nulls first,
to_number(substr(regexp_substr(column, '\d+'),1,4)) asc
Sort numbers as numbers:
first the ones in front of the hyphen (line #16)
then the ones after it (line #17),
then the rest (line #18)
Mind the to_number function! Without it, you'll be sorting strings! and get the wrong result.
SQL> with test (col) as
2 ( select '1101-1' from dual union all
3 select '1101-2' from dual union all
4 select '1101-10' from dual union all
5 select '1101-11' from dual union all
6 select 'pulse' from dual union all
7 select 'shock' from dual union all
8 select 'abc' from dual union all
9 select '1104-2' from dual union all
10 select '1104-11' from dual union all
11 select '2201-1' from dual union all
12 select '2202-4' from dual
13 )
14 select col
15 from test
16 order by to_number(regexp_substr(col, '^\d+')),
17 to_number(regexp_substr(col, '\d+$')),
18 col;
COL
-------
1101-1
1101-2
1101-10
1101-11
1104-2
1104-11
2201-1
2202-4
abc
pulse
shock
11 rows selected.
SQL>
For your examples, this should do:
order by regexp_substr(column, '^[^-]+'), -- everything before the hyphen
len(column),
column
To get the number after '-' specifically:
with ttt (col) as (
select cast(column_value as varchar2(10)) as second_str
from table(sys.dbms_debug_vc2coll
( '1101-1'
, '1101-2'
, '1101-10'
, '1101-11'
, '1104-2'
, '1104-11'
, '2201-1'
, '2202-4'
, 'abc'
, 'pulse'
, 'shock'
))
)
select col
, regexp_substr(col, '(^\d+-)(\d+)', 1, 1, '', 2)
from ttt;
COL SECOND_STR
---------- ----------
1101-1 1
2201-1 1
1101-10 10
1101-11 11
1104-11 11
1101-2 2
1104-2 2
2202-4 4
abc
pulse
shock
11 rows selected
This treats the text string as two values, (^\d+-) followed by (\d+), and takes the second substring (the final '2' parameter). As only positional parameters are allowed for built-in SQL functions, you also have to specify occurrence (1) and match param (null, as we don't care about case etc).

Find strings starting with alphanumeric in Oracle

I want to search for all records starting alphabet or number only.
I know there is REGEXP_LIKE to find if col contains alphanumeric but couldn't apply it for starting with.
SELECT * FROM mytable WHERE col1 like 'ABC:XYZ%'
I have data in below format:--
ABC:XYZ
ABC:XYZ (ERW)
ABC:XYZ TMN
ABC:XYZ123
ABC:XYZRTY:YER
I am trying to get only below output
ABC:XYZ
ABC:XYZ123
ABC:XYZRTY:YER
Regards
Something like this? Sample data up to line #7; query you might be interested in begins at line #8.
SQL> with mytable (col1) as
2 (select 'ABC:XYZ' from dual union all
3 select 'ABC:XYZ (ERW)' from dual union all
4 select 'ABC:XYZ TMN' from dual union all
5 select 'ABC:XYZ123' from dual union all
6 select 'ABC:XZYRTY:YER' from dual
7 )
8 select col1
9 from mytable
10 where not regexp_like(col1, '[^[:alnum:]:]');
COL1
--------------
ABC:XYZ
ABC:XYZ123
ABC:XZYRTY:YER
SQL>

TRIM Trailing Zeros

I know this is a relatively simple question, but I couldn't find anything with my keywords using Google.
I am referencing with a SQL (Oracle) to a column that has numbers like that:
100
12500
300
Now I need to remove the last 2 zeros.
This approach is not working:
Trim(TRAILING '00' FROM F0035.Nr) "Sequence",
Does anyone have any idea?
The result should be a column with numbers - not a text
See these two options:
SQL> with test (col) as
2 (select '100' from dual union all
3 select '12500' from dual union all
4 select '300' from dual
5 )
6 select col,
7 to_number(substr(col, 1, length(col) - 2)) result_1,
8 to_number(col) / 100 result_2
9 from test;
COL RESULT_1 RESULT_2
----- ---------- ----------
100 1 1
12500 125 125
300 3 3
SQL>
the first one removes the last two characters (from your sample data, it seems that they are always 00)
the second one divides that "number" by 100
You could do this:
SELECT regexp_replace(F0035.Nr, '^(.*)00$', '\1')
FROM F0035
You can easily tweak the regular expression if your requirements change subtly, such as removing more than 2 trailing zeros (e.g. ^(.*)00+), or other characters
with test (col) as (
select 10 from dual union all
select 100 from dual union all
select 1000 from dual union all
select 12500 from dual union all
select 125002 from dual union all
select 3000 from dual
)
select col,
case when substr(col, -2) = '00' then col/100 else col end newnum
from test;
If the column contains numbers, why are you using string operations?
If all values have two 00s as the end, then:
F0035.Nr / 100
If some do not, then use a case:
(case when mod(F0035.Nr, 100) = 0 then F0035.Nr / 100 else F0035.Nr end)
I don't recommend converting to a string to do numeric operations under most circumstances.
The following expression will strip off any number of zeroes from a number:
SELECT NR / POWER(10, LENGTH(REGEXP_SUBSTR(TO_CHAR(NR), '0*$')))
FROM F0035
db<>fiddle here

Sort a value list that contains letters and also numbers in a specific order

I have a problem in SQL Oracle, I'm trying to create a view that contains values with letters and numbers and I want to sort them in a specific order.
Here is my query:
create or replace view table1_val (val, msg_text) as
select
val, msg_text
from
table_val
where
val in ('L1','L2','L3','L4','L5','L6','L7','L8','L9','L10','L11','L12','L13','L14','G1','G2','G3','G4')
order by lpad(val, 3);
The values are displayed like this:
G1,G2,G3,G4,L1,L2,L3,L4,L5,L6,L7,L8,L9,L10,L11,L12,L13
The thing is that I want to display the L values first and then the G values like in the where condition. The 'val' column is VARCHAR2(3 CHAR). The msg_text column is irrelevant. Can someone help me with that? I use Oracle 12C.
You must interpret the second part of the val column as a number
order by
case when val like 'L%' then 0 else 1 end,
to_number(substr(val,2))
This work fine for your current data, but may fail in future if a new record is added with non-numeric structure.
More conservative (and more hard to write), but safe would be to used a decode for all the current keys, ordering unknown keys on the last position (id = 18 in the example):
order by
decode(
'L1',1,
'L2',2,
'L3',3,
'L4',4,
'L5',5,
'L6',6,
'L7',7,
'L8',8,
'L9',9,
'L10',10,
'L11',11,
'L12',12,
'L13',13,
'G1',14,
'G2',15,
'G3',16,
'G4',17,18)
You can't do anything based on the order of the WHERE condition
But you can use a CASE on the ORDER BY
ORDER BY CASE
WHEN SUBSTR(val, 1, 1) = 'L' THEN 1
WHEN SUBSTR(val, 1, 1) = 'G' THEN 2
ELSE 3
END,
TO_NUMBER (SUBSTR(val, 2, 10));
Another option to consider might be using regular expressions, such as
SQL> with table1_val (val) as
2 (select 'L1' from dual union all
3 select 'L26' from dual union all
4 select 'L3' from dual union all
5 select 'L21' from dual union all
6 select 'L11' from dual union all
7 select 'L4' from dual union all
8 select 'G88' from dual union all
9 select 'G10' from dual union all
10 select 'G2' from dual
11 )
12 select val
13 from table1_val
14 order by regexp_substr(val, '^[[:alpha:]]+') desc,
15 to_number(regexp_substr(val, '\d+$'));
VAL
---
L1
L3
L4
L11
L21
L26
G2
G10
G88
9 rows selected.
SQL>

SQL function REGEXP_SUBSTR: Regular Expression how to get the content between two characters but not include them

For these strings
RSLR_AIRL19_ID3454_T20030913091226
RSLR_AIRL19_ID3122454_T20030913091226
RSLR_AIRL19_ID34_T20030913091226
How to get the number after ID ?
Or how to get the content between two characters but not include them ?
I use this '/\_ID([^_]+)/' got matches like Array ( [0] => _ID3454 [1] => 3454 )
Is this the right way?
To extract a number after an ID, you could write a similar query.
SQL> with t1 as(
2 select 'RSLR_AIRL19_ID3454_T20030913091226' as col from dual union all
3 select 'RSLR_AIRL19_ID3122454_T20030913091226' from dual union all
4 select 'RSLR_AIRL19_ID34_T20030913091226' from dual
5 )
6 select regexp_substr(col, '^([[:alnum:]]+_){2}ID([[:digit:]]+)_([[:alnum:]]+){1}$', 1, 1, 'i', 2) as ID
7 from t1
8 ;
ID
-------------
3454
3122454
34
Or, if you want to extract digits from a first occurrence of the pattern without verifying if an entire string matches a specific format:
SQL> with t1 as(
2 select 'RSLR_AI_RL19_ID3454_T20030913091226' as col from dual union all
3 select 'RSLR_AIRL19_ID3122454_T20030913091226' from dual union all
4 select 'RSLR_AIRL19_ID34_T20030913091226' from dual
5 )
6 select regexp_substr(col, 'ID([[:digit:]]+)', 1, 1, 'i', 1) as ID
7 from t1
8 ;
ID
--------------
3454
3122454
34
With pcre & perl engines :
ID\K\w+
NOTE
\K "restart" the match.
See http://www.phpfreaks.com/blog/pcre-regex-spotlight-k (php use pcre)