How can I sort by a table column in varying cases (Oracle) - sql

How can I sort a table with a column of varchar2 with characters in varying cases (UPPER and lower)?
For example, when I do an order by of the Name column, I get the following results:
ANNIE
BOB
Daniel
annie
bob
What I want is something like this:
ANNIE
annie
BOB
bob
Daniel

Use lower(field), e.g.
select * from tbl order by lower(name)
If you need to address special characters for non-english languages then the other answers about NLSSORT may be what you need. If you don't I would try and KISS and use lower() as it is very easy to remember and use and be read by others (maintainability).

Another option is the use of the NLSSORT function to perform linguistic sorting:
SQL> with test as (select 'ANNIE' as col from dual
2 union all select 'BOB' from dual
3 union all select 'Daniel' from dual
4 union all select 'annie' from dual
5 union all select 'bob' from dual
6 union all select 'Ångström' from dual
7 union all select 'ångström' from dual)
8 select col
9 from test
10 order by nlssort(col, 'NLS_SORT = WEST_EUROPEAN')
11 /
COL
----------
Ångström
ångström
ANNIE
annie
BOB
bob
Daniel
The advantages are more flexibility. One can sort characters with accents as well as different cases together. One can choose to treat some characters in a language specific way by specifying different values for NLS_SORT. Defines an order within the set of equivalent characters. So 'A' and 'a' are sorted together, but within the 'a's, the upper case comes first. Disadvantages I expect that NLSSORT uses more CPU than LOWER, though I have not bench marked it. And NLSSORT will only use a prefix of longer strings:
The string returned, also known as the collation key, is of RAW data
type. The length of the collation key resulting from a given char
value for a given collation may exceed 2000 bytes, which is the
maximum length of the RAW value returned by NLSSORT. In this case,
NLSSORT calculates the collation key for a maximum prefix, or initial
substring, of char so that the calculated result does not exceed 2000
bytes. For monolingual collations, for example FRENCH, the prefix
length is typically 1000 characters. For multilingual collations, for
example GENERIC_M, the prefix is typically 500 characters. The exact
length may be lower or higher depending on the collation and the
characters contained in char.

If you're on relatively recent versions of Oracle, you should look at setting NLS_SORT/NLS_COMP, rather than using the LOWER() function.
If you don't want to globally affect the instance, you can use the NLSSORT() function to set the NLS_SORT for the scope of a specific query.
SQL> create table case_insensitive(a varchar2(10));
Table created.
SQL> insert into case_insensitive values('D');
1 row created.
SQL>
SQL>
SQL> c/'D/'c
1* insert into case_insensitive values('c')
SQL> /
1 row created.
SQL> c/'c/'B
1* insert into case_insensitive values('B')
SQL> /
1 row created.
SQL> c/'B/'a
1* insert into case_insensitive values('a')
SQL> /
1 row created.
SQL> commit;
Commit complete.
SQL> select * from case_insensitive;
A
----------
D
c
B
a
SQL> select * from case_insensitive order by a;
A
----------
B
D
a
c
SQL> select * from case_insensitive order by nlssort(a,'NLS_SORT=BINARY_CI');
A
----------
a
B
c
D
A good example of this can be found here.

You can use INITCAP e.g.
SELECT fld FROM tbl ORDER BY INITCAP(fld) ASC;

You can use the Order by cluse for this
select col_name from table_name
order by col_name ;

Related

List of values as table

