This is because ?
designates a placeholder value, so you always sort by constant.
Oracle distinguishes between constant int (that you pass via ?
) and integer position of the column, that you put when you do select * from ... order by 1
. For the latter 1 is a part of syntax and acts like an identifier. You can check this with below code.
As you see, the first query treats 1 as column position and sorts by the first column, when the second actually sorts by the second column.
with a as (
select 1 as s1, 10 as s2 from dual union all
select 3, 20 from dual union all
select 2, 30 from dual
)
select *
from a
order by 1, 2
S1 | S2
-: | -:
1 | 10
2 | 30
3 | 20
with a as (
select 1 as s1, 10 as s2 from dual union all
select 3, 20 from dual union all
select 2, 30 from dual
)
select *
from a
order by cast('1' as number), 2
S1 | S2
-: | -:
1 | 10
3 | 20
2 | 30
db<>fiddle here
UPD:
If you have predefined number of columns and it is not so much of them, you may use CASE
(or DECODE
in Oracle) to choose the column for sorting, but you'll need to take care of datatypes: the same datatypes should be in the same CASE
statement. And it will be hard to maintain such a code. For example:
with a as (
select
'A' as COL1_VARCHAR,
'B' as COL2_VARCHAR,
sysdate as COL3_DATE,
sysdate as COL4_DATE,
1 as COL5_NUMBER,
2 as COL6_NUMBER,
3 as COL7_NUMBER
from dual
)
select *
from a
order by
/*varchar*/
decode(?,
1, COL1_VARCHAR,
2, COL2_VARCHAR
) asc,
/*date*/
decode(?,
3, COL3_DATE,
4, COL4_DATE
) asc,
/*number*/
decode(?,
5, COL5_NUMBER,
6, COL6_NUMBER,
7, COL7_NUMBER
) asc
Or more flexible way - compose dynamic SQL query at the app side and pass it to the DB. But take care of SQL injection, take the metadata to sort with the application code, not from some user input (like input field). Or check the input against DBMS dictionary for example:
select count(1)
from all_tab_cols
where table_name = 'YOUR_TABLE_NAME_IN_FROM'
and column_name = ? --user input with column name