MODEL Clause

Missing Cells - Right Side

In this tutorial we continue exploring the RULES part of the MODEL clause with a focus on cell references for missing cells on the right hand side of a rule.

By default, if a cell reference on the right hand side of a rule targets a cell that does not exist, the reference evaluates to NULL. In the following snippet dimension "0" does not exist in the result set.

set null "(null)"

select
  key, num_measure, text_measure, date_measure
from
  t
where
  key = 1
model
  dimension by ( key )
  measures
  (
    num_val ,
    group_1 ,
    date_val ,
    123              as num_measure  ,
    'abcdefghijklmn' as text_measure ,
    sysdate          as date_measure
  )
  rules upsert
  (
    num_measure [1] = num_val [0] ,
    text_measure[1] = group_1 [0] ,
    date_measure[1] = date_val[0]
  )
;
 
   KEY NUM_MEASURE TEXT_MEASURE   DATE_MEASU
------ ----------- -------------- ----------
     1 (null)      (null)         (null)
 

Note that missing cell references are relative to the query results, not the base table. For example, in the following snippets dimension reference [2] points to an existing cell in the first snippet and a missing cell in the second.

select
  key, num_measure, text_measure, date_measure
from
  t
where
  KEY = 2
model
  dimension by ( key )
  measures
  (
    num_val ,
    group_1 ,
    date_val ,
    123              as num_measure  ,
    'abcdefghijklmn' as text_measure ,
    sysdate          as date_measure
  )
  rules upsert
  (
    num_measure [any] = num_val [2] ,
    text_measure[any] = group_1 [2] ,
    date_measure[any] = date_val[2]
  )
;
 
   KEY NUM_MEASURE TEXT_MEASURE   DATE_MEASU
------ ----------- -------------- ----------
     2         200 A              2005-06-12
 
select
  key, num_measure, text_measure, date_measure
from
  t
where
  KEY = 1  -- row with KEY = '2' is not included this time
model
  dimension by ( key )
  measures
  (
    num_val ,
    group_1 ,
    date_val ,
    123              as num_measure  ,
    'abcdefghijklmn' as text_measure ,
    sysdate          as date_measure
  )
  rules upsert
  (
    num_measure [any] = num_val [2] ,
    text_measure[any] = group_1 [2] ,
    date_measure[any] = date_val[2]
  )
;
 
   KEY NUM_MEASURE TEXT_MEASURE   DATE_MEASU
------ ----------- -------------- ----------
     1 (null)      (null)         (null)
 

IGNORE NAV

You can control missing cell behaviour with the IGNORE NAV option which causes Oracle to return

  • 0 for missing numeric data
  • an empty string for missing character/string data
  • 01-JAN-2000 for missing date data
  • NULL for all other data types.
alter session set nls_date_format = 'YYYY-MM-DD';

select
  key, num_measure, text_measure, date_measure
from
  t
where
  key = 1
model
  IGNORE NAV
  dimension by ( key )
  measures
  (
    num_val ,
    group_1 ,
    date_val ,
    123          as num_measure  ,
    'abcdefghij' as text_measure ,
    sysdate      as date_measure
  )
  rules upsert
  ( num_measure [1] = num_val [0] ,
    text_measure[1] = group_1 [0] ,
    date_measure[1] = date_val[0]
  )
;
 
   KEY NUM_MEASURE TEXT_MEASU DATE_MEASU
------ ----------- ---------- ----------
     1           0 (null)     2000-01-01
 

Note that in Oracle® Database Data Warehousing Guide 10g Release 2 (10.2) - Chapter 22 - Treatment of NULLs and Missing Cells it incorrectly states that "01-JAN-2001" is returned for missing data data. The correct value, "01-JAN-2000", is documented in Oracle® Database SQL Reference 10g Release 2 (10.2) - SELECT - model_clause.

IS PRESENT, PRESENTV, and PRESENTNNV

If you need to distinguish between NULL values from your data and NULL values representing missing cells you can use the IS PRESENT condition or the PRESENTV and PRESENTNNV functions on the right hand side of a rule.

Here is an example of their use. Note that cell references with a dimension of "0" point to cells that were missing from the result set before the MODEL clause was executed.

select
  key, num_val, num_val_is_present, presentv_num_val, presentnnv_num_val
