Skip to Main Content
  • Questions
  • Processing order of Analytical function and Model clause

Breadcrumb

Question and Answer

Chris Saxon

Thanks for the question, Girish.

Asked: September 06, 2023 - 10:57 am UTC

Last updated: September 12, 2023 - 1:02 pm UTC

Version: 19

Viewed 100+ times

You Asked

Hi,

In below article
https://oracle.readthedocs.io/en/latest/sql/basics/query-processing-order.html

It is written that the order in which clauses are logically processed by Oracle is as follows:
FROM -> CONNECT BY -> WHERE -> GROUP BY -> HAVING -> SELECT -> ORDER BY

In this order of execution when will analytical functions and MODEL clause will be applied.
Is it applied after SELECT ? or how these will be executed ?

Thanks,
Girish


and Chris said...

Analytic functions are processed during the SELECT. This is why you need to use a subquery to filter their output. WHERE is before SELECT, so the values haven't been assigned for any analytic functions yet!

You can see this by looking at the execution plan (remember these start from the leaves at the bottom).

The query below has the ROW_NUMBER() analytic. This is processed at step 2 (WINDOW NOSORT). This is after the GROUP BY operation at 3 and before the ORDER BY at 1:

set serveroutput off

select row_number() over ( order by employee_id, department_id ) rn 
from   hr.employees e
join   hr.departments 
using ( department_id )
group  by employee_id, department_id
having count(*) = 1
order  by rn desc;

select * 
from   dbms_xplan.display_cursor ( format => 'BASIC' );

--------------------------------------------------------
| Id  | Operation                  | Name              |
--------------------------------------------------------
|   0 | SELECT STATEMENT           |                   |
|   1 |  SORT ORDER BY             |                   |
|   2 |   WINDOW NOSORT            |                   |
|   3 |    FILTER                  |                   |
|   4 |     SORT GROUP BY          |                   |
|   5 |      VIEW                  | index$_join$_001  |
|   6 |       HASH JOIN            |                   |
|   7 |        INDEX FAST FULL SCAN| EMP_DEPARTMENT_IX |
|   8 |        INDEX FAST FULL SCAN| EMP_EMP_ID_PK     |
--------------------------------------------------------


Model expressions have their own clause to process them. Like analytic functions, this comes after GROUP/HAVING and before ORDER BY.

Again, looking at the plan for an example query you can see it's between the GROUP BY at step 4 and ORDER BY at step 1:

select employee_id, department_id
from   hr.employees e
join   hr.departments 
using ( department_id )
group  by employee_id, department_id 
having count(*) = 1
model 
  dimension by ( employee_id, department_id )
  measures     ( 0 total_pay ) (
    total_pay[employee_id, department_id] = 1
  )
order  by employee_id;

select * 
from   dbms_xplan.display_cursor ( format => 'BASIC' );

--------------------------------------------------------
| Id  | Operation                  | Name              |
--------------------------------------------------------
|   0 | SELECT STATEMENT           |                   |
|   1 |  SORT ORDER BY             |                   |
|   2 |   SQL MODEL ORDERED        |                   |
|   3 |    FILTER                  |                   |
|   4 |     HASH GROUP BY          |                   |
|   5 |      VIEW                  | index$_join$_001  |
|   6 |       HASH JOIN            |                   |
|   7 |        INDEX FAST FULL SCAN| EMP_DEPARTMENT_IX |
|   8 |        INDEX FAST FULL SCAN| EMP_EMP_ID_PK     |
--------------------------------------------------------


It's also worth mentioning this is the logical order of processing. The optimizer has lots of tricks to rewrite SQL to make it more efficient.

For example, the first query above sorts by the assigned row number descending (ORDER BY rn DESC). This is to force the database into an extra sorting step.

If instead we use the default (ascending) sort, the optimizer spots we're already sorting the data this way in ROW_NUMBER. The GROUP BY step 3 has this sorting (SORT GROUP BY). So it no longer needs an extra sort after SELECT and the WINDOW operation is last:

select row_number() over ( order by employee_id, department_id ) rn 
from   hr.employees e
join   hr.departments 
using ( department_id )
group  by employee_id, department_id
having count(*) = 1
order  by rn;

select * 
from   dbms_xplan.display_cursor ( format => 'BASIC' );

-------------------------------------------------------
| Id  | Operation                 | Name              |
-------------------------------------------------------
|   0 | SELECT STATEMENT          |                   |
|   1 |  WINDOW NOSORT            |                   |
|   2 |   FILTER                  |                   |
|   3 |    SORT GROUP BY          |                   |
|   4 |     VIEW                  | index$_join$_001  |
|   5 |      HASH JOIN            |                   |
|   6 |       INDEX FAST FULL SCAN| EMP_DEPARTMENT_IX |
|   7 |       INDEX FAST FULL SCAN| EMP_EMP_ID_PK     |
-------------------------------------------------------


So the "rules" above may appear to be broken when you look at the plans for complex queries. In reality the optimizer has transformed your query to make it more efficient.

Rating

  (1 rating)

Comments

A reader, September 11, 2023 - 12:52 pm UTC

Thanks … This helps
Chris Saxon
September 12, 2023 - 1:02 pm UTC

You're welcome

More to Explore

Analytics

Analytic SQL got you confused? Check out Connor McDonald's complete video course.