Oracle sql order by with character in order - sql

Good day, Maybe someone can help me , i want to get n select output on oracle sql , but when i order by it is not correct.
code_order
1.
2.
2.1
3.
4.2
10.1
10.0
21.
21.1
23.
31.
it needs to be ordered numeric and all values has a full stop in.
any ideas??
Thanx, but i see that some values can contain non numeric values as well like C,B etc.
7C. 40. 50. 51. 6.
Table outline :
code_order is varchar2

use order by to_number(code_order)

with
val as
(
SELECT '1.' as c FROM dual
union all
SELECT '2.' as c FROM dual
union all
SELECT '3.' as c FROM dual
union all
SELECT '2.1' as c FROM dual
union all
SELECT '4.2' as c FROM dual
union all
SELECT '10.1' as c FROM dual
union all
SELECT '21.' as c FROM dual
union all
SELECT '10.0' as c FROM dual
union all
SELECT '21.1' as c FROM dual
union all
SELECT '23.' as c FROM dual
union all
SELECT '31.' as c FROM dual
)
SELECT c FROM val
order by to_number(regexp_replace(c, '^(\d+)\..*$', '\1'))
;

Related

How to extract a digit from number in oracle

please help with query how to extract digit '1' from below table using SQL in oracle.
Table
1000
1001
1010
0100
expected result ;
Table
1
11
11
1
You can use the simple string function TRANSLATE (which is faster than regular expressions):
SELECT TRANSLATE(value, '10', '1') AS result
FROM table_name
If you have more than binary digits then:
SELECT TRANSLATE(value, '1023456789.', '1') AS result
FROM table_name
Which, for the sample data:
CREATE TABLE table_name (value) AS
SELECT '1000' FROM DUAL UNION ALL
SELECT '1001' FROM DUAL UNION ALL
SELECT '1010' FROM DUAL UNION ALL
SELECT '0100' FROM DUAL;
Both output:
RESULT
1
11
11
1
db<>fiddle here
I expect you are giving us a simplified version of your problem? One way to achieve this is is using REGEXP_REPLACE to replace all characters but the character 1 with an empty space:
SELECT
REGEXP_REPLACE(YOUR_COLUMN,'[^1]','') AS DESIRED_RESULT
FROM YOUR_TABLE
You can check out this example: db<>fiddle
You can use regexp_replace:
WITH dat AS
(
SELECT '1000' AS numb FROM dual UNION ALL
SELECT '1001' FROM dual UNION ALL
SELECT '1010' FROM dual UNION ALL
SELECT '0100' FROM dual
)
SELECT regexp_replace(numb,'[^1]','')
FROM dat;

Regex to get 00 if the string has less digits

