LEAD/LAG Functions to Place a Value within Extents

March 6, 2014

in Analytics, SQL

LEAD LAG Analytic Functions

LEAD/LAG are analytic functions that provide access to more than one row of a table at the same time, without a self join. Let’s see how.

Take a table that stores the master list of exam grades, mapped to the upper limit up to which the grade applies. The table (GRADE_MASTER) has two columns: {GRADE_CODE, SCORE_UPTO}. For exam scores in the range 0-100, GRADE_MASTER specifies the A-F.

SQL> desc grade_master
 Name              Null?    Type
 ----------------- -------- -----------
 GRADE_CODE        NOT NULL VARCHAR2(2)
 SCORE_UPTO        NOT NULL NUMBER(3)

SQL> select * from grade_master;

GR SCORE_UPTO
-- ----------
F          59
D          69
C          79
B          89
A         100

The above data means that grade F applies to scores 0-59, D applies to scores 60-69, C to scores 70-79, and so on.

To find the grade for a given the examination score, the SQL needs to compare the EXTENT values across *two* rows. Comparing a column’s value across more than one row can be tricky to implement – unless you turn to Oracle functions LEAD/LAG.

From a series of rows returned from a query and a position of the cursor,

  • LEAD lets lets you access a row at a given offset after that position
  • LAG lets you access a row at a given offset before that position

LEAD/LAG Syntax

LAG (value_expression [,offset] [,default]) OVER ([query_partition_clause] order_by_clause)
LEAD (value_expression [,offset] [,default]) OVER ([query_partition_clause] order_by_clause)

If the offset is unspecified, it is assumed to be 1. If the offset goes beyond the scope of the window, the default value gets used.

Place a Value between extents with LEAD/LAG: Solution Approach

To find the position of a value within extents, the trick is to:

  1. sort the extents
  2. transpose consecutive extents  across two rows in the form of range_low and range_high in a single row
  3. get the row where value is BETWEEN range_low and range_high

Achieving 1 and 2 as above, using LAG:

select grade_code
   -- Set default to 0 for
   -- range_low of first row
   , lag(score_upto,1,-1)
     over (order by score_upto) + 1
     score_range_low 
   , score_upto score_range_high
from grade_master;

Place a value between extents with LEAD/LAG: Runtime Output

SQL> select grade_code
  2     -- Set default to 0 for
  3     -- range_low of first row
  4     , lag(score_upto,1,-1)
  5       over (order by score_upto) + 1
  6       score_range_low
  7     , score_upto score_range_high
  8  from grade_master;

GR SCORE_RANGE_LOW SCORE_RANGE_HIGH
-- --------------- ----------------
F                0               59
D               60               69
C               70               79
B               80               89
A               90              100

Applying 3, after pushing 1 and 2 into an inline view:

SQL> select grade_code
  2  from (
  3    select grade_code
  4       , lag(score_upto,1,-1)
  5         over (order by score_upto) + 1
  6         score_range_low
  7       , score_upto score_range_high
  8    from grade_master
  9       )
 10  where 34 -- input score, b/w 0 and 59
 11  between score_range_low and score_range_high;

GR
--
F

SQL> select grade_code
  2  from (
  3    select grade_code
  4       , lag(score_upto,1,-1)
  5         over (order by score_upto) + 1
  6         score_range_low
  7       , score_upto score_range_high
  8    from grade_master
  9       )
 10  where 69 -- input score, b/w 60 and 69
 11  between score_range_low and score_range_high;

GR
--
D

SQL> select grade_code
  2  from (
  3    select grade_code
  4       , lag(score_upto,1,-1)
  5         over (order by score_upto) + 1
  6         score_range_low
  7       , score_upto score_range_high
  8    from grade_master
  9       )
 10  where 85 -- input score, b/w 80 and 89
 11  between score_range_low and score_range_high;

GR
--
B

Summary

This article shows you how analytic functions LEAD and LAG can be used to access a row in a table given an offset value from another row, without needing a self-join.

This feature can be used to find the position of a value within extents, where the extents are defined by the upper bound only and the lower bound is derived via comparison to the previous/next row.

The example of finding the grade for an exam score shows one possible implementation of LEAD and LAG.

Leave a Comment

Previous post:

Next post: