In SQL sort by Alphabets first then by Numbers - sql

In H2 Database when i have applied order by on varchar column Numbers are coming first then Alphabets. But need to come Alphabets first then Numbers.
I have tried with
ORDER BY IF(name RLIKE '^[a-z]', 1, 2), name
but getting error like If condition is not available in H2.
My Column Data is Like
A
1-A
3
M
2-B
5
B-2
it should come like
A
B-2
M
1-A
2-B
3
5

try this out
SELECT MYCOLUMN FROM MYTABLE ORDER BY REGEXP_REPLACE (MYCOLUMN,'(*)(\d)(*)','}\2') , MYCOLUMN

One thing can be done is by altering the ASCII in order by clause.
WITH tab
AS (SELECT 'A' col FROM DUAL
UNION ALL
SELECT '1-A' FROM DUAL
UNION ALL
SELECT '3' FROM DUAL
UNION ALL
SELECT 'M' FROM DUAL
UNION ALL
SELECT '2-B' FROM DUAL
UNION ALL
SELECT '5' FROM DUAL
UNION ALL
SELECT 'B-2' FROM DUAL)
SELECT col
FROM tab
ORDER BY CASE WHEN SUBSTR (col, 1, 1) < CHR (58) THEN CHR (177) || col ELSE col END;
I have Used CHR(58) as ASCII value of numbers end at 57. and CHR(177) is used as this is the maximum in the ASCII table.
FYR : ASCII table

Given the example dataset, I'm not sure if you need further logic than this- so I'll refrain from making further assumptions:
DECLARE #temp TABLE (myval char(3))
INSERT INTO #temp VALUES
('A'), ('1-A'), ('3'), ('M'), ('2-B'), ('5'), ('B-2')
SELECT myval
FROM #temp
ORDER BY CASE WHEN LEFT(myval, 1) LIKE '[a-Z]'
THEN 1
ELSE 2
END
,LEFT(myval, 1)
Gives output:
myval
A
B-2
M
1-A
2-B
3
5

Related

Oracle SQL REGEXP for finding a value between columns, irrespective of their position

I have following set of data in two different columns in a table. I need to check if the BB% value is matching between both the columns or not.
Column A
Column B
BB12,AA13,CC24
AA13,BB12,CC24
BB99,AA34,CC78
AA34,CC78,BB77
AA22,BB33,CC77
AA22,BB33,BB44,CC77
I initially tried using below REGEXP. But this will only work if the BB value in column A is always in position 1 and in Column B with position 2. But I need a REGEXP to check something for row 2 and row 3 scenarios.
REGEXP_SUBSTR(ColumnA, '\w+', 1, 1) <> REGEXP_SUBSTR(ColumnB, '\w+', 1, 2)
You can try this. But, it does not take account the case there are more than one occurrence of BBXXX in the columnA. In such a case, the comparison will only be based on the first occurence of BBXXX in the columnA.
with your_table (ColumnA, ColumnB) as (
select 'BB12,AA13,CC24', 'AA13,BB12,CC24' from dual union all
select 'BB99,AA34,CC78', 'AA34,CC78,BB77' from dual union all
select 'AA22,BB33,CC77', 'AA22,BB33,BB44,CC77' from dual union all
select 'AA22,BB33,CC77', 'AA22,BB44,BB33,CC77' from dual union all
select 'AA22,BB33,CC77', 'BB33,AA22,BB44,CC77' from dual
)
select COLUMNA, COLUMNB
from your_table t
where ','||COLUMNB||',' NOT like '%,'||regexp_substr(COLUMNA, 'BB[^,]*', 1, 1)||',%'
;
I believe this will do the trick and allow for the value in COLB to be in any position. Return the rows where the first "BB" element in COLA is not in COLB. Note row 3 has the "BB" values swapped from the original example to show order does not matter.
WITH tbl(ID, cola, colb) AS (
SELECT 1, 'BB12,AA13,CC24','AA13,BB12,CC24' FROM dual UNION ALL
SELECT 2, 'BB99,AA34,CC78','AA34,CC78,BB77' FROM dual UNION ALL
SELECT 3, 'AA22,BB33,CC77','AA22,BB44,BB33,CC77' FROM dual
)
SELECT ID, COLA, COLB
FROM tbl T
WHERE NOT REGEXP_LIKE(COLB, REGEXP_SUBSTR(COLA, 'BB[^,]*'));
ID COLA COLB
---------- -------------- -------------------
2 BB99,AA34,CC78 AA34,CC78,BB77
I suggest extracting the BBxx substring from each string and then compare the two for equality or inequality:
WITH cteData(ColumnA,ColumnB) AS
(SELECT 'BB12,AA13,CC24', 'AA13,BB12,CC24' FROM DUAL UNION ALL
SELECT 'BB99,AA34,CC78', 'AA34,CC78,BB77' FROM DUAL UNION ALL
SELECT 'AA22,BB33,CC77', 'AA22,BB33,BB44,CC77' FROM DUAL)
SELECT COLUMNA,
REGEXP_SUBSTR(COLUMNA, 'BB[^,]+') AS COLA_BB,
COLUMNB,
REGEXP_SUBSTR(COLUMNB, 'BB[^,]+') AS COLB_BB
FROM cteData
WHERE REGEXP_SUBSTR(COLUMNA, 'BB[^,]+') = REGEXP_SUBSTR(COLUMNB, 'BB[^,]+')
dbfiddle 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>