I have a column that is giving me output like 'ABC2001' , 'ABC100145', 'ABC009282' ,' ABC1901'
I want to change this column value to have '00' in between literals and numbers if number is less than 6 digits. Something like -
COL_A
------------
ABC2001
ABC100145
ABC009282
ABC1901
Expected output
COL_B
------------
ABC002001
ABC100145
ABC009282
ABC001901
How to use regex for this ? Currently I am using
SELECT SUBSTR(COL_A,1,3)||LPAD(REGEXP_REPLACE(COL_A,'\D+'),6,'0') FROM TAB
and it is giving me output like -
ABC210073
ABC210073
You do not need (slow) regular expressions and can use simple string functions:
SELECT col_a,
SUBSTR(col_a, 1, 3) || LPAD(SUBSTR(col_a, 4), 6, '0') AS col_b
FROM table_name;
Which, for your sample data:
CREATE TABLE table_name (col_a) AS
SELECT 'ABC2001' FROM DUAL UNION ALL
SELECT 'ABC100145' FROM DUAL UNION ALL
SELECT 'ABC009282' FROM DUAL UNION ALL
SELECT 'ABC1901' FROM DUAL;
Outputs:
COL_A
COL_B
ABC2001
ABC002001
ABC100145
ABC100145
ABC009282
ABC009282
ABC1901
ABC001901
db<>fiddle here
Just for fun, for different prefixes:
usual string functions: trim/lpad/substr:
with table_name (col_a) AS (
SELECT 'ABC2001' FROM DUAL UNION ALL
SELECT 'ABC100145' FROM DUAL UNION ALL
SELECT 'ABC009282' FROM DUAL UNION ALL
SELECT 'ABC1901' FROM DUAL UNION ALL
-- other different prefixes:
select 'ABC2001' from dual union all
select 'AB100145' from dual union all
select 'A-BC9282' from dual union all
select 'A8C2374' from dual union all
select '7x-ABC32129' from dual union all
select '123ABC8942' from dual
)
select v.*, prefix||num as col_b
from (
select
col_a,
rtrim(col_a,'0123456789') as prefix,
lpad(substr(col_a,1+length(rtrim(col_a,'0123456789'))),6,'0') as num
from table_name
) v
;
DBFiddle
using regex functions:
with table_name (col_a) AS (
SELECT 'ABC2001' FROM DUAL UNION ALL
SELECT 'ABC100145' FROM DUAL UNION ALL
SELECT 'ABC009282' FROM DUAL UNION ALL
SELECT 'ABC1901' FROM DUAL UNION ALL
-- other different prefixes:
select 'ABC2001' from dual union all
select 'AB100145' from dual union all
select 'A-BC9282' from dual union all
select 'A8C2374' from dual union all
select '7x-ABC32129' from dual union all
select '123ABC8942' from dual
)
select
col_a,
regexp_replace(
regexp_replace(col_a,'(\d+)$','00000\1')
,'0*(\d{6})$'
,'\1'
) as col_b
from table_name
;
DBFiddle
regex solution for padding numbers to the maximum their length, ie not knowing max numbers length(if it's not hard-coded 6):
select
v.*,
regexp_replace(
regexp_replace(col_a,'(\d+)$',rpad('0',max_num_length,'0')||'\1')
,'0*(\d{'||max_num_length||'})$'
,'\1'
) as col_b
from (
select t.*, max(length(regexp_substr(col_a,'\d+$')))over() as max_num_length
from table_name t
) v
;
DBFiddle

Oracle SQL order by Varchar2 with '_'

I've got a table with a varchar2 column. Sorting by that column did not give me the expected result:
with test (col) as
(select '_83_' from dual union all
select '_81_' from dual union all
select '4___' from dual union all
select '____' from dual
)
select * from test
order by col desc;
returns:
Col
1. '_83_'
2. '_81_'
3. '4___'
4. '____'
I did expect:
Col
1. '4___'
2. '_83_'
3. '_81_'
4. '____'
Can you explain this and help me to get '4___' to the start of my order by statement?
Edit Using Littlefoots statement for preproduceabillity...
Edit I am using Oracle 12c
Edit NLS_Sort is set to German language. This was the issue.
In my local database, NLS_SORT is set to BINARY so It is not reproducible.
WITH TEMO AS
(
SELECT '_83_' AS X FROM DUAL UNION ALL
SELECT '_81_' AS X FROM DUAL UNION ALL
SELECT '4___' AS X FROM DUAL UNION ALL
SELECT '____' AS X FROM DUAL
)
SELECT * FROM TEMO ORDER BY X DESC;
X
----
____
_83_
_81_
4___
But, after changing NLS_SORT from BINARY to GERMAN, the issue is reproduced.
ALTER SESSION SET NLS_SORT=GERMAN;
WITH TEMO AS
(
SELECT '_83_' AS X FROM DUAL UNION ALL
SELECT '_81_' AS X FROM DUAL UNION ALL
SELECT '4___' AS X FROM DUAL UNION ALL
SELECT '____' AS X FROM DUAL
)
SELECT * FROM TEMO ORDER BY X DESC;
X
----
_83_
_81_
4___
____
You can check the NLS values using the following table:
NLS_SESSION_PARAMETERS
NLS_DATABASE_PARAMETERS
so the conclusion is NLS_SORT parameter must be set accordingly because not everyone wants to sort using technique.
Default value of NLS_SORT is derived from NLS_LANGUAGE.
Refer oracle documents for more information about NLS_SORT.
Solution is to change NLS_SORT according to requirement.
Cheers!!
I don't understand the question. Yes, a comment would be more appropriate but I can't post this:
SQL> with test (col) as
2 (select '_83_' from dual union all
3 select '_81_' from dual union all
4 select '4___' from dual union all
5 select '____' from dual
6 )
7 select * from test
8 order by col;
COL
----
____
4___
_81_
_83_
SQL>
As you can see, my results differ from yours, i.e. I can't reproduce what you are saying. Could you explain it once again, please?
Using Oracle 11g R2:
Select Column1 From (
SELECT CAST( '_83_' AS varchar2(4) ) AS Column1 FROM dual
union all
SELECT CAST( '_81_' AS varchar2(4) ) AS Column1 FROM dual
union all
SELECT CAST( '4___' AS varchar2(4) ) AS Column1 FROM dual
union all
SELECT CAST( '____' AS varchar2(4) ) AS Column1 FROM dual
) A order by Column1 desc
Output:
____
_83_
_81_
4___
Personally I'd recommend Tejash's answer but you can also fudge it with something like:
SELECT * FROM table ORDER BY TRANSLATE(col, '_', 'z') desc;

How to get an accurate JOIN using Fuzzy matching in Oracle

I'm trying to join a set of county names from one table with county names in another table. The issue here is that, the county names in both tables are not normalized. They are not same in count; also, they may not be appearing in similar pattern always. For instance, the county 'SAINT JOHNS' in "Table A" may be represented as 'ST JOHNS' in "Table B". We cannot predict a common pattern for them.
That means , we cannot use "equal to" (=) condition while joining. So, I'm trying to join them using the JARO_WINKLER_SIMILARITY function in oracle.
My Left Outer Join condition would be like:
Table_A.State = Table_B.State
AND UTL_MATCH.JARO_WINKLER_SIMILARITY(Table_A.County_Name,Table_B.County_Name)>=80
I've given the measure 80 after some testing of the results and it seemed to be optimal.
Here, the issue is that I'm getting set of "false Positives" when joining. For instance, if there are some counties with similarity in names under the same state ("BARRY'and "BAY" for example), they will be matched if the measure is >=80.
This creates inaccurate set of joined data.
Can anyone please suggest some work around?
Thanks,
DAV
Can you plz help me to build a query that will lookup Table_A for each record in Table B/C/D, and match against the county name in A with highest ranked similarity that is >=80
Oracle Setup:
CREATE TABLE official_words ( word ) AS
SELECT 'SAINT JOHNS' FROM DUAL UNION ALL
SELECT 'MONTGOMERY' FROM DUAL UNION ALL
SELECT 'MONROE' FROM DUAL UNION ALL
SELECT 'SAINT JAMES' FROM DUAL UNION ALL
SELECT 'BOTANY BAY' FROM DUAL;
CREATE TABLE words_to_match ( word ) AS
SELECT 'SAINT JOHN' FROM DUAL UNION ALL
SELECT 'ST JAMES' FROM DUAL UNION ALL
SELECT 'MONTGOMERY BAY' FROM DUAL UNION ALL
SELECT 'MONROE ST' FROM DUAL;
Query:
SELECT *
FROM (
SELECT wtm.word,
ow.word AS official_word,
UTL_MATCH.JARO_WINKLER_SIMILARITY( wtm.word, ow.word ) AS similarity,
ROW_NUMBER() OVER ( PARTITION BY wtm.word ORDER BY UTL_MATCH.JARO_WINKLER_SIMILARITY( wtm.word, ow.word ) DESC ) AS rn
FROM words_to_match wtm
INNER JOIN
official_words ow
ON ( UTL_MATCH.JARO_WINKLER_SIMILARITY( wtm.word, ow.word )>=80 )
)
WHERE rn = 1;
Output:
WORD OFFICIAL_WO SIMILARITY RN
-------------- ----------- ---------- ----------
MONROE ST MONROE 93 1
MONTGOMERY BAY MONTGOMERY 94 1
SAINT JOHN SAINT JOHNS 98 1
ST JAMES SAINT JAMES 80 1
Using some made up test data inline (you would use your own TABLE_A and TABLE_B in place of the first two with clauses, and begin at with matches as ...):
with table_a (state, county_name) as
( select 'A', 'ST JOHNS' from dual union all
select 'A', 'BARRY' from dual union all
select 'B', 'CHEESECAKE' from dual union all
select 'B', 'WAFFLES' from dual union all
select 'C', 'UMBRELLAS' from dual )
, table_b (state, county_name) as
( select 'A', 'SAINT JOHNS' from dual union all
select 'A', 'SAINT JOANS' from dual union all
select 'A', 'BARRY' from dual union all
select 'A', 'BARRIERS' from dual union all
select 'A', 'BANANA' from dual union all
select 'A', 'BANOFFEE' from dual union all
select 'B', 'CHEESE' from dual union all
select 'B', 'CHIPS' from dual union all
select 'B', 'CHICKENS' from dual union all
select 'B', 'WAFFLING' from dual union all
select 'B', 'KITTENS' from dual union all
select 'C', 'PUPPIES' from dual union all
select 'C', 'UMBRIA' from dual union all
select 'C', 'UMBRELLAS' from dual )
, matches as
( select a.state, a.county_name, b.county_name as matched_name
, utl_match.jaro_winkler_similarity(a.county_name,b.county_name) as score
from table_a a
join table_b b on b.state = a.state )
, ranked_matches as
( select m.*
, rank() over (partition by m.state, m.county_name order by m.score desc) as ranking
from matches m
where score > 50 )
select rm.state, rm.county_name, rm. matched_name, rm.score
from ranked_matches rm
where ranking = 1
order by 1,2;
Results:
STATE COUNTY_NAME MATCHED_NAME SCORE
----- ----------- ------------ ----------
A BARRY BARRY 100
A ST JOHNS SAINT JOHNS 80
B CHEESECAKE CHEESE 92
B WAFFLES WAFFLING 86
C UMBRELLAS UMBRELLAS 100
The idea is matches computes all scores, ranked_matches assigns them a sequence within (state, county_name), and the final query picks all the top scorers (i.e. filters on ranking = 1).
You may still get some duplicates as there is nothing to stop two different fuzzy matches scoring the same.

