Order and filter follow different criteria in Oracle? - oracle

I'm having a problem while trying to apply a filter in an Oracle (12.2.0.1.0) query.
When I query for the values of a column in a table using order by that same column, I get
75A0000000
7597953181
7597953182
But then, when I try to filter the rows with the column between the first and the last value I'm not getting results at all.
This works perfectly fine if the column has only numeric values, but it gets it all wrong when there is some alphabetic value in the middle.
It seems like Oracle is following the criteria A<0 when ordering a result but A>0 when filtering that result.
This can be reproduced using this query:
select * from (
WITH DATA AS
(SELECT '1, A, 2' str FROM dual)
SELECT trim(regexp_substr(str, '[^,]+', 1, LEVEL)) str
FROM DATA
CONNECT BY instr(str, ',', 1, LEVEL - 1) > 0)
where str>='1' and str<='A'
order by str
The result of this query is
A
1
2
that should mean A is less than 1, but the filter applied to make it return some values is just the opposite : str values greater or equal than 1 and less or equal than A.
Anybody knows why? Thanks in advance.

To me, it looks as if it is about binary or linguistic sort.
Sample data:
SQL> select * from test;
C
-
1
A
2
Sort by col, as is:
SQL> select col, ascii(col)
2 from test
3 order by col;
C ASCII(COL)
- ----------
A 65
1 49
2 50
Or, sort by col's ASCII code:
SQL> select col, ascii(col)
2 from test
3 order by ascii(col);
C ASCII(COL)
- ----------
1 49
2 50
A 65
Explicitly stating binary or linguistic sort:
SQL> select col
2 from test
3 order by nlssort(col, 'nls_sort=binary'); --> binary
C
-
1
2
A
SQL> select col
2 from test
3 order by nlssort(col, 'nls_sort=croatian'); --> linguistic
C
-
A
1
2
SQL>
Or, alter session and set nls_sort you want:
SQL> alter session set nls_sort='binary';
Session altered.
SQL> select * From test order by col;
C
-
1
2
A
SQL>
If you get unexpected result, check what's nls_sort set in your session:
SQL> select * from nls_session_parameters where parameter = 'NLS_SORT';
PARAMETER VALUE
------------------------------ ----------------------------------------
NLS_SORT BINARY
SQL>
Using your query:
SQL> select * from (
2 WITH DATA AS
3 (SELECT '1, A, 2' str FROM dual)
4 SELECT trim(regexp_substr(str, '[^,]+', 1, LEVEL)) str
5 FROM DATA
6 CONNECT BY instr(str, ',', 1, LEVEL - 1) > 0)
7 where str>='1' and str<='A'
8 order by nlssort(str, 'nls_sort=binary'); --> this
STR
-------
1
2
A
SQL>

Related

How to sort texts with '_' in Oracle exactly like EXCEL?

In Excel When I sort the texts in ascending order, results shows as below. Text with the underscore character precedes the others. And in Excel cell, when I type in ="_" < "A", then "True" shows as expected.
C10_
C10A
C20_
C20A
But, In Oracle, when I sort in ascending order, results shows as below.
(I guess, Oracle treats '_' < 'A' False)
C10A
C10_
C20A
C20_
How can I make Oracle sort the list exactly as Excel does? I have changed ASC to DESC, but the result was not what I expect.
My sorting code is as below,
WITH DATAA AS (
SELECT *
FROM
(
SELECT 'C10_'rr FROM DUAL
UNION
SELECT 'C10A' rr FROM DUAL
UNION
SELECT 'C20_' rr FROM DUAL
UNION
SELECT 'C20A' rr FROM DUAL
)
)
SELECT *
FROM DATAA
ORDER BY rr ASC;
You can achieve this by altering the sorting method using NLS_SORT as following:
According to ORACLE Documentation:
NLS_SORT specifies the type of sort for character data. It overrides
the default value that is derived from NLS_LANGUAGE.
NLS_SORT contains either of the following values:
NLS_SORT = BINARY | sort_name
BINARY specifies a binary sort. sort_name specifies a linguistic sort
sequence.
Here is how you can achieve the result.
SQL> -- Your original query
SQL> --
SQL> WITH DATAA AS (
2 SELECT *
3 FROM
4 (
5 SELECT 'C10_'rr FROM DUAL UNION
6 SELECT 'C10A' rr FROM DUAL UNION
7 SELECT 'C20_' rr FROM DUAL UNION
8 SELECT 'C20A' rr FROM DUAL )
9 )
10 SELECT *
11 FROM DATAA
12 ORDER BY rr ASC;
RR
----
C10A
C10_
C20A
C20_
--
SQL> -- Now altering the sorting method
SQL> --
SQL> --
SQL> alter session set NLS_SORT = German;
Session altered.
SQL> --
SQL> --
SQL> -- Now see the result
SQL> --
SQL> --
SQL> WITH DATAA AS (
2 SELECT *
3 FROM
4 (
5 SELECT 'C10_'rr FROM DUAL UNION
6 SELECT 'C10A' rr FROM DUAL UNION
7 SELECT 'C20_' rr FROM DUAL UNION
8 SELECT 'C20A' rr FROM DUAL )
9 )
10 SELECT *
11 FROM DATAA
12 ORDER BY rr ASC;
RR
----
C10_
C10A
C20_
C20A
SQL>
Cheers!!