Oracle Order By Sorting: Column Values with character First Followed by number

I have column values as
AVG,ABC, AFG, 3/M, 150,RFG,567, 5HJ
Requirement is to sort as below:
ABC,AFG,AVG,RFG,3/M,5HJ,150,567
Any help?
If you want to sort letters before numbers, then you can test each character. Here is one method:
order by (case when substr(col, 1, 1) between 'A' and 'Z' then 1 else 2 end),
(case when substr(col, 2, 1) between 'A' and 'Z' then 1 else 2 end),
(case when substr(col, 3, 1) between 'A' and 'Z' then 1 else 2 end),
col
This doesn't produce the requested output, but for lexicographic with numbers second TRANSLATE is a simple solution:
http://docs.oracle.com/cd/B19306_01/server.102/b14200/functions196.htm
select value
from (
select 'AVG' as value from dual
union all
select 'ABC' as value from dual
union all
select 'AFG' as value from dual
union all
select '3/M' as value from dual
union all
select '150' as value from dual
union all
select 'RFG' as value from dual
union all
select '567' as value from dual
union all
select '5HJ' as value from dual
)
order by translate(upper(value), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ')
;
This shifts all the letters down and numbers to the end.
Unfortunately within the sort order numbers are before chars.
I could suggest you the put an additional calculated column where you are adding 'ZZZ' in front of the values if they start with a number then you will sort by that virtual column
If there aren't a large number of unique values, build a table that has the value and it's artificial sort order, then order by the sort key. Something like:
create table sort_map
( value varchar2(35),
sort_order number(4)
);
insert into sort_map (value, sort_order) values ('ABC',10);
insert into sort_map (value, sort_order) values ('AFG', 20);
....
insert into sort_map (value, sort_order) values ('150', 70);
insert into sort_map (value, sort_order) values ('567', 80);
--example query
select t.my_col, s.sort_order
from my_table t
join sort_map s
on (t.my_col = s.value)
order by s.sort_order;
A) If you only want to change the order of full numberic secuences just create your isNumeric function:
SELECT * FROM table WHERE isNumeric(field) ORDER BY FIELD
UNION ALL
SELECT * FROM table WHERE NOT isNumeric(field) ORDER BY FIELD
B) If you want to change the order of characters.
Create a funtion that adds a number before every character with a modifier.
Number -> 0
Other -> 2
Letter -> 4
For example:
shortHelper("2FT/") => "024F4T2/"
shortHelper("AZ") => "4A4Z"
shortHelper("Z1") => "4Z01"
Then use "ORDER BY shortHelper(Field)

Decode values of a column in plsql

I have table in which there is a column which contains values as 1:2 or 2:3 or 2:3:4 etc. I need to decode these values on the basis of their values mentioned in the different table. like 1 is x and 2 is y.
there are 5 values in the column. earlier only one value were there where name against that values was being fetched from other table by join condition. But now any combination of 5 values can be there separated by ":"-colon. Please suggest how to get names of these values for a column. Let me know if any other detail is required
Please suggest a way to implement this.
Hi here how you need to go;
first start with inner query it will give all values in you column like 1, 2,3, 4 etc. then need to create mapping table eg. 1 to 'x' , 2 to 'y' physically or logically as I have done and select from mapping table as per result from inner query which is your spitted column values.
with map_data as (
select 1 as d_value , 'X' as m_value from dual
union all
select 2 as d_value , 'Y' as m_value from dual
union all
select 3 as d_value , 'Z' as m_value from dual)
select * from map_data
where d_value in(
WITH data AS (
SELECT '1:2:3' AS "value" FROM DUAL
UNION ALL
SELECT '2:4' AS "value" FROM DUAL
)
SELECT REGEXP_SUBSTR( data."value", '[^:]+', 1, levels.COLUMN_VALUE )
FROM data,
TABLE(
CAST(
MULTISET(
SELECT LEVEL
FROM DUAL
CONNECT BY LEVEL <= LENGTH( regexp_replace( "value", '[^:]+')) + 1
) AS sys.OdciNumberList
)
) levels)

Concatenate results from a SQL query in Oracle