cross-dbms way to check if string is numeric

Ok, I have this field: code varchar(255). It contains some values used in our export routine like
DB84
DB34
3567
3568
I need to select only auto-generated (fully numeric) fields
WHERE is_numeric(table.code)
is_numeric() checks if code field contains only positive digits.
Can you propose anything that will work both under mysql 5.1 and oracle 10g?
Below are three separate implementations for each of SQL Server, MySQL and Oracle. None use (or can) the same approach, so there doesn't seem to be a cross DBMS way to do it.
For MySQL and Oracle, only the simple integer test is show; for SQL Server, the full numeric test is shown.
For SQL Server:
note that isnumeric('.') returns 1.. but it can not actually be converted to float. Some text like '1e6' cannot be converted to numeric directly, but you can pass through float, then numeric.
;with tmp(x) as (
select 'db01' union all select '1' union all select '1e2' union all
select '1234' union all select '' union all select null union all
select '1.2e4' union all select '1.e10' union all select '0' union all
select '1.2e+4' union all select '1.e-10' union all select '1e--5' union all
select '.' union all select '.123' union all select '1.1.23' union all
select '-.123' union all select '-1.123' union all select '--1' union all
select '---1.1' union all select '+1.123' union all select '++3' union all
select '-+1.123' union all select '1 1' union all select '1e1.3' union all
select '1.234' union all select 'e4' union all select '+.123' union all
select '1-' union all select '-3e-4' union all select '+3e-4' union all
select '+3e+4' union all select '-3.2e+4' union all select '1e1e1' union all
select '-1e-1-1')
select x, isnumeric(x),
case when x not like '%[^0-9]%' and x >'' then convert(int, x) end as SimpleInt,
case
when x is null or x = '' then null -- blanks
when x like '%[^0-9e.+-]%' then null -- non valid char found
when x like 'e%' or x like '%e%[e.]%' then null -- e cannot be first, and cannot be followed by e/.
when x like '%e%_%[+-]%' then null -- nothing must come between e and +/-
when x='.' or x like '%.%.%' then null -- no more than one decimal, and not the decimal alone
when x like '%[^e][+-]%' then null -- no more than one of either +/-, and it must be at the start
when x like '%[+-]%[+-]%' and not x like '%[+-]%e[+-]%' then null
else convert(float,x)
end
from tmp order by 2, 3
For MySQL
create table tmp(x varchar(100));
insert into tmp
select 'db01' union all select '1' union all select '1e2' union all
select '1234' union all select '' union all select null union all
select '1.2e4' union all select '1.e10' union all select '0' union all
select '1.2e+4' union all select '1.e-10' union all select '1e--5' union all
select '.' union all select '.123' union all select '1.1.23' union all
select '-.123' union all select '-1.123' union all select '--1' union all
select '---1.1' union all select '+1.123' union all select '++3' union all
select '-+1.123' union all select '1 1' union all select '1e1.3' union all
select '1.234' union all select 'e4' union all select '+.123' union all
select '1-' union all select '-3e-4' union all select '+3e-4' union all
select '+3e+4' union all select '-3.2e+4' union all select '1e1e1' union all
select '-1e-1-1';
select x,
case when x not regexp('[^0-9]') then x*1 end as SimpleInt
from tmp order by 2
For Oracle
case when REGEXP_LIKE(col, '[^0-9]') then col*1 end