This section presents tutorials on the MODEL clause of the SELECT command. Introduced in Oracle 10g, the MODEL clause is a powerful feature that gives you the ability to change any cell in the query’s result set using data from any other cell (similar to the way a spreadsheet works). It also adds procedural features to SQL previously available only through PL/SQL calls.
For example, with MODEL you can take a simple table like this
1 2 3 4 5 6 7 8 9 10 11 12
KEY GROUP_1 GROUP_2 DATE_VAL NUM_VAL ------ ---------- ---------- ---------- ------- 1 A a1 2005-01-01 100 2 A a2 2005-06-12 200 3 A a3 300 4 B a1 2006-02-01 5 B a2 2006-06-12 300 6 B a3 2005-01-01 100 7 C a1 2006-06-12 100 8 C a2 9 a1 2005-02-01 200 10 a2 2005-02-01 800
and, with a single command, create a report containing ad-hoc totals like this.
select casewhen key like'Total%'then key elsenullendas total , group_1 , group_2 , num_val from t model dimension by ( cast(key as varchar2(20)) as key , nvl( group_1, 'n/a' ) as group_1 , nvl( group_2, 'n/a' ) as group_2 ) measures( num_val ) rules ( num_val[ 'Total 1 - A + C', null, null ] = sum(num_val)[any,group_1 in ('A','C'),any] , num_val[ 'Total 2 - A + a2', null, null ] = sum(num_val)[any,'A',any] + sum(num_val)[any,group_1 <>'A','a2'] , num_val[ 'Total 3 - n/a', null, null ] = sum(num_val)[any,'n/a',any] , num_val[ 'Total 4 - a1 + a3', null, null ] = sum(num_val)[any,any,group_2 in ('a1','a3')] ) orderby group_1 , group_2 , total nulls first ;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
TOTAL GROUP_1 GROUP_2 NUM_VAL -------------------- ---------- ---------- ------- A a1 100 A a2 200 A a3 300 B a1 B a2 300 B a3 100 C a1 100 C a2 n/a a1 200 n/a a2 800 Total 1 - A + C 700 Total 2 - A + a2 1700 Total 3 - n/a 1000 Total 4 - a1 + a3 800
You can also use MODEL’S procedural features to produce results that are difficult, inefficient, or impossible to do with a non-MODEL SELECT command. Here is an example.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
setnull "(null)" column string format a40
select group_1, substr( string, 2 ) as string from t where num_val isnotnull model return updated rows partitionby ( group_1 ) dimension by ( row_number() over (partitionby group_1 orderby num_val) as position ) measures ( cast( num_val as varchar2(65) ) as string ) -- Note 1 rules upsert iterate( 6 ) until ( presentv(string[iteration_number+2],1,0) =0 ) ( string[0] = string[0] ||','|| string[iteration_number+1] ) orderby group_1 ;
1 2 3 4 5 6
GROUP_1 STRING ---------- ---------------------------------------- A 100,200,300 B 100,300 C 100 (null) 200,800
(This last technique is explained fully in another section of SQL Snippets at Rows to String: MODEL Method 1.)
Though powerful, the MODEL clause is also somewhat complex and this can be intimidating when you read about it for the first time. The tutorials to follow will therefore present very simple MODEL examples to help you quickly become comfortable with its many features.
Before continuing it is important to know that everything in the MODEL clause is evaluated after all other clauses in the query, except for SELECT DISTINCT and ORDER BY. Knowing this will help you better understand the examples in this section’s tutorials.
In this tutorial we learn about the DIMENSION BY component of the MODEL clause.
DIMENSION BY specifies which columns in a SELECT statement are dimension columns, which for our purposes can be thought of as any column that serves to identify each row in the result of a SELECT statement. By default, the dimension columns in a MODEL clause must produce a unique key for the result set. See the Oracle® Database Data Warehousing Guide 10g Release 2 (10.2) - Glossary for a formal definition.
Before we begin please note that, on its own, DIMENSION BY has little visible effect on the output of the SELECT statement. Most of the examples below would produce the same result as one with no MODEL clause at all. This is because we are not trying to manipulate the results just yet. We are simply seeing how to specify our dimension columns, which is a precursor for learning to manipulate results in subsequent pages.
Consider the following table.
1 2 3 4 5 6 7 8 9 10 11
select key , key_2 , group_1 , group_2 , num_val from t orderby key ;
1 2 3 4 5 6 7 8 9 10 11 12
KEY KEY_2 GROUP_1 GROUP_2 NUM_VAL ------ ----- ---------- ---------- ------- 1 T-1 A a1 100 2 T-2 A a2 200 3 T-3 A a3 300 4 T-4 B a1 5 T-5 B a2 300 6 T-6 B a3 100 7 T-7 C a1 100 8 T-8 C a2 9 T-9 a1 200 10 T-10 a2 800
We see that KEY, KEY_2, and (GROUP_1, GROUP_2) all uniquely identify each row in the table. They are therefore dimension column candidates. To let Oracle know which column(s) we plan to use as dimensions we compose a MODEL clause like this. (Ignore the MEASURES and RULES clauses for now. We will explore those later.)
1 2 3 4 5 6 7 8 9 10 11 12
select key , num_val from t model DIMENSION BY ( KEY ) measures ( num_val ) rules () orderby key ;
select KEY AS KEY_3 , num_val from t model dimension by ( KEY_3 ) measures ( num_val ) rules () ; dimension by ( KEY_3 ) * ERROR at line 7: ORA-00904: "KEY_3": invalid identifier
select KEY *10AS KEY_3 , num_val from t model dimension by ( KEY_3 ) measures ( num_val ) rules () ; dimension by ( KEY_3 ) * ERROR at line 7: ORA-00904: "KEY_3": invalid identifier
select ROWNUM AS KEY_3 , num_val from t model dimension by ( KEY_3 ) measures ( num_val ) rules () ; dimension by ( KEY_3 ) * ERROR at line 7: ORA-00904: "KEY_3": invalid identifier
You can however alias such expressions directly in DIMENSION BY.
1 2 3 4 5 6 7 8 9 10 11 12
select KEY_3, num_val from t model DIMENSION BY ( KEY AS KEY_3 ) measures ( num_val ) rules () orderby key_3 ;
By default, if your DIMENSION BY columns do not give you a unique key for your result set you will get an error.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
select group_2 , num_val from t model DIMENSION BY ( GROUP_2 ) -- group_2 is not unique measures ( num_val ) rules () orderby group_2 ; t * ERROR at line 5: ORA-32638: Non unique addressing in MODEL dimensions
This rule can be relaxed somewhat by specifying UNIQUE SINGLE REFERENCE.
1 2 3 4 5 6 7 8 9 10 11 12
select group_2 , num_val from t model UNIQUE SINGLE REFERENCE dimension by ( group_2 ) -- group_2 is not unique measures ( num_val ) rules () orderby group_2 ;
In this tutorial we learn about the MEASURES component of the MODEL clause.
MEASURES specifies which columns in a SELECT are measure columns, which for our purposes can be thought of as any column containing a measurable quantity like a price or a length. See the Oracle® Database Data Warehousing Guide 10g Release 2 (10.2) - Glossary for a formal definition.
Before we begin please note that, on its own, MEASURES has little visible effect on the output of the SELECT statement. Most of the examples below would produce the same result as one with no MODEL clause at all. This is because we are not trying to manipulate the results just yet. We are simply seeing how to specify our measure columns, which is a precursor to manipulating the results. We will see how to actually manipulate our output when we explore the RULES clause in subsequent tutorials.
Before we see MEASURES in action first consider the following table.
1 2 3 4 5 6 7 8 9 10 11
select key , group_1 , group_2 , date_val , num_val from t orderby key ;
1 2 3 4 5 6 7 8 9 10 11 12
KEY GROUP_1 GROUP_2 DATE_VAL NUM_VAL ------ ---------- ---------- ---------- ------- 1 A a1 2005-01-01 100 2 A a2 2005-06-12 200 3 A a3 300 4 B a1 2006-02-01 5 B a2 2006-06-12 300 6 B a3 2005-01-01 100 7 C a1 2006-06-12 100 8 C a2 9 a1 2005-02-01 200 10 a2 2005-02-01 800
If we decide to use KEY as our sole dimension column, then all other columns are available for use as measure columns. To let Oracle know we want to use the NUM_VAL column as our measure we can compose a MODEL clause like this.
1 2 3 4 5 6 7 8 9 10 11 12
select key , num_val from t model dimension by ( key ) MEASURES ( NUM_VAL ) rules () orderby key ;
In this tutorial we examine restrictions related to both the DIMENSION BY and MEASURES components of the MODEL clause.
If you try to include columns in your SELECT clause which are not DIMENSION or MEASURE columns you will get an error.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
select key , GROUP_1 , -- exists in table T, but it is not a DIMENSION or MEASURE column num_val from t model dimension by ( key ) measures ( num_val ) rules () orderby key ; GROUP_1 , -- exists in table T, but it is not a DIMENSION or MEASURE column * ERROR at line 3: ORA-32614: illegal MODEL SELECT expression
If you try to use the same column for both a dimension and a measure you will get an error.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
select key , num_val from t model DIMENSION BY ( KEY ) MEASURES ( KEY, num_val ) rules () orderby key ; key , * ERROR at line 2: ORA-00957: duplicate column name
The constants NULL and ‘’ (empty string), by themselves, are not allowed in DIMENSION BY or MEASURES clauses.
Pay attention to implicit data type conversions. In the following example NEW_TEXT_MEASURE is implicitly assigned a datatype with a length of 13 characters, the length of the constant used in the MEASURES clause. Attempting to assign a value that is longer than 13 characters raises a ORA-25137 error.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
select key , new_text_measure from t where key =1 model dimension by( key ) measures ( '13 CHARACTERS'as new_text_measure ) rules ( new_text_measure[any] ='OVER 13 CHARACTERS' ) ; measures ( '13 CHARACTERS'as new_text_measure ) * ERROR at line 10: ORA-25137: Data valueoutofrange
Despite the error message’s phrasing, it is really the value “OVER 13 CHARACTERS” which is out of range, not “13 CHARACTERS”.
To avoid the error, use CAST to force a datatype long enough to contain the largest value in the result set.
1 2 3 4 5 6 7 8 9 10 11 12
select key , new_text_measure from t where key =1 model dimension by( key ) measures ( CAST( '13 CHARACTERS'as varchar2(4000) ) as new_text_measure ) rules ( new_text_measure[any] ='OVER 13 CHARACTERS' ) ;
1 2 3
KEY NEW_TEXT_MEASURE ------ -------------------- 1 OVER 13 CHARACTERS
The term “M_1[1]” is called a “cell reference” and it refers to the M_1 column value where the KEY column value is “1” in the SELECT command’s output. Cell references can only refer to measure cells, not dimension cells. When a cell reference is used as the assignment target on the left side of a rule equation it is called a "cell assignment". Note that the term “cell assignment” refers only to the term on the left hand side of the rule equation, not the entire equation.
On the right hand side of a rule cell references do not have a special name. To avoid confusion, we will always refer to the cell(s) targeted by a rule only as a “cell assignment” and we will use “cell reference” in all other cases.
The part between the square brackets, “[]”, is called a “dimension reference”. When a dimension reference appears in a cell assignment it can be a condition, expression, or loop construct (described in subsequent tutorials) targeting one or more dimension values. Dimension references for all other cell references can only contain conditions or expressions.
To create a cell assignment targeting all cells in a measure column use the ANY keyword in the dimension reference.
1 2 3 4 5 6 7 8 9 10 11 12 13
select key , num_val , m_1 from t model dimension by ( key ) measures ( num_val, 0as m_1 ) rules ( m_1[ ANY ] =100 ) orderby key ;
You can nest a cell reference within a cell assignment if the nested cell reference only identifies a single cell. Other restrictions on nested cell references are documented at Rules and Restrictions when Using SQL for Modeling.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
column key_equals_num_val_2 format a16
select key , num_val , key_equals_num_val_2 from t model dimension by ( key ) measures ( num_val, 'N'as key_equals_num_val_2 ) rules ( key_equals_num_val_2[ NUM_VAL[2]/100 ] ='Y' ) orderby key ;
1 2 3 4 5 6 7 8 9 10 11 12
KEY NUM_VAL KEY_EQUALS_NUM_V ------ ------- ---------------- 1 100 N 2 200 Y 3 300 N 4 N 5 300 N 6 100 N 7 100 N 8 N 9 200 N 10 800 N
A cell whose dimension is null will be targeted by any of the following styles of dimension reference.
[any]
[group_1 is any]
[null]
[group_1 is null]
A reference like “[group_1 = null]” will not work.
GROUP_1 USING_ANY USING_IS_ANY USING_NULL USING_IS_NULL USING_EQUALS_NULL ---------- --------- ------------ ---------- ------------- ----------------- A Y Y N N N Y Y Y Y N
Attempting to modify a dimension column with a rule will generate an error. The left side of a rule can only reference measure cells.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
select key , num_val from t model dimension by ( key ) measures ( num_val ) rules ( KEY[1] =0 ) orderby key ; rules ( KEY[1] =0 ) * ERROR at line 9: ORA-00904: : invalid identifier
Specifying too few dimension values in a cell reference results in an ORA-00947 error.
1 2 3 4 5 6 7 8 9 10 11 12 13 14
select key , m_1 from t model dimension by ( group_1, group_2 ) measures ( 0as m_1 ) rules ( m_1['A'] =100 ) ; rules ( m_1['A'] =100 ) * ERROR at line 9: ORA-00947: not enough values
Specifying too many dimension values in a cell reference results in the same ORA-00947 error as the last example. In this case the error text “not enough values” is misleading.
1 2 3 4 5 6 7 8 9 10 11 12 13 14
select key , m_1 from t model dimension by ( key ) measures ( 0as m_1 ) rules ( m_1[1,2] =100 ) ; rules ( m_1[1,2] =100 ) * ERROR at line 9: ORA-00947: not enough values
Including a dimension column on the right side of a dimension reference expression will produce an error.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
select group_1 , group_2 , m_1 from t model dimension by ( group_1, group_2 ) measures ( 'N'as m_1 ) rules ( m_1[ any, group_2 > GROUP_1 ] ='Y' ) orderby group_1 ; rules ( m_1[ any, group_2 > GROUP_1 ] ='Y' ) * ERROR at line 10: ORA-32625: illegal dimension in cell reference predicate
A nested cell reference that targets more than one cell will produce an error.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
select key , num_val , m_1 from t model dimension by ( key ) measures ( num_val, 0as m_1 ) rules ( m_1[ NUM_VAL[KEY IN (1,2,3)] ] =1 ) orderby key ; select * ERROR at line 1: ORA-32622: illegal multi-cell reference
In this tutorial we continue exploring the RULES part of the MODEL clause with a focus on FOR loops. FOR loops allow you to take a MODEL clause like this
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
select key , flag from t model dimension by ( key ) measures ( '.'as flag ) rules ( flag[2] ='Y' , flag[4] ='Y' , flag[6] ='Y' ) orderby key ;
1 2 3 4 5 6 7 8 9 10 11 12
KEY FLAG ------ ---- 1 . 2 Y 3 . 4 Y 5 . 6 Y 7 . 8 . 9 . 10 .
and rewrite it like this …
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
select key , flag from t model dimension by ( key ) measures ( '.'as flag ) rules ( flag[ FOR KEY IN ( 2, 4, 6 ) ] ='Y' ) orderby key ;
1 2 3 4 5 6 7 8 9 10 11 12
KEY FLAG ------ ---- 1 . 2 Y 3 . 4 Y 5 . 6 Y 7 . 8 . 9 . 10 .
or this
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
select key , flag from t model dimension by ( key ) measures ( '.'as flag ) rules ( flag[ FOR KEY FROM2TO6 INCREMENT 2 ] ='Y' ) orderby key ;
1 2 3 4 5 6 7 8 9 10 11 12
KEY FLAG ------ ---- 1 . 2 Y 3 . 4 Y 5 . 6 Y 7 . 8 . 9 . 10 .
select key , flag from t model dimension by ( key ) measures ( '.' as flag ) rules ( flag[ FOR KEY IN ( SELECT KEY FROM T2 ) ] = 'Y' ) order by key ;
1 2 3 4 5 6 7 8 9 10 11 12
KEY FLAG ------ ---- 1 . 2 Y 3 . 4 Y 5 . 6 Y 7 . 8 . 9 . 10 .
With “FOR dimension LIKE …” syntax you can combine strings with incrementing/decrementing values like this.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
select key_2 , flag from t model dimension by ( key_2 ) measures ( '.'as flag ) rules ( flag[ FOR KEY_2 LIKE'T-%'FROM2TO6 INCREMENT 2 ] ='Y' ) orderby key_2 ;
1 2 3 4 5 6 7 8 9 10 11 12
KEY_2 FLAG ----- ---- T-1 . T-10 . T-2 Y T-3 . T-4 Y T-5 . T-6 Y T-7 . T-8 . T-9 .
select group_1 , group_2 , num_val , flag from t model dimension by ( group_1, group_2 ) measures ( num_val, '.'as flag ) rules ( flag [ FOR ( GROUP_1, GROUP_2 ) IN ( SELECT GROUP_1, GROUP_2 FROM T WHERE NUM_VAL ISNULL ) ] ='Y' ) orderby group_1 , group_2 ;
1 2 3 4 5 6 7 8 9 10 11 12
GROUP_1 GROUP_2 NUM_VAL FLAG ---------- ---------- ------- ---- A a1 100 . A a2 200 . A a3 300 . B a1 Y B a2 300 . B a3 100 . C a1 100 . C a2 Y a1 200 . a2 800 .
It is important to note that a dimension reference with a FOR loop like FOR KEY IN ( 2, 4, 6 ) differs from one with an IN expression like KEY IN ( 2, 4, 6 ) . This is explained further in the Missing Cells - UPSERT tutorial.
In this tutorial we continue exploring the RULES part of the MODEL clause. We will focus on cell references in the right hand side of rule equations. Subsequent tutorials will cover other aspects of rules.
The term “NUM_VAL[1]” in the last example is called a “cell reference”. Cell references can only refer to measure cells and they must only refer to a single measure cell when used on the right hand side of a rule.
The part between the square brackets in a cell reference is called a dimension reference and it can only contain literal values (e.g. “1”), expressions (e.g. “1+1”), or conditions (e.g. “KEY=3”) that resolve to a single dimension value. Here are some examples.
If required, you can nest one cell reference within another as long as the nested cell reference only identifies a single cell. Other restrictions on nested cell references are documented at Rules and Restrictions when Using SQL for Modeling.
1 2 3 4 5 6 7 8 9 10 11 12 13
select key , num_val , m_1 from t model dimension by ( key ) measures ( num_val, 0as m_1 ) rules ( m_1[ NUM_VAL[1]/100 ] =10 ) orderby key ;
As described in DIMENSION BY the UNIQUE SINGLE REFERENCE feature allows you to have duplicate domain values in your result set. You can only use this feature as long as you do not include any multi-cell references on the right hand side of your query rules. For example, this is o.k.
1 2 3 4 5 6 7 8 9 10 11 12 13
select group_2 , num_val , m_1 from t model UNIQUE SINGLE REFERENCE dimension by ( group_2 ) measures ( num_val, 0as m_1 ) rules ( m_1[any] =10 ) orderby group_2 ;
select group_2 , num_val , m_1 from t model UNIQUE SINGLE REFERENCE dimension by ( group_2 ) measures ( num_val, 0as m_1 ) rules ( m_1[any] = NUM_VAL['a2'] *10 ) ; t * ERROR at line 6: ORA-32638: Non unique addressing in MODEL dimensions
and neither is this.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
select group_2 , num_val , m_1 from t model UNIQUE SINGLE REFERENCE dimension by ( group_2 ) measures ( num_val, 0as m_1 ) rules ( m_1[any] = NUM_VAL[CV()] *10 ) ; t * ERROR at line 6: ORA-32638: Non unique addressing in MODEL dimensions
You cannot reference columns in rules. In the following rule the column NUM_VAL is referenced illegally. Use a cell reference like “NUM_VAL[3]” instead.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
select key , num_val , m_1 from t model dimension by ( key ) measures ( num_val, 0as m_1 ) rules ( m_1[3] = NUM_VAL *10 ) orderby key ; rules ( m_1[3] = NUM_VAL *10 ) * ERROR at line 10: ORA-00984: columnnot allowed here
Including a cell reference to a dimension column will produce an error. Only measure columns are allowed in cell references.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
select key , num_val , key_x_10 from t model dimension by ( key ) measures ( num_val, 0as key_x_10 ) rules ( key_x_10[3] = KEY[3] *10 ) orderby key ; rules ( key_x_10[3] = KEY[3] *10 ) * ERROR at line 10: ORA-00904: : invalid identifier
Multi-cell references, like NUM_VAL[ANY], are not allowed on the right hand side of a rule.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
select key , num_val , m_1 from t model dimension by ( key ) measures ( num_val, 0as m_1 ) rules ( m_1[any] = NUM_VAL[ANY] ) ; rules ( m_1[any] = NUM_VAL[ANY] ) * ERROR at line 10: ORA-32622: illegal multi-cell reference
A nested cell reference that targets more than one cell will produce an error.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
select key , num_val , m_1 from t model dimension by ( key ) measures ( num_val, 0as m_1 ) rules ( m_1[ NUM_VAL[KEY IN (1,2,3)] /100 ] =1 ) orderby key ; select * ERROR at line 1: ORA-32622: illegal multi-cell reference
Even though a dimension expression resolves to a single value, it may still trigger an error.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
select key , num_val , m_1 from t model dimension by ( key ) measures ( num_val, 0as m_1 ) rules ( m_1[3] = num_val[KEY BETWEEN3AND3] *1000 ) orderby key ; select * ERROR at line 1: ORA-32622: illegal multi-cell reference
You may not use subqueries in rules, except in the FOR construct (described in FOR Loops).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
select key , dummy from t where key =1 model dimension by ( key ) measures ( 'Y'as dummy ) rules ( m_1[1] = ( SELECT DUMMY FROM DUAL ) ) ; rules ( m_1[1] = ( SELECT DUMMY FROM DUAL ) ) * ERROR at line 11: ORA-00904: : invalid identifier
Instead, define the subquery in the MEASURES list.
1 2 3 4 5 6 7 8 9 10 11 12
select key , dummy from t where key =1 model dimension by ( key ) measures ( ( SELECT DUMMY FROM DUAL ) as dummy ) rules ( ) ;
In this tutorial we continue exploring the RULES part of the MODEL clause. We will focus on the CV() function here. Subsequent tutorials will cover other aspects of rules.
If we need to access the value of a dimension cell, we cannot do it using a cell reference because cell references can only refer to measure columns. For example, we cannot use a cell reference like KEY[ CV() ] to access the value of KEY cells.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
select key , num_val , m_1 from t model dimension by ( key ) measures ( num_val, 0as m_1 ) rules ( m_1[any] = KEY[CV()] *10 ) orderby key ; rules ( m_1[any] = KEY[CV()] *10 ) * ERROR at line 10: ORA-00904: : invalid identifier
Fortunately the CV() function exists to helps us access the current value of any dimension cell. In the following example CV(KEY) returns the current value of the KEY dimension column.
1 2 3 4 5 6 7 8 9 10 11 12
select key , m_1 from t model dimension by ( key ) measures ( 0as m_1 ) rules ( m_1[any] = CV(KEY) *10 ) orderby key ;
You can use CV() without an argument in dimension references to return the current dimension value associated with its relative position within a cell reference. For example, in the following snippet the first occurrence of CV() refers to the current value of the GROUP_1 dimension and the second occurrence refers to the current value of the GROUP_2 dimension.
In this tutorial we explore the PARTITION component of the MODEL clause. The Oracle® Database Data Warehousing Guide 10g Release 2 (10.2) - Chapter 22 describes partition columns this way.
“Partition columns define the logical blocks of the result set in a way similar to the partitions of the analytical functions described in Chapter 21, “SQL for Analysis and Reporting”. Rules in the MODEL clause are applied to each partition independent of other partitions.”
“You can partition data and evaluate rules within each partition independent of other partitions. This enables parallelization of model computation based on partitions.”
If your system has multiple processors the PARTITION BY clause may improve your query’s performance (see Parallel Execution). For example, with a query like this
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
break on group_1 skip1 duplicates
select group_1 , group_2 , num_val , m_1 from t model dimension by ( group_1, group_2 ) measures ( num_val, 0 m_1 ) rules ( m_1[any, any] = num_val[cv(), cv()] *10 ) orderby group_1 , group_2 ;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
GROUP_1 GROUP_2 NUM_VAL M_1 ---------- ---------- ------- ---------- A a1 100 1000 A a2 200 2000 A a3 300 3000
B a1 B a2 300 3000 B a3 100 1000
C a1 100 1000 C a2
a1 200 2000 a2 800 8000
one CPU will most likely process the M_1 rule for all rows in the result set (note: I have not tested this behaviour myself; this information is assumed from the manuals). Using a query like the following produces the same output while allowing one processor on your system to process the rows where GROUP_1 is ‘A’, another CPU to process those where GROUP_1 is ‘B’, etc.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
select group_1 , group_2 , num_val , m_1 from t model PARTITIONBY ( GROUP_1 ) dimension by ( group_2 ) measures ( num_val, 0 m_1 ) rules ( m_1[any] = num_val[cv()] *10 ) -- note only one dimension now orderby group_1 , group_2 ;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
GROUP_1 GROUP_2 NUM_VAL M_1 ---------- ---------- ------- ---------- A a1 100 1000 A a2 200 2000 A a3 300 3000
B a1 B a2 300 3000 B a3 100 1000
C a1 100 1000 C a2
a1 200 2000 a2 800 8000
While PARTITION BY had no visible effect on the output in the last example, there are situations where it will have an effect. These are described next.
In the DIMENSION BY tutorial we saw that a DIMENSION BY clause whose columns do not uniquely identify each result row generate an error.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
select group_2 , num_val from t model dimension by ( group_2 ) -- group_2 is not unique measures ( num_val ) rules () orderby group_2 ; t * ERROR at line 5: ORA-32638: Non unique addressing in MODEL dimensions
With a partition clause this no longer happens, as long as the dimension values are unique within a partition.
1 2 3 4 5 6 7 8 9 10 11 12 13
select group_2 , num_val from t model PARTITIONBY ( GROUP_1 ) dimension by ( group_2 ) -- group_2 values are unique within group_1 values measures ( num_val ) rules () orderby group_2 ;
select group_1 , date_val , num_val from t model partitionby ( group_1 ) dimension by ( DATE_VAL ) -- there are duplicate date values in group_1 measures ( num_val ) rules () orderby group_1 , date_val ; t * ERROR at line 6: ORA-32638: Non unique addressing in MODEL dimensions
PARTITION BY also effects query results with aggregate and analytic functions. This effect is described in the Aggregate and Analytic Expressions tutorial.
Queries with a MODEL clause cannot specify aggregate or analytic functions in their SELECT or ORDER BY lists. For example, this attempt generates an error.
select group_1 , MIN( GROUP_2 ) as c_1 , MAX( GROUP_2 ) as c_2 , SUM( NUM_VAL ) as c_3 from t groupby group_1 model dimension by( group_1 ) measures ( group_2, num_val ) rules ( ) orderby group_1 ; measures ( group_2, num_val ) * ERROR at line 12: ORA-00979: not a GROUPBY expression
If needed, these functions can instead be specified in the PARTITION BY, DIMENSION BY, MEASURES, or RULES clauses. The following examples demonstrate their use in DIMENSION BY and MEASURES.
You can also use aggregate and analytic functions in the RULES clause, but it is not as straightforward as using them in DIMENSION BY and MEASURES is. See the tutorial Aggregate and Analytic Expressions for more information on this topic.
In this tutorial we continue exploring the RULES part of the MODEL clause with a focus on aggregate and analytic expressions. (See Model Expressions for full syntax.)
Aggregate and analytic functions can be used on the right hand side of a rule to process multiple cell values. They can not appear on the left hand side of rules. Both types of functions can take constants, bind variables, measure columns, or expressions involving them as arguments. Beyond that, aggregate functions can also specify a cell reference to restrict which cells they operate on. For example, in this snippet
1 2 3 4 5 6 7 8 9 10 11 12 13
select key , num_val , m_1 from t model dimension by ( key ) measures ( num_val, 0 m_1 ) rules ( m_1[3] =SUM( NUM_VAL )[ KEY IN (1,2,3) ] ) orderby key ;
the aggregate function SUM takes the measure column NUM_VAL as an argument. The dimension reference [ KEY IN (1,2,3) ] restricts the function to three specific cell values.
Here is an analytic example. Note that analytic functions are not qualified with dimension references.
1 2 3 4 5 6 7 8 9 10 11 12
select key , m_1 from t model dimension by ( key ) measures ( 0 m_1 ) rules ( m_1[any] =ROW_NUMBER() OVER ( ORDERBY KEY DESC ) ) orderby key ;
The following two snippets perform similar logic. One uses analytic functions and the other aggregate functions. By using the CV() and ANY dimension references with the aggregate functions we see that they can produce the same results analytic functions do.
To see how PARTITION BY affects aggregate functions, consider the following snippets. The one on the left does not use PARTITION BY. The one on the right does. As you can see, using PARTITION BY produces different values under M_1 than not using it.
To see how PARTITION BY affects analytic functions, consider the following snippets. The one on the left does not use PARTITION BY. The one on the right does. Again, using PARTITION BY produces different values under M_1 than not using it.
In this tutorial we continue exploring the RULES part of the MODEL clause with a focus on UPSERT behaviour with cell assignments for missing cells.
By default, if a cell assignment targets a cell that does not exist then UPSERT logic is used to process the results. With UPSERT logic Oracle may or may not add a new cell (plus the associated cells in the new cell’s row) to the result set when it encounters a nonexistent dimension value, depending on the style of dimension reference used.
If a dimension reference uses a constant expression (aka “positional references”) then existing cells are updated and missing cells are created. In the example below the cell assignment with the KEY value “5” points to an existing cell, so it updates the cell to “5”. The ones with KEY values “0” and “-1” point to nonexistent dimension values so new cells are added to the results.
Note that FOR loops like FOR KEY IN (-1,5) are considered positional references.
If the dimension reference uses a boolean expression (aka a “symbolic reference”) that evaluates to FALSE, i.e. the cell assignment points to a missing cell, then the rule is ignored. In this example the rule for M_1 is applied where KEY = 5 is TRUE. The rule for M_2 is never applied because KEY = 0 is always FALSE.
Note that, unlike FOR KEY IN (-1,5) , the dimension reference KEY IN (-1,5) is a symbolic reference.
UPSERT creates cells only when all the dimension references in a cell are either positional references to existing dimension values or symbolic references that evaluate to TRUE. No cells are created for any other combination. For example, the dimension references for the first and second rules below will not trigger new cells, those for the third and fourth will.
Given that WHERE clauses are evaluated before MODEL clauses and given that UPSERT may create new cells, be aware that WHERE clauses with FALSE results will not necessarily lead to empty result sets when MODEL is used. Here is a good example of a query some people might expect a “no rows selected” result from, but in fact returns two rows.
select key, key_2, m_1, m_2 from t where 1=2-- (1) no rows are returned from table T, initial result set is empty model dimension by ( key ) measures ( key_2 , cast( nullas number) m_1 , cast( nullas number) m_2 ) rules upsert ( M_1[ 0 ] =1 , -- (2) adds a new row to the result set M_2[ 5 ] =1-- (3) adds another new row to the result set ) orderby key ;
In this case, the row with a KEY value of 5 is a new row created by the rule for M2. It is **_not** related to the row in table T with a KEY value of ‘5’.
In this tutorial we continue exploring the RULES part of the MODEL clause with a focus on UPSERT ALL behaviour with cell assignments for missing cells.
UPSERT ALL behaviour is similar to UPSERT behaviour. If we specify UPSERT ALL for the first two examples used in the Missing Cells - UPSERT tutorial we see that the output for both UPSERT and UPSERT ALL is identical. Missing cells are created when a dimension reference is positional and they are ignored when it is symbolic.
The difference between UPSERT and UPSERT ALL becomes evident when there is a mix of symbolic and positional references. The Oracle® Database Data Warehousing Guide 10g Release 2 (10.2) - Chapter 22 - UPSERT ALL Behavior describes it this way
“UPSERT ALL behavior allows model rules with existential predicates (comparisons, IN, ANY, and so on) in their left side to have UPSERT behavior.”
According to the Wikipedia entry for Existentialism
“existentialism is often associated with anxiety, dread, awareness of death, and freedom.”
Fear not though, existential predicates in SQL are not as angst-ridden as their philosophical namesake. A few examples will clarify matters.
Here is the third example from the Missing Cells - UPSERT tutorial modified to use UPSERT ALL. Unlike with UPSERT, here we see that missing cells are created for M_2 even though it has a nonexistent positional reference.
GROUP_1 GROUP_2 KEY_2 M_1 M_2 M_3 M_4 ---------- ---------- ----- ---------- ---------- ---------- ---------- A a1 T-1 4 A a2 T-2 4 A a3 T-3 3 4 B a1 T-4 B a2 T-5 B a3 T-6 C a1 T-7 C a2 T-8 D a3 2 a1 T-9 a2 T-10
UPSERT ALL created a cell for the M_2 rule because there was at least one row in the initial result set with GROUP_2 = 'a3' , even though it was not paired with any GROUP_1 values of “D”.
Here are some more examples of existential predicates that match at least one row in the result set.
GROUP_1 GROUP_2 KEY_2 M_1 M_2 M_3 M_4 ---------- ---------- ----- ---------- ---------- ---------- ---------- A a1 T-1 A a2 T-2 A a3 T-3 B a1 T-4 B a2 T-5 B a3 T-6 C a1 T-7 C a2 T-8 D a2 1 D a3 1 E a3 2 F a3 3 G a1 4 G a2 4 G a3 4 a1 T-9 a2 T-10
In this tutorial we continue exploring the RULES part of the MODEL clause with a focus on UPDATE behaviour with cell assignments for missing cells.
Finally, we examine the last and simplest of the three available rule behaviours, UPDATE. Here are the three examples used in the Missing Cells - UPSERT tutorial modified to use UPDATE behaviour. As you can see, missing cells are never created for nonexistent dimension references like ‘-1’, ‘0’, ‘D’, and ‘a4’ when UPDATE is specified.
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.
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 = 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] ) ;
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.
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.
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.
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.
Unlike PREVIOUS(), which can only be used in the UNTIL clause, ITERATION_NUMBER can appear anywhere in a main model rule. In the following example ITERATION_NUMBER is used in the UNTIL clause, as a dimension reference, and in an expression on the right side of a rule.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
select key , m_1 from dual model dimension by ( 0as key ) measures ( 0as m_1 ) rules upsert iterate (10) until ( ITERATION_NUMBER =5 ) ( m_1[ITERATION_NUMBER] = ITERATION_NUMBER *10 ) orderby key ;
When the PARTITION BY clause is used ITERATE(n) causes rules to be iterated n times for each distinct partitioned value. For example, the following query repeats its rule five times for each of the three distinct values in GROUP_1, leading to a total of 15 rule iterations.
1 2 3 4 5 6 7 8 9
select group_1, iteration_count from ( selectdistinct group_1 from t where group_1 isnotnull ) t_a model partitionby ( group_1 ) dimension by ( 0as key ) measures ( cast( nullas varchar2(15) ) as iteration_count ) rules iterate( 5 ) ( iteration_count[0] = iteration_count[0] || to_char(iteration_number) ||' ' ) ;
1 2 3 4 5
GROUP_1 ITERATION_COUNT ---------- --------------- A 0 1 2 3 4 B 0 1 2 3 4 C 0 1 2 3 4
You can only use PREVIOUS in the UNTIL clause. If you use it elsewhere you will get an error.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
select key , m_1 from dual model dimension by ( 0as key ) measures ( 0as m_1 ) rules iterate (5) ( m_1[0] = PREVIOUS(m_1[0]) +10 ) ; ( m_1[0] = PREVIOUS(m_1[0]) +10 ) * ERROR at line 11: ORA-32618: incorrect use of MODEL PREVIOUS function
Keep in mind that referencing the current value of a cell on the right hand side of an iterated rule automatically gives you the previous value (assuming no other rules change the cell). Attempting to use PREVIOUS in the example above was a redundant effort.
ITERATION_NUMBER cannot be used in DIMENSION BY or MEASURES clauses.
By default, Oracle evaluates rules in the order they appear in the RULES clause. This is know as SEQUENTIAL ORDER. For example, consider the following query.
In addition to SEQUENTIAL ORDER there is another rule ordering behaviour available. It is called AUTOMATIC ORDER. With AUTOMATIC ORDER Oracle determines rule evaluation order based on the dependencies between rules. Consider the following example.
Unlike SEQUENTIAL ORDER, these results show that AUTOMATIC ORDER caused Oracle to evaluate rule 2 before rules 1 and 3 because rules 1 and 3 depend on the measure modified by rule 2.
In this tutorial we continue exploring the RULES part of the MODEL clause with a focus on rules that use an ORDER BY clause, also known as ordered rules.
In addition to controlling the evaluation order of different rules, as we saw with sequential and automatic ordering in a previous tutorial, you can also control the assignment order of different cells targeted by a single rule. To do this we specify an ORDER BY clause after a cell reference. This is useful when a rule uses the ANY keyword or a symbolic reference on its left side. Here is an example of two very similar rules. One processes cells in ascending domain value order. The other processes cells in descending domain value order.
In this tutorial we explore the RETURN ROWS component of the MODEL clause. With it you can control whether all rows selected or only those rows updated by model rules are returned by the query.
RETURN ALL ROWS is the default behaviour for MODEL queries. It causes the query to returns all rows selected whether they were updated by a rule or not.
1 2 3 4 5 6 7 8 9 10
break on group_1 skip1 duplicates
select key, row_status from t model RETURNALLROWS dimension by ( key ) measures ( cast( 'x'as varchar2(10) ) as row_status ) rules ( row_status[key in (1,3,5,7)] ='UPDATED' ) orderby key ;
1 2 3 4 5 6 7 8 9 10 11 12
KEY ROW_STATUS ------ ---------- 1 UPDATED 2 x 3 UPDATED 4 x 5 UPDATED 6 x 7 UPDATED 8 x 9 x 10 x
In this tutorial we explore the concept of reference models within the MODEL clause.
Until now the tutorials in this section have all dealt with a single model, called the main model. Beyond the main model you can create additional multi-dimensional arrays, called reference models, whose cells you can refer to in your main model. Reference models act like read-only lookup tables for your main model. As such, you cannot update or insert cells in a reference model.
Before we look at a reference model example note that, in addition to the table “T” we have used so far we will also use a table called “T3” which looks like this.
select key, group_2, group_2_name from t model REFERENCE T3_REF ON ( SELECT KEY, NAME FROM T3 ) DIMENSION BY ( KEY ) MEASURES ( NAME ) main T_MAIN dimension by ( key ) measures ( group_2, 'Undefined Group'as group_2_name ) rules ( group_2_name[any] = T3_REF.NAME[ GROUP_2[CV()] ] ) orderby key ;
createtable t3 ( key varchar2(10) , name varchar2(15) );
insertinto t3 values ( 'a1', 'Alpha Squad 1' ); insertinto t3 values ( 'a2', 'Alpha Squad 2' ); insertinto t3 values ( 'a3', 'Alpha Squad 3' );
commit;
column key format 99999 column new_text_measure format a20
column flag format a4 column using_any format a9 column using_is_any format a12 column using_null format a10 column using_is_null format a13 column using_equals_null format a17
Run the code on this page to drop the sample tables, procedures, etc. created in earlier parts of this section. To clear session state changes (e.g. those made by SET, COLUMN, and VARIABLE commands) exit your SQL*Plus session after running these cleanup commands.
Be sure to read Using SQL Snippets ™ before executing any of these setup steps.