I have data like this in a table
NAME PRICE
A 2
B 3
C 5
D 9
E 5
I want to display all the values in one row; for instance:
A,2|B,3|C,5|D,9|E,5|
How would I go about making a query that will give me a string like this in Oracle? I don't need it to be programmed into something; I just want a way to get that line to appear in the results so I can copy it over and paste it in a word document.
My Oracle version is 10.2.0.5.
-- Oracle 10g --
SELECT deptno, WM_CONCAT(ename) AS employees
FROM scott.emp
GROUP BY deptno;
Output:
10 CLARK,MILLER,KING
20 SMITH,FORD,ADAMS,SCOTT,JONES
30 ALLEN,JAMES,TURNER,BLAKE,MARTIN,WARD
I know this is a little late but try this:
SELECT LISTAGG(CONCAT(CONCAT(NAME,','),PRICE),'|') WITHIN GROUP (ORDER BY NAME) AS CONCATDATA
FROM your_table
Usually when I need something like that quickly and I want to stay on SQL without using PL/SQL, I use something similar to the hack below:
select sys_connect_by_path(col, ', ') as concat
from
(
select 'E' as col, 1 as seq from dual
union
select 'F', 2 from dual
union
select 'G', 3 from dual
)
where seq = 3
start with seq = 1
connect by prior seq+1 = seq
It's a hierarchical query which uses the "sys_connect_by_path" special function, which is designed to get the "path" from a parent to a child.
What we are doing is simulating that the record with seq=1 is the parent of the record with seq=2 and so fourth, and then getting the full path of the last child (in this case, record with seq = 3), which will effectively be a concatenation of all the "col" columns
Adapted to your case:
select sys_connect_by_path(to_clob(col), '|') as concat
from
(
select name || ',' || price as col, rownum as seq, max(rownum) over (partition by 1) as max_seq
from
(
/* Simulating your table */
select 'A' as name, 2 as price from dual
union
select 'B' as name, 3 as price from dual
union
select 'C' as name, 5 as price from dual
union
select 'D' as name, 9 as price from dual
union
select 'E' as name, 5 as price from dual
)
)
where seq = max_seq
start with seq = 1
connect by prior seq+1 = seq
Result is: |A,2|B,3|C,5|D,9|E,5
As you're in Oracle 10g you can't use the excellent listagg(). However, there are numerous other string aggregation techniques.
There's no particular need for all the complicated stuff. Assuming the following table
create table a ( NAME varchar2(1), PRICE number);
insert all
into a values ('A', 2)
into a values ('B', 3)
into a values ('C', 5)
into a values ('D', 9)
into a values ('E', 5)
select * from dual
The unsupported function wm_concat should be sufficient:
select replace(replace(wm_concat (name || '#' || price), ',', '|'), '#', ',')
from a;
REPLACE(REPLACE(WM_CONCAT(NAME||'#'||PRICE),',','|'),'#',',')
--------------------------------------------------------------------------------
A,2|B,3|C,5|D,9|E,5
But, you could also alter Tom Kyte's stragg, also in the above link, to do it without the replace functions.
Here is another approach, using model clause:
-- sample of data from your question
with t1(NAME1, PRICE) as(
select 'A', 2 from dual union all
select 'B', 3 from dual union all
select 'C', 5 from dual union all
select 'D', 9 from dual union all
select 'E', 5 from dual
) -- the query
select Res
from (select name1
, price
, rn
, res
from t1
model
dimension by (row_number() over(order by name1) rn)
measures (name1, price, cast(null as varchar2(101)) as res)
(res[rn] order by rn desc = name1[cv()] || ',' || price[cv()] || '|' || res[cv() + 1])
)
where rn = 1
Result:
RES
----------------------
A,2|B,3|C,5|D,9|E,5|
SQLFiddle Example
Something like the following, which is grossly inefficient and untested.
create function foo returning varchar2 as
(
declare bar varchar2(8000) --arbitrary number
CURSOR cur IS
SELECT name,price
from my_table
LOOP
FETCH cur INTO r;
EXIT WHEN cur%NOTFOUND;
bar:= r.name|| ',' ||r.price || '|'
END LOOP;
dbms_output.put_line(bar);
return bar
)
Managed to get till here using xmlagg: using oracle 11G from sql fiddle.
Data Table:
COL1 COL2 COL3
1 0 0
1 1 1
2 0 0
3 0 0
3 1 0
SELECT
RTRIM(REPLACE(REPLACE(
XMLAgg(XMLElement("x", col1,',', col2, col3)
ORDER BY col1), '<x>'), '</x>', '|')) AS COLS
FROM ab
;
Results:
COLS
1,00| 3,00| 2,00| 1,11| 3,10|
* SQLFIDDLE DEMO
Reference to read on XMLAGG