from
  t
where
  key in ( 1, 4 )
model
  dimension by ( key )
  measures
  (
    num_val ,
    cast ( 'default' as varchar2(20) ) as num_val_is_present ,
    cast ( 'default' as varchar2(20) ) as presentv_num_val   ,
    cast ( 'default' as varchar2(20) ) as presentnnv_num_val
  )
  rules upsert
  (
    num_val_is_present[0] =
      case when num_val[cv()] IS PRESENT then 'TRUE' else 'FALSE' end ,
    num_val_is_present[1] =
      case when num_val[cv()] IS PRESENT then 'TRUE' else 'FALSE' end ,
    num_val_is_present[4] =
      case when num_val[cv()] IS PRESENT then 'TRUE' else 'FALSE' end ,
    --
    presentv_num_val  [0] = PRESENTV  (num_val[cv()], 'expr 1', 'expr 2'),
    presentv_num_val  [1] = PRESENTV  (num_val[cv()], 'expr 1', 'expr 2'),
    presentv_num_val  [4] = PRESENTV  (num_val[cv()], 'expr 1', 'expr 2'),
    --
    presentnnv_num_val[0] = PRESENTNNV(num_val[cv()], 'expr 1', 'expr 2'),
    presentnnv_num_val[1] = PRESENTNNV(num_val[cv()], 'expr 1', 'expr 2'),
    presentnnv_num_val[4] = PRESENTNNV(num_val[cv()], 'expr 1', 'expr 2')
  )
order by
  key
;
 
   KEY NUM_VAL NUM_VAL_IS_PRESENT   PRESENTV_NUM_VAL     PRESENTNNV_NUM_VAL
------ ------- -------------------- -------------------- --------------------
     0 (null)  FALSE                expr 2               expr 2
     1     100 TRUE                 expr 1               expr 1
     4 (null)  TRUE                 expr 1               expr 2
 

It is important to note that these three functions consider a cell missing if it does not exist before the MODEL clause is executed. They will return the same results as above even when the cell they reference is created, via a rule, before the functions are called. The following variation of the last snippet demonstrates this.

select
  key, num_val, num_val_is_present, presentv_num_val, presentnnv_num_val
from
  t
where
  key in ( 1, 4 )
model
  dimension by ( key )
  measures
  (
    num_val ,
    cast ( 'default' as varchar2(20) ) as num_val_is_present ,
    cast ( 'default' as varchar2(20) ) as presentv_num_val   ,
    cast ( 'default' as varchar2(20) ) as presentnnv_num_val
  )
  rules upsert
  (
    num_val[0] = 0 , --(1)
    --
    num_val_is_present[0] =
      case when num_val[cv()] IS PRESENT then 'TRUE' else 'FALSE' end , --(2)
    num_val_is_present[1] =
      case when num_val[cv()] IS PRESENT then 'TRUE' else 'FALSE' end ,
    num_val_is_present[4] =
      case when num_val[cv()] IS PRESENT then 'TRUE' else 'FALSE' end ,
    --
    presentv_num_val  [0] = PRESENTV  (num_val[cv()], 'expr 1', 'expr 2'), --(3)
    presentv_num_val  [1] = PRESENTV  (num_val[cv()], 'expr 1', 'expr 2'),
    presentv_num_val  [4] = PRESENTV  (num_val[cv()], 'expr 1', 'expr 2'),
    --
    presentnnv_num_val[0] = PRESENTNNV(num_val[cv()], 'expr 1', 'expr 2'), --(4)
    presentnnv_num_val[1] = PRESENTNNV(num_val[cv()], 'expr 1', 'expr 2'),
    presentnnv_num_val[4] = PRESENTNNV(num_val[cv()], 'expr 1', 'expr 2')
  )
order by
  key
--
-- (1) num_val[0] is created
-- (2) still yields FALSE
-- (3) still yields 'expr 2'
-- (4) still yields 'expr 2'
;
 
   KEY NUM_VAL NUM_VAL_IS_PRESENT   PRESENTV_NUM_VAL     PRESENTNNV_NUM_VAL
------ ------- -------------------- -------------------- --------------------
     0       0 FALSE                expr 2               expr 2
     1     100 TRUE                 expr 1               expr 1
     4 (null)  TRUE                 expr 1               expr 2
 

