Skip to Main Content

Breadcrumb

Question and Answer

Connor McDonald

Thanks for the question, Mohammad.

Asked: April 16, 2018 - 7:12 am UTC

Last updated: April 19, 2018 - 12:59 am UTC

Version: 12.2

Viewed 1000+ times

You Asked

Hello,

we have some huge tables to query, and with order by clause (must be used) it takes a very long time for a query to be done.

as I know that we can do the order by at run time using dynamic SQL, but my questions are:
1. do we have any other options to do it at runtime?
2. is it possible to do the order by clause at run time with the query itself (without PL/SQL)?

Best Regards

and Connor said...

As long as you keep the data types consistent, you can use a CASE statement to 'dynamically' order, eg

SQL> variable seq varchar2(10)
SQL>
SQL> exec :seq := 'EMPNO'

PL/SQL procedure successfully completed.

SQL> select *
  2  from emp
  3  order by
  4    case
  5      when :seq = 'ENAME' then ename
  6      when :seq = 'EMPNO' then lpad(empno,10,'0')
  7      when :seq = 'HIREDATE' then to_char(hiredate,'yyyymmddhh24miss')
  8    end;

     EMPNO ENAME      JOB              MGR HIREDATE         SAL       COMM     DEPTNO
---------- ---------- --------- ---------- --------- ---------- ---------- ----------
      7369 SMITH      CLERK           7902 17-DEC-80        800                    20
      7499 ALLEN      SALESMAN        7698 20-FEB-81       1600        300         30
      7521 WARD       SALESMAN        7698 22-FEB-81       1250        500         30
      7566 JONES      MANAGER         7839 02-APR-81       2975                    20
      7654 MARTIN     SALESMAN        7698 28-SEP-81       1250       1400         30
      7698 BLAKE      MANAGER         7839 01-MAY-81       2850                    30
      7782 CLARK      MANAGER         7839 09-JUN-81       2450                    10
      7788 SCOTT      ANALYST         7566 09-DEC-82       3000                    20
      7839 KING       PRESIDENT            17-NOV-81       5000                    10
      7844 TURNER     SALESMAN        7698 08-SEP-81       1500                    30
      7876 ADAMS      CLERK           7788 12-JAN-83       1100                    20
      7900 JAMES      CLERK           7698 03-DEC-81        950                    30
      7902 FORD       ANALYST         7566 03-DEC-81       3000                    20
      7934 MILLER     CLERK           7782 23-JAN-82       1300                    10

14 rows selected.

SQL>
SQL> exec :seq := 'ENAME'

PL/SQL procedure successfully completed.

SQL> /

     EMPNO ENAME      JOB              MGR HIREDATE         SAL       COMM     DEPTNO
---------- ---------- --------- ---------- --------- ---------- ---------- ----------
      7876 ADAMS      CLERK           7788 12-JAN-83       1100                    20
      7499 ALLEN      SALESMAN        7698 20-FEB-81       1600        300         30
      7698 BLAKE      MANAGER         7839 01-MAY-81       2850                    30
      7782 CLARK      MANAGER         7839 09-JUN-81       2450                    10
      7902 FORD       ANALYST         7566 03-DEC-81       3000                    20
      7900 JAMES      CLERK           7698 03-DEC-81        950                    30
      7566 JONES      MANAGER         7839 02-APR-81       2975                    20
      7839 KING       PRESIDENT            17-NOV-81       5000                    10
      7654 MARTIN     SALESMAN        7698 28-SEP-81       1250       1400         30
      7934 MILLER     CLERK           7782 23-JAN-82       1300                    10
      7788 SCOTT      ANALYST         7566 09-DEC-82       3000                    20
      7369 SMITH      CLERK           7902 17-DEC-80        800                    20
      7844 TURNER     SALESMAN        7698 08-SEP-81       1500                    30
      7521 WARD       SALESMAN        7698 22-FEB-81       1250        500         30

14 rows selected.

SQL> exec :seq := 'HIREDATE'

PL/SQL procedure successfully completed.

SQL> /

     EMPNO ENAME      JOB              MGR HIREDATE         SAL       COMM     DEPTNO
---------- ---------- --------- ---------- --------- ---------- ---------- ----------
      7369 SMITH      CLERK           7902 17-DEC-80        800                    20
      7499 ALLEN      SALESMAN        7698 20-FEB-81       1600        300         30
      7521 WARD       SALESMAN        7698 22-FEB-81       1250        500         30
      7566 JONES      MANAGER         7839 02-APR-81       2975                    20
      7698 BLAKE      MANAGER         7839 01-MAY-81       2850                    30
      7782 CLARK      MANAGER         7839 09-JUN-81       2450                    10
      7844 TURNER     SALESMAN        7698 08-SEP-81       1500                    30
      7654 MARTIN     SALESMAN        7698 28-SEP-81       1250       1400         30
      7839 KING       PRESIDENT            17-NOV-81       5000                    10
      7900 JAMES      CLERK           7698 03-DEC-81        950                    30
      7902 FORD       ANALYST         7566 03-DEC-81       3000                    20
      7934 MILLER     CLERK           7782 23-JAN-82       1300                    10
      7788 SCOTT      ANALYST         7566 09-DEC-82       3000                    20
      7876 ADAMS      CLERK           7788 12-JAN-83       1100                    20

