Rows to String

MODEL Method 2 - Explained

This tutorial explains how the Rows to String solution presented in MODEL Method 2 works. If you are unfamiliar with Oracle's MODEL feature you should review the tutorials at SQL Features Tutorials: MODEL Clause tutorial before proceeding.

The basic solution in the preceding tutorial looked like this.

set null "(null)"

clear breaks

select group_key, string
from   t_group_keys
model
  reference r on
    ( select
        group_key ,
        row_number() over ( partition by group_key order by val ) - 1
          as position ,
        val
      from t
    )
    dimension by ( group_key, position )
    measures     ( val )
  main m
    dimension by ( group_key )
    measures     ( cast( null as varchar2(65) ) as string ) -- note 1
    rules
      update
      iterate( 6 ) -- note 2
      (
        string[ any ] =
          string[cv()] ||
          case presentv( r.val[cv(group_key),iteration_number],1,0)
          when 0 then null
          else
            case iteration_number when 0 then null else ',' end ||
            r.val[cv(group_key),iteration_number]
          end
      )
order by group_key ;
 
GROUP_KEY  STRING
---------- ---------------
Group 1    a
Group 2    a,b
Group 3    a,b,c
Group 4    a,a,b,b
Group 5    a,b,d,e,
Group 6    (null)
Group 7    ,
Group 8    a,a,b,b,,
(null)     (null)
 

To understand how it works we will look at intermediate steps in processing the query. First consider the reference model query, which selects the individual values we wish to form into a string. It also includes an extra column, POSITION, that numbers each value within each group.

break on group_key skip 1 duplicates

select
  group_key ,
  row_number() over ( partition by group_key order by val ) - 1
    as position ,
  val
from t
;
 
GROUP_KEY    POSITION VAL
---------- ---------- ----------
Group 1             0 a

Group 2             0 a
Group 2             1 b

Group 3             0 a
Group 3             1 b
Group 3             2 c

Group 4             0 a
Group 4             1 a
Group 4             2 b
Group 4             3 b

Group 5             0 a
Group 5             1 b
Group 5             2 d
Group 5             3 e
Group 5             4 (null)

Group 6             0 (null)

Group 7             0 (null)
Group 7             1 (null)

Group 8             0 a
Group 8             1 a
Group 8             2 b
Group 8             3 b
Group 8             4 (null)
Group 8             5 (null)

(null)              0 (null)
 

Next consider columns derived from the T_GROUP_KEYS table, which form the basis for our end result.

clear breaks

select
  group_key ,
  cast( null as varchar2(65) ) as string
from   t_group_keys
order by 1 ;
 
GROUP_KEY  STRING
---------- ---------------
Group 1    (null)
Group 2    (null)
Group 3    (null)
Group 4    (null)
Group 5    (null)
Group 6    (null)
Group 7    (null)
Group 8    (null)
(null)     (null)
 

To calculate the maximum size of the STRING column we multiply the maximum number of possible values in the string (6 in our example) times the maximum size of each value (10 characters) and then add room for the commas that will separate each value (5 commas).

Now let's add the MODEL clause. We will not iterate the rule though so we can demonstrate what happens only in the first iteration. A bind variable takes the place of the ITERATION_NUMBER function.

variable iteration_number number

execute :iteration_number := 0 ;

select group_key, string
from   t_group_keys
model
  reference r on
    ( select
        group_key ,
        row_number() over ( partition by group_key order by val ) - 1
          as position ,
        val
      from t
    )
    dimension by ( group_key, position )
    measures     ( val )
  main m
    dimension by ( group_key )
    measures     ( cast( null as varchar2(65) ) as string ) -- note 1
    RULES
      UPDATE
      (
        STRING[ ANY ] =
          STRING[CV()] ||
          CASE PRESENTV( R.VAL[CV(GROUP_KEY),:ITERATION_NUMBER],1,0)
          WHEN 0 THEN NULL
          ELSE
            CASE :ITERATION_NUMBER WHEN 0 THEN NULL ELSE ',' END ||
            R.VAL[CV(GROUP_KEY),:ITERATION_NUMBER]
          END
      )
order by group_key ;
 
GROUP_KEY  STRING
---------- ---------------
Group 1    a
Group 2    a
Group 3    a
Group 4    a
Group 5    a
Group 6    (null)
Group 7    (null)
Group 8    a
(null)     (null)
 

As we can see, the rule changed the STRING values from NULL to the first value in each group.

Now we will iterate the rule once for every possible value position. Each iteration concatenates a new value to the STRING values. This gives us the final result.

select group_key, string
from   t_group_keys
model
  reference r on
    ( select
        group_key ,
        row_number() over ( partition by group_key order by val ) - 1
          as position ,
        val
      from t
    )
    dimension by ( group_key, position )
    measures     ( val )
  main m
    dimension by ( group_key )
    measures     ( cast( null as varchar2(65) ) as string ) -- note 1
    rules
      update
      iterate( 6 ) -- note 2
      (
        string[ any ] =
          string[cv()] ||
          case presentv( r.val[cv(group_key),iteration_number],1,0)
          when 0 then null
          else
            case iteration_number when 0 then null else ',' end ||
            r.val[cv(group_key),iteration_number]
          end
      )
order by group_key ;
 
GROUP_KEY  STRING
---------- ---------------
Group 1    a
Group 2    a,b
Group 3    a,b,c
Group 4    a,a,b,b
Group 5    a,b,d,e,
Group 6    (null)
Group 7    ,
Group 8    a,a,b,b,,
(null)     (null)
 



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-12098.html]SQL Snippets: Rows to String - MODEL Method 2 - Explained[/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-12098.html">SQL Snippets: Rows to String - MODEL Method 2 - Explained</a>

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

  • Link Text : SQL Snippets: Rows to String - MODEL Method 2 - Explained
  • URL (href): http://www.sqlsnippets.com/en/topic-12098.html

Revision Notes

Date Category Note
2007-05-13 Revision Changed solution to use T_GROUP_KEYS instead of SELECT DISTINCT ... FROM T.