UNPIVOT with multi-column groups in Oracle 11g

0

I really like Oracle 11g database and one of my favourite features is the PIVOT/UNPIVOT clause in SQL. PIVOT is used to present rows as columns and UNPIVOT is the opposite operation, columns are presented as rows. I have found them very useful for APEX applications, when I need to present data in table rows as columns in an APEX form for user input.

PIVOT feature seems to have recieved a lot of bloggers attention, but I haven't read much about UNPIVOT. Using UNPIVOT with one column is pretty straight forward and easy, just a small example (I use Oracle Database 11.1.0.7 Enterprise Edition for all the examples here):

1
2
3
4
5
6
7
create table unpivot_ex1 (
 id number primary key,
 jan number,
 feb number,
 mar number,
 apr number
);

Fill it up with test data:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
insert into unpivot_ex1 (id, jan, feb, mar, apr)
 values (1, 1, 2, null, 4);
insert into unpivot_ex1 (id, jan, feb, mar, apr)
 values (2, 5, 6, null, 8);
insert into unpivot_ex1 (id, jan, feb, mar, apr)
 values (3, 9, 10, null, null);
commit;
 
SELECT * FROM unpivot_ex1;
 
    ID        JAN        FEB        MAR        APR
---------- ---------- ---------- ---------- ----------
     1          1          2                     4
     2          5          6                     8
     3          9         10

Now, if you want to select JAN, FEB, MAR, APR columns as separate rows, then its possible to use the simplest UNPIVOT clause:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
SELECT * FROM unpivot_ex1
  UNPIVOT INCLUDE NULLS
    (monthly_value FOR month IN (jan, feb, mar, apr));
 
        ID MON MONTHLY_VALUE
---------- --- -------------
         1 JAN             1
         1 FEB             2
         1 MAR
         1 APR             4
         2 JAN             5
         2 FEB             6
         2 MAR
         2 APR             8
         3 JAN             9
         3 FEB            10
         3 MAR
         3 APR
 
12 rows selected.

Now, lets take a more interesting case. What if some of the columns are combined into logical groups and you want to preserve them in one row? For example:

1
2
3
4
5
6
7
8
9
create table unpivot_ex2 (
  id number primary key,
  jan_income number,
  jan_expense number,
  feb_income number,
  feb_expense number,
  mar_income number,
  mar_expense number
);

For every month there is income and expense column.

1
2
3
4
5
6
7
8
9
10
insert into unpivot_ex2 values (1, 1, 1, 2, 3, 5, 2);
insert into unpivot_ex2 values (2, 5, 6, 4, 4, null, null);
commit;
 
SELECT * FROM unpivot_ex2;
 
        ID JAN_INCOME JAN_EXPENSE FEB_INCOME FEB_EXPENSE MAR_INCOME MAR_EXPENSE
---------- ---------- ----------- ---------- ----------- ---------- -----------
         1          1           1          2           3          5           2
         2          5           6          4           4

UNPIVOT allows to create column groups, like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
SELECT * FROM unpivot_ex2
  UNPIVOT INCLUDE NULLS
    ((income, expense) FOR month IN (
     (jan_income, jan_expense), (feb_income, feb_expense),
     (mar_income, mar_expense)));
 
        ID MONTH                      INCOME    EXPENSE
---------- ---------------------- ---------- ----------
         1 JAN_INCOME_JAN_EXPENSE          1          1
         1 FEB_INCOME_FEB_EXPENSE          2          3
         1 MAR_INCOME_MAR_EXPENSE          5          2
         2 JAN_INCOME_JAN_EXPENSE          5          6
         2 FEB_INCOME_FEB_EXPENSE          4          4
         2 MAR_INCOME_MAR_EXPENSE
 
6 rows selected.