14 rows selected.

SQL>
SQL>


Rating

  (3 ratings)

Is this answer out of date? If it is, please let us know via a Comment

Comments

Mohammad, April 17, 2018 - 8:58 am UTC

so as a practical case we have to do it with PL/SQL.... and there is no way to do it with SQL?!
Chris Saxon
April 17, 2018 - 9:47 am UTC

I don't see any PL/SQL in the above... What's the problem here?

Mohammad, April 18, 2018 - 9:20 am UTC

true no PL/SQL in the above, but as a practical case I have to assign a value to a variable.... when am doing a report maybe in this case it does not make any sense to do it like this and I'll use PL/SQL to create variables and assign values to them, e.g I want to query customers shipments ordered by date (always must be ordered by date) and table I query is a huge one (millions of records will be returned).
what I'm looking for is to do a single query and do the order by at run time.
Connor McDonald
April 19, 2018 - 12:59 am UTC

Sorry - I don't understand

I want to query customers shipments ordered by date (always must be ordered by date)

In that case, a simple ORDER BY is needed.

Another way

Dan, April 18, 2018 - 11:47 am UTC

I had the same issue where requested order-by involved different data types, and they had to support ASC/DESC.... and pagination.

To get around the datatype consistency that Conner pointed out, I add a row_number() column and actually use that instead in the WHERE clause.

create table dyn_order_by_test
(
    name        varchar2(10),
    date_col    date,
    address_1   varchar2(50),
    address_2   varchar2(50),
    city        varchar2(20),
    state       varchar(2),
    num_col     number
)

Insert into DYN_ORDER_BY_TEST Values ('me', TO_DATE('04/01/2018', 'MM/DD/YYYY'), '123 main street', null, 'NY', 'NY',  345.44);
Insert into DYN_ORDER_BY_TEST Values ('you', TO_DATE('05/01/2018', 'MM/DD/YYYY'), '321 main street', null, 'LA', 'CA', -4);
Insert into DYN_ORDER_BY_TEST values ('you', TO_DATE('05/01/2018', 'MM/DD/YYYY'), '321 main street', null, 'LA', 'CA', -4);
Insert into DYN_ORDER_BY_TEST values ('me', TO_DATE('04/18/2018', 'MM/DD/YYYY'), '123 main street', null, 'NY', 'NY', 1.234);
Insert into DYN_ORDER_BY_TEST values ('me', TO_DATE('04/18/2018', 'MM/DD/YYYY'), '123 main street', null 'NY', 'NY', 1.234);
Insert into DYN_ORDER_BY_TEST Values ('me', TO_DATE('04/18/2018', 'MM/DD/YYYY'), '123 main street', 'line 2', 'NY', 'NY', 1.234);

With zz as
        (SELECT
            d.name,
            d.date_col,
            d.address_1,
            d.address_2,
            d.city,
            d.state,
            d.num_col,
            count(*) over () total_rows ,
            case :sort_col
                when 'NAME' then row_number() over (order by d.name, d.date_col, d.rowid) 
                when 'DATE' then row_number() over (order by d.date_col, d.name, d.rowid) 
                when 'ADDRESS' then row_number() over (order by d.address_1, d.address_2, d.city, d.state, d.rowid) 
                when 'CITY' then row_number() over (order by d.city, d.state, d.date_col, d.rowid) 
                when 'STATE' then row_number() over (order by d.state, d.city, d.name, d.rowid) 
                when 'NUM' then row_number() over (order by d.num_col, d.name, d.rowid) 
            end rn_sort
       FROM dyn_order_by_test d
       ) 
select 
    zz.name,
    zz.date_col,
    zz.address_1,
    zz.address_2,
    zz.city,
    zz.state,
    zz.num_col,
    zz.total_rows,
    zz.rn_sort
from zz                            
where
    case when :order_by = 'DESC' then
        zz.total_rows - zz.rn_sort + 1
    else
        zz.rn_sort
    end
    between ((:curr_page-1)*:page_size)+1 and (:curr_page*:page_size)
order by                             
    case when :order_by = 'DESC' then
        zz.total_rows - zz.rn_sort + 1
    else
        zz.rn_sort
    end


The trick here is the order by and where clause for the pagination

order by [rownumber of a] desc

is the same as
 order by [total count] - [rownumber of a] + 1


So I can "always" sort ASC, I just change what I order by.


What has also let me do is make an intelligent (by adding more columns in the order by) and deterministic (by adding rowed) sort. By intelligent, I mean when sorted by "State", it is ALSO sorted by City and name. So we humans can actually look for something in the returned list.

By deterministic, if I sorted by State, and there are more than 1 page of the same state, as we go from page 1 to page 2, there are no repeats, and every one is shown once.

You still need to scan the entire table (have to, to be able to sort it), but you are getting the dynamic ordering.

Connor McDonald
April 19, 2018 - 12:59 am UTC

nice input.

More to Explore

SQL

The Oracle documentation contains a complete SQL reference.