Other SQL functions, such as PRESENTNNV's close cousin NVL2, operate on cells as they appear at the time the function is called. The following two snippets demonstrate the difference between PRESENTNNV and NVL2. 1

In this first snippet the results are the same for both functions.

select
  key, num_val, presentnnv_num_val, nvl2_num_val
from
  t
where
  key in ( 1, 4 )
model
  dimension by ( key )
  measures
  (
    num_val ,
    cast ( 'default' as varchar2(20) ) as num_val_is_present ,
    cast ( 'default' as varchar2(20) ) as presentv_num_val   ,
    cast ( 'default' as varchar2(20) ) as presentnnv_num_val ,
    cast ( 'default' as varchar2(20) ) as nvl2_num_val
  )
  rules upsert
  (
    presentnnv_num_val[0] = PRESENTNNV(num_val[cv()], 'expr 1', 'expr 2'),
    presentnnv_num_val[1] = PRESENTNNV(num_val[cv()], 'expr 1', 'expr 2'),
    presentnnv_num_val[4] = PRESENTNNV(num_val[cv()], 'expr 1', 'expr 2'),
    --
    nvl2_num_val      [0] = NVL2      (num_val[cv()], 'expr 1', 'expr 2'),
    nvl2_num_val      [1] = NVL2      (num_val[cv()], 'expr 1', 'expr 2'),
    nvl2_num_val      [4] = NVL2      (num_val[cv()], 'expr 1', 'expr 2')
  )
order by
  key
;
 
   KEY NUM_VAL PRESENTNNV_NUM_VAL   NVL2_NUM_VAL
------ ------- -------------------- --------------------
     0 (null)  expr 2               expr 2
     1     100 expr 1               expr 1
     4 (null)  expr 2               expr 2
 

In this next snippet the first rule creates cell "NUM_VAL[0]". The results for NVL2 now differ from those of PRESENTNNV.

select
  key, num_val, presentnnv_num_val, nvl2_num_val
from
  t
where
  key in ( 1, 4 )
model
  dimension by ( key )
  measures
  (
    num_val ,
    cast ( 'default' as varchar2(20) ) as presentnnv_num_val ,
    cast ( 'default' as varchar2(20) ) as nvl2_num_val
  )
  rules upsert
  (
    num_val[0] = 0 , --(1)
    --
    presentnnv_num_val[0] = PRESENTNNV(num_val[cv()], 'expr 1', 'expr 2'), --(2)
    presentnnv_num_val[1] = PRESENTNNV(num_val[cv()], 'expr 1', 'expr 2'),
    presentnnv_num_val[4] = PRESENTNNV(num_val[cv()], 'expr 1', 'expr 2'),
    --
    nvl2_num_val      [0] = NVL2      (num_val[cv()], 'expr 1', 'expr 2'), --(3)
    nvl2_num_val      [1] = NVL2      (num_val[cv()], 'expr 1', 'expr 2'),
    nvl2_num_val      [4] = NVL2      (num_val[cv()], 'expr 1', 'expr 2')
  )
order by
  key
--
-- (1) num_val[0] is created
-- (2) still yields 'expr 2'
-- (3) yields 'expr 1' this time
;
 
   KEY NUM_VAL PRESENTNNV_NUM_VAL   NVL2_NUM_VAL
------ ------- -------------------- --------------------
     0       0 expr 2               expr 1
     1     100 expr 1               expr 1
     4 (null)  expr 2               expr 2
 

Acknowledgements

  1. Re: PRESENTNNV vs NVL2 by OTN user martyn



Linking to SQL Snippets ™

To link to this page in Oracle Technology Network Forums or OraFAQ Forums cut and paste this code.

  • [url=http://www.sqlsnippets.com/en/topic-11676.html]SQL Snippets: MODEL Clause - Missing Cells - Right Side[/url]

To link to this page in HTML documents or Blogger comments cut and paste this code.

  • <a href="http://www.sqlsnippets.com/en/topic-11676.html">SQL Snippets: MODEL Clause - Missing Cells - Right Side</a>

To link to this page in other web sites use the following values.

  • Link Text : SQL Snippets: MODEL Clause - Missing Cells - Right Side
  • URL (href): http://www.sqlsnippets.com/en/topic-11676.html