How to select second split of column data from oracle database

I want to select the data from a Oracle table, whereas the table columns contains the data as , [ex : key,value] separated values; so here I want to select the second split i.e, value
table column data as below :
column_data
++++++++++++++
asper,worse
tincher,good
golder
null -- null values need to eliminate while selection
www,ewe
from the above data, desired output like below:
column_data
+++++++++++++
worse
good
golder
ewe
Please help me with the query
According to data you provided, here are two options:
result1: regular expressions one (get the 2nd word if it exists; otherwise, get the 1st one)
result2: SUBSTR + INSTR combination
SQL> with test (col) as
2 (select 'asper,worse' from dual union all
3 select 'tincher,good' from dual union all
4 select 'golder' from dual union all
5 select null from dual union all
6 select 'www,ewe' from dual
7 )
8 select col,
9 nvl(regexp_substr(col, '\w+', 1, 2), regexp_substr(col, '\w+', 1,1 )) result1,
10 --
11 nvl(substr(col, instr(col, ',') + 1), col) result2
12 from test
13 where col is not null;
COL RESULT1 RESULT2
------------ -------------------- --------------------
asper,worse worse worse
tincher,good good good
golder golder golder
www,ewe ewe ewe
SQL>

Generate Random String in PL/SQL (oracle 12c)

I'm trying to generate a Random String using PL/SQL with only 2 fixed words. It's this possible?
Is this what you're looking for?
SQL> with
2 -- two fixed words
3 test as
4 (select 'fixed words' col from dual),
5 -- split them to rows
6 inter as
7 (select level lvl, regexp_substr(col, '.', 1, level) let
8 from test
9 connect by level <= length(col)
10 )
11 -- aggregate them back, randomly
12 select listagg(let, '') within group (order by dbms_random.value(1, max_lvl)) result
13 from inter
14 join (select max(lvl) max_lvl from inter) on 1 = 1;
RESULT
--------------------------------------------------------------------------------
reiosdwxf d
SQL> /
RESULT
--------------------------------------------------------------------------------
fe ixoddrws
SQL> /
RESULT
--------------------------------------------------------------------------------
wdxeorsdfi
SQL>

Parse Values with Delimiters Using REGEXP_SUPSTR in Oracle 10g

I have a table called TVL_DETAIL that contains column TVL_CD_LIST. Column TVL_CD_LIST contains three records:
TVL_CD_LIST:
M1180_Z6827
K5900_Z6828
I2510
I've used the following code in an attempt to return the values only(so excluding the underscore):
SELECT
TVL_CD_LIST
FROM TVL_DETAIL
WHERE TVL_CD_LIST IN (SELECT regexp_substr(TVL_CD_LIST,'[^_]+', 1, level) FROM DUAL
CONNECT BY regexp_substr(TVL_CD_LIST,'[^_]+', 1, level) IS NOT NULL)
What I was expecting to see returned in separate rows was:
M1180
Z6827
K5900
Z6828
I2510
But it only returns I2510(which is the original value that doesn't contain an underscore).
What am I doing wrong? Any help is appreciated. Thanks!
To answer your question, you are querying for the list where it matches a sub-element and that will only happen where the list is comprised of one element. What you really wanted to select are the sub-elements themselves.
Note: Explanation of why parsing strings using the regex form '[^_]+' is bad here: https://stackoverflow.com/a/31464699/2543416
You want to parse the list, selecting the elements:
SQL> with TVL_DETAIL(TVL_CD_LIST) as (
select 'M1180_Z6827' from dual union
select 'K5900_Z6828' from dual union
select 'I2510' from dual
)
SELECT distinct regexp_substr(TVL_CD_LIST, '(.*?)(_|$)', 1, level, NULL, 1) element
FROM TVL_DETAIL
CONNECT BY level <= LENGTH(regexp_replace(TVL_CD_LIST, '[^_]', '')) + 1;
-- 11g CONNECT BY level <= regexp_count(TVL_CD_LIST, '_') + 1;
ELEMENT
-----------
Z6827
K5900
M1180
I2510
Z6828
SQL>
And this is cool if you want to track by row and element within row:
SQL> with TVL_DETAIL(row_nbr, TVL_CD_LIST) as (
select 1, 'M1180_Z6827' from dual union
select 2, 'K5900_Z6828' from dual union
select 3, 'I2510' from dual
)
SELECT row_nbr, column_value substring_nbr,
regexp_substr(TVL_CD_LIST, '(.*?)(_|$)', 1, column_value, NULL, 1) element
FROM TVL_DETAIL,
TABLE(
CAST(
MULTISET(SELECT LEVEL
FROM dual
CONNECT BY level <= LENGTH(regexp_replace(TVL_CD_LIST, '[^_]', '')) + 1
-- 11g CONNECT BY LEVEL <= REGEXP_COUNT(TVL_CD_LIST, '_')+1
) AS sys.OdciNumberList
)
)
order by row_nbr, substring_nbr;
ROW_NBR SUBSTRING_NBR ELEMENT
---------- ------------- -----------
1 1 M1180
1 2 Z6827
2 1 K5900
2 2 Z6828
3 1 I2510
SQL>
EDIT: Oops, edited to work with 10g as REGEXP_COUNT is not available until 11g.
The query you have used creates the list but you are comparing the list of record with column it self using the in clause, as such M1180 or Z6827 cannot be equal to M1180_Z6827 and so for K5900_Z6828. I2510 has only one value so it gets matched.
You can use below query if your requirement is exactly what you have mentioned in your desired output.
SQL> WITH tvl_detail AS
2 (SELECT 'M1180_Z6827' tvl_cd_list FROM dual
3 UNION ALL
4 SELECT 'K5900_Z6828' FROM dual
5 UNION ALL
6 SELECT 'I2510' FROM dual)
7 ---------------------------
8 --- End of data preparation
9 ---------------------------
10 SELECT regexp_substr(tvl_cd_list, '[^_]+', 1, LEVEL) AS tvl_cd_list
11 FROM tvl_detail
12 CONNECT BY regexp_substr(tvl_cd_list, '[^_]+', 1, LEVEL) IS NOT NULL
13 AND PRIOR tvl_cd_list = tvl_cd_list
14 AND PRIOR sys_guid() IS NOT NULL;
OUTPUT:
TVL_CD_LIST
--------------------------------------------
I2510
K5900
Z6828
M1180
Z6827

incerement the values in Oracle insert statement (neagative numbers)

I have to insert data in a table. It has a colum whose values should negative numbers like -1,-2,-3..... so on upto -50.
I thought of using sequence but it wont accept the negative values.
How can i genarate i mean insert the values???
"I thought of using sequence but it wont accept the negative values."
Are you sure?
SQL> create table t1 (c1 number)
2 /
Table created.
SQL> create sequence myseq increment by -1
2 /
Sequence created.
SQL> insert into t1 values (myseq.nextval)
2 /
1 row created.
SQL> r
1* insert into t1 values (myseq.nextval)
1 row created.
SQL> r
1* insert into t1 values (myseq.nextval)
1 row created.
SQL> select * from t1
2 /
C1
----------
-1
-2
-3
SQL>
What version of the database are you using? This is from Oracle 11g R1, but I don't think it's a recent feature.
search for any table with minium recourd count you like, and then write:
use a select to generate the data:
select (rownum * -1) as negRowNum from whatevertableyoufound where rownum <= 50
Use 0 - rownum.
SELECT 0 - rownum
FROM DUAL
CONNECT BY LEVEL <= 50;
0-ROWNUM
-1
-2
-3
-4
-5
...

Resources