Oracle dynamic DESC and ASC in order by
If you also want to make the sort order (ASC/DESC) dynamic, you could do the following:
SELECT ...
Order By CASE WHEN InputParam = 'PRICE' THEN l_so * OFFER_PRICE END,
CASE WHEN InputParam = 'ENDING SOON'
THEN l_so * (SYSDATE - EXPIRY_DATE) END,
CASE WHEN InputParam = 'DISCOUNT' THEN l_so * DISC_PERCENTAGE END,
CASE WHEN InputParam = 'SAVING' THEN l_so * SAVING END
with a variable l_so
that contains 1 or -1 depending upon which sort order you want.
SQL Dynamic ASC and DESC
You can do solutions like @TonyAndrews by manipulating numeric or data values. For VARCHAR2
an alternative to dynamic SQL could be to have two expressions:
order by
case when :sorting='ASC' then col1 end ASC,
case when :sorting='DESC' then col1 end DESC
When :sorting
has the value 'ASC'
the result of that ORDER BY
becomes like if it had been:
order by
col1 ASC,
NULL DESC
When :sorting
has the value 'DESC'
the result of that ORDER BY
becomes like if it had been:
order by
NULL ASC,
col1 DESC
One downside to this method is that those cases where the optimizer can skip a SORT operation because there is an index involved that makes the data already sorted like desired, that will not happen when using the CASE method like this. This will mandate a sorting operation no matter what.
How to specify dynamic order by clause in Oracle
Order by two outer CASE
expressions, one ASC
one DESC
. In the first one check if you want to sort ASC
and if not let the expression return a constant or NULL
, i.e. it doesn't change the order. For the case you want to sort ASC
add an inner CASE
expression, that returns the column to sort by. Analogously handle the DESC
case.
...
ORDER BY CASE
WHEN in_order_dir = 'ASC' THEN
CASE
WHEN in_order_by = 1 THEN
city_code
WHEN in_order_by = 2 THEN
city_name
...
END
END ASC,
CASE
WHEN in_order_dir = 'DESC' THEN
CASE
WHEN in_order_by = 1 THEN
city_code
WHEN in_order_by = 2 THEN
city_name
...
END
END DESC;
Maybe you have to adapt it because of type incompatibilities between the columns, I can't tell that from what you posted. But it should convey the general concept.
Edit:
On the type problem:
One possibility is to convert the columns to compatible data types in a way the order is kept. For instance, if you have a char_column
(of the type char
), a date_column
and an integer_column
(of date
and integer
) you could do
to_char(date_column, 'YYYYMMDDHH24MISS')
to convert the date and
lpad(11, 38, '0')
to convert the integer to char
.
The cleaner thing to do, but the one with more work to do (for the programmer, in terms of performance there should be no significant difference whatsoever) is to split the expressions once more. I.e. have an outer CASE
for each type your columns are for each direction.
Like
ORDER BY CASE
WHEN in_order_dir = 'ASC' THEN
CASE
WHEN in_order_by = 1 THEN
char_column1
WHEN in_order_by = 2 THEN
char_column2
...
END
END ASC,
CASE
WHEN in_order_dir = 'ASC' THEN
CASE
WHEN in_order_by = 3 THEN
date_column1
WHEN in_order_by = 4 THEN
date_column2
...
END
END ASC,
and so on for all types and analogously for DESC
. Remember, if the column isn't meant to be sorted after such CASE
will yield NULL
and sorting by that won't influence the order.
How to generate Dynamic Order by clause in PL/SQL procedure?
When doing a dynamic sort, I recommend using separate clauses:
order by (case when in_sort_column = 'col1' then col1 end),
(case when in_sort_column = 'col2' then col2 end),
(case when in_sort_column = 'col3' then col3 end)
This guarantees that you will not have an unexpected problem with type conversion, if the columns are of different types. Note that case
return NULL
without an else
clause.
Oracle ORDER BY input value
The Link provided by @user7294900 contains the solution for your question.
Basically, all you have to do is implement the CASE section correctly.
SELECT * FROM your_table
WHERE SOMETHING = 'something'
ORDER BY
CASE WHEN INPUT = 'str1' THEN 1 END,
CASE WHEN INPUT = 'str2' THEN 2 END DESC;
Dynamic order by date data type in Oracle using CASE
Should be simple - just use ISO date format in your case:
TO_CHAR(IR.IDATE, 'yyyy-mm-dd')
and you should be fine.
SQL order by DECODE and CASE return different data for the same conditions
I asked what the datatype was because sorting looked like sorting strings, not numbers.
Have a look at this. First, only one expression in order by clause:
SQL> with test (seq_diff) as
2 (select -990 from dual union all
3 select -610 from dual union all
4 select -1350 from dual union all
5 select -1340 from dual
6 )
7 select *
8 from test
9 order by decode(seq_diff, abs(seq_diff), seq_diff, null);
SEQ_DIFF
----------
-990
-1340
-1350
-610
SQL>
How are they sorted? They aren't. According to sample data set, no seq_diff
is equal to abs(seq_diff)
so ordering moves to null
which causes "randomly" ordered values. They aren't sorted at all.
Now, let's add another decode
into order by:
SQL> with test (seq_diff) as
2 (select -990 from dual union all
3 select -610 from dual union all
4 select -1350 from dual union all
5 select -1340 from dual
6 )
7 select *
8 from test
9 order by decode(seq_diff, abs(seq_diff), seq_diff, null),
10 decode(seq_diff, abs(seq_diff), null, seq_diff) desc;
SEQ_DIFF
----------
-990
-610
-1350
-1340
SQL>
The first decode
didn't do anything, as if it doesn't exist so we move on to the second decode
. Again, according to data set, no seq_diff
is equal to abs(seq_diff)
, but this time it returns seq_diff
. Documentation (as @krokodilko mentioned in their comment) says:
- The DECODE function returns a value that is the same datatype as the first result in the list.
- If the first result is NULL, then the return value is converted to VARCHAR2.
- this is our case, so return value (
seq_diff
) is converted tovarchar2
- this is our case, so return value (
- If the first result has a datatype of CHAR, then the return value is converted to VARCHAR2.
- If no matches are found, the default value is returned.
- If default is omitted and no matches are found, then NULL is returned.
Once again: our case is the second one:
decode(seq_diff, abs(seq_diff), null, seq_diff)
----
the first result is NULL
Therefore, seq_diff
is converted to a string and values are sorted as such. Let's check that:
SQL> with test (seq_diff) as
2 (select -990 from dual union all
3 select -610 from dual union all
4 select -1350 from dual union all
5 select -1340 from dual
6 )
7 select *
8 from test
9 order by to_char(seq_diff) desc;
SEQ_DIFF
----------
-990
-610
-1350
-1340
SQL>
See? The same result as we got with order by decode(seq_diff, abs(seq_diff), null, seq_diff) desc;
The final part of your order by
clause is trivial (abs(dist_diff)
), I guess there's no need to explain that.
That's why you got strange result with DECODE
; actually, that's expected behavior.
Dynamic order direction
You could have two near-identical ORDER BY
items, one ASC
and one DESC
, and extend your CASE
statement to make one or other of them always equal a single value:
ORDER BY
CASE WHEN @OrderDirection = 0 THEN 1
ELSE
CASE WHEN @OrderByColumn = 'AddedDate' THEN CONVERT(varchar(50), AddedDate)
WHEN @OrderByColumn = 'Visible' THEN CONVERT(varchar(2), Visible)
WHEN @OrderByColumn = 'AddedBy' THEN AddedBy
WHEN @OrderByColumn = 'Title' THEN Title
END
END ASC,
CASE WHEN @OrderDirection = 1 THEN 1
ELSE
CASE WHEN @OrderByColumn = 'AddedDate' THEN CONVERT(varchar(50), AddedDate)
WHEN @OrderByColumn = 'Visible' THEN CONVERT(varchar(2), Visible)
WHEN @OrderByColumn = 'AddedBy' THEN AddedBy
WHEN @OrderByColumn = 'Title' THEN Title
END
END DESC
Related Topics
Time Zone Conversion in SQL Query
Help with Writing a SQL Query for Nested Sets
Group by Every N Records in T-Sql
Using in Clause in a Native SQL Query
How to Split Comma Separated String Inside Stored Procedure
How to Log All Failed SQL Statements in Oracle 10G
Find Top 10 Latest Record for Each Buyer_Id for Yesterday's Date
Tsql Datediff to Return Number of Days with 2 Decimal Places
How to Change the Primary Key to Be Non-Clustered
SQL Query with Union in Doctrine Symfony
Differencebetween Join Keyword and Inner Join Keyword in Oracle SQL
A Reliable Way to Verify T-SQL Stored Procedures
Rounding Issue in Log and Exp Functions
Retrieve Aggregates for Arbitrary Time Intervals
Many-To-Many Relationship: Use Associative Table or Delimited Values in a Column