I'm looking for a smarter way to have a list of values as a table in Oracle.
What I do nowadays is
select 'value1' as val from dual
union
select 'value2' from dual
What I'm hoping for is some function/way/magic, that I'll do for example
select 'value1', 'value2' from dual -- + some additional magic
I'm looking for non-PL/SQL way which I think is overkill, but I'm not saying definite no to PL/SQL if that's the only option, but I can look here Create an Oracle function that returns a table for inspiration for PL/SQL. But extra table to have a list seems still easier to maintain than PL/SQL.
The motivation for not using select distict from transactional table is that I want to have a defined list of values, and with that approach, I can miss those I expect there but have no records in the table.
The expected number of elements in the list is several tens of records (like 30).
Here is one option:
select column_value
from table(sys.dbms_debug_vc2coll('value1', 'value2', 'value3', 'value4'));
Starting Oracle 12.2, you don't need the table function:
select column_value
from sys.dbms_debug_vc2coll('value1', 'value2', 'value3', 'value4');
Or yet another, similar:
SQL> select column_value
2 from table(sys.odcivarchar2list('Little', 'Foot', 'Scott', 'Tiger'))
3 order by column_value;
COLUMN_VALUE
----------------------------------------------------------------------------
Foot
Little
Scott
Tiger
SQL>
Starting with Oracle 12c you could use JSON_TABLE for that:
select *
from json_table('["value1", "value2"]', '$[*]'
columns val varchar(20) path '$');
If you aren't on 12c and can't use json_table (or even if you are/can but don't want to) you could use an XML sequence via xmltable instead:
select *
from xmltable('"value1", "value2", "value3"');
Result Sequence
--------------------------------------------------------------------------------
value1
value2
value3
I'd probably use an ODCI collection by default, but might be interesting to compare how all of these options compare with large data volumes - with 30 values you might not be able to see much of a difference.
Also, You can use the connect by query:
SQL> select regexp_substr('VALUE1,VALUE2','[^,]+', 1, level) from dual
2 connect by level <= regexp_count('VALUE1,VALUE2', '[^,]+');
REGEXP_SUBSTR('VALUE1,VALUE2','[^,]+',1,LEVEL)
----------------------------------------------------
VALUE1
VALUE2
SQL>

How to check/select if a string contains value from a table column values?

Lets say I have this number 123456789 and I have a table column which have different values numbers like:
TABLE_COLUMN
123
456
555
763
Is there a way to do something like SELECT * FROM TABLE WHERE 123456789 CONTAINS (values from that table column).
Are you looking for this?
select t.*
from table t
where cast(123456789 as varchar2(255)) like '%' || cast(table_column as varchar2(255)) || '%';
The explicit casts are not necessary, but I'm not a fan of implicit type conversion.
How about INSTR?
SQL> with test (tc) as
2 (select '123' from dual union all
3 select '456' from dual union all
4 select '555' from dual union all
5 select '763' from dual
6 )
7 select *
8 from test
9 where instr('123456789', tc) > 0;
TC
---
123
456
SQL>
You show some pseudo-code, but I doubt that's what you want to do. You show "select * from table where..." - is the string 123456789 in the same table where you have the column against which you must check? That sounds odd.
Rather, I imagine you have a table with a column of values against which you must test, and an "input" value (either a single one or perhaps values in ANOTHER table), and you must test that input value against ALL the values (in ALL rows) in the "test" table.
If so, you probably want something like this... I show the input as a bind variable, but you can change this easily for other uses.
select <whatever>
from <wherever>
where exists (select * from <table> where instr(:input_string, table_column) > 0)
If the inputs (or the values stored in that column) are numbers instead of strings, you can convert to strings using TO_CHAR().

Select data from dual table

I have a data as following:
1203
1222
3201
4300
Which are numbers of products, so i need to select them as a data in table ..
I think the solution will be with using dual table ..
I tried the following code:
Select * from { values '1203','1222','3201','4300'} as Table1
But it not working !
Oracle has the TABLE function, which takes as input a varray and returns an anonymous table (anonymous means "the table doesn't have a name"), with a single column named column_value.
Oracle also has the function sys.odcinumberlist(), which takes as input a comma-separated list of numbers and returns a varray of numbers. (Similarly, to generate a varray of varchar2 values, there is sys.odcivarchar2list().)
To input your for values and return a result with a single column named num (or any other name you want), you can use these two functions together - and alias the column in the select clause. Like this:
select column_value as num
from table( sys.odcinumberlist( 1203, 1222, 3201, 4300 ) )
;
NUM
----
1203
1222
3201
4300
In standard SQL you would need to use:
Select *
from (values ('1203','1222','3201','4300')) as Table1;
That would create a single row with four (character) columns. If you intended to return four rows it would be:
Select *
from (values ('1203'),('1222'),('3201'),('4300')) as Table1
However, Oracle does NOT support that syntax. Neither does it support a SELECT without a FROM clause.
If you want a single row with 4 columns in Oracle you need
select '1203','1222','3201','4300'
from dual;
If you want four rows you need
select '1203'
from dual
union all
select '1222'
from dual
union all
select '3201'
from dual
union all
select '4300'
from dual;
Additionally: '1203' is not a "number". It's a character string. Number literals are written without quotes in SQL: 1203 is a number
You can also use XMLTABLE.
select TO_NUMBER(TRIM(COLUMN_VALUE)) as numbers from XMLTABLE( '1203,1222,3201,4300');
Numbers
-------
1203
1222
3201
4300
This works only for numbers.
EDIT: Better to wrap it with to_number as suggested by experts in the comments.

While inserting multiple rows what does the statement 'select 1 from dual' do?

While inserting multiple rows into a table using the following style :
insert all
into ghazal_current (GhazalName,Rating) values('Ajab Apna Haal Hota Jo Visaal-e-Yaar Hota',5)
into ghazal_current (GhazalName,Rating) values('Apne Hothon Par Sajana Chahta Hun',4)
into ghazal_current (GhazalName,Rating) values('Shaam Se Aankh Mein Nami Si Hai',4)
into ghazal_current (GhazalName,Rating) values('Tumhe Yaad Ho Ke Na Yaad Ho',3)
select 1 from dual;
What does the statement select 1 from dual mean ? What is it here for ?
DUAL is a built-in table, useful because it is guaranteed to return only one row. This means DUAL may be used to get pseudo-columns such as user or sysdate, the results of calculations and the like. The owner of DUAL is SYS but it can be accessed by every user. DUAL is well-covered in the documentation. Find out more.
In your case, SELECT 1 FROM DUAL; will simply returns 1. You need it because the INSERT ALL syntax demands a SELECT clause but you are not querying the input values from a table.
Brief re-introduction to one-row tables
Some SQL databases require all values to come FROM a table or table-like object, whereas others permit queries to construct values ex nihilo:
-- MySQL, sqlite, PostgreSQL, HSQLdb, and many others permit
-- a "naked" select:
SELECT 1;
-- Others *require* a FROM target, like Oracle.
SELECT 1 FROM DUAL;
-- ...and Firebird/Interbase:
SELECT 1 FROM RDB$DATABASE;
-- ...and DB2:
SELECT 1 FROM SYSIBM.SYSDUMMY1;
Here the cardinality of DUAL is important. If it had more than one row, your result set would have more than one row. What happens, for example, when you SELECT 1 FROM A_Table_With_Ten_Rows?
Why DUAL is used here
The SQL construct VALUES (<row-value-expression>) is a row value constructor. VALUES (1, 2, 3) "creates" a row of values just as SELECT 1, 2, 3 does.
Oracle, of course, requires that these values come FROM somewhere.
As a demonstration, instead of SELECTing from DUAL at the end of the INSERT ALL, try a table with N rows, and you'll see that each VALUES() row is inserted N times.
There are some samples about using dual in Queries:
select sysdate from dual /--it returns date of system
SELECT chr(223) FROM dual /--it returns character of Asciهi code
select my_sequence.nextval from dual; /-- It returns the next value of a sequence
select to_char(sysdate,'yyyy/mm/dd','nls_calendar=persian')from dual
/--returns persian date of system

Oracle Select numbers from an IN clause

I'm looking for the best way to select numbers directly from an in clause.
Basically like:
SELECT * FROM (2,6,1,8);
That doesn't work. I can do it this way:
SELECT Lv FROM ( SELECT Level LV
FROM DUAL
CONNECT BY Level < 20)
WHERE Lv IN (2,6,1,8);
But that seems to be a bit clunky. Is there a more elegant way?
You can do
select column_value from table(sys.dbms_debug_vc2coll(1,2,3,4,5));
but that actually returns a varchar2. You can create your own TYPE and use that
create type tab_num is table of number;
/
select column_value from table(tab_num(1,2,3,4,5));
It's also worth looking at the MODEL clause. It looks complicated, but it is very good at generating data
SELECT x from dual
MODEL DIMENSION BY (1 AS z) MEASURES (1 x)
RULES ITERATE (7) (x[ITERATION_NUMBER]=ITERATION_NUMBER+1)
It's more elegant if you materialize an auxiliary numbers table:
SELECT num FROM numbers WHERE num IN (2,6,1,8);
And this is also useful when combined with another table.
For instance, I've had a case where I needed to populate large configuration tables with changes from piecewise results:
Big SP or Excel sheet or report identifies missing cost centers in config gives a large set of results which need to be inserted with varying data in some groups.
Paste partial results into a individual comma separated lists:
INSERT INTO {stuff}
SELECT {stuff}, 130 as line_item
FROM numbers
WHERE numbers.num IN ({pasted a section of results})
INSERT INTO {stuff}
SELECT {stuff}, 135 as line_item
FROM numbers
WHERE numbers.num IN ({pasted another section of results})
If you don't explicitly need the IN clause, you could use UNION:
select 2 from dual
union
select 6 from dual
union
select 1 from dual
union
select 8 from dual
There is a more elegant variant to INSERT multiple rows into a table:
INSERT ALL
INTO table (col) VALUES ('a')
INTO table (col) VALUES ('b')
INTO table (col) VALUES ('c')
SELECT * FROM dual;
But I don't know a way to do that for a SELECT.