Introduction
TL;DR : Why to use UNPIVOT over UNION ALL ? Performance, UNION ALL scans all the data for each subquery, with UNPIVOT the scan happens only once.
Disclaimer : in the example below WEEK_NUMBER is not a suitable identifier for the varchar type, the better name is WEEK_REF and the variables should better be prefixed with year, like 2024W01
Setup
Feel free to check the Oracle XE Quick Start
I will be using docker enabled Oracle XE 21.3.0 with DBeaver as a client.
UNPIVOT
Before starting, I'm inviting you to check a wonderful article from Oracle regarding PIVOTand UNPIVOT. If this article does exist, why to write another one? The answer is simple : because this article is a step-by-step guide which will help you to understand over copy-paste.
While writing this article I have found another good article about unpivot, check it out !
What is the business problem we are trying to solve : as a result of an aggregation over a big chunk of data we have a dataset composed with rows with multiple columns that we would like to stack one below another, probably with the common prefix for each chunk of the row stacked in the new representation.
Let's have an example :
SELECT 
    'SUNDAY' AS DAY_0,
    'MONDAY' AS DAY_1
FROM DUAL;

Simple stacking with UNION ALL :
WITH 
    DAY_DATA AS 
        (SELECT 
            'SUNDAY' AS DAY_0,
            'MONDAY' AS DAY_1
        FROM DUAL)
SELECT DAY_0 AS DAY_NAME FROM DAY_DATA
UNION ALL 
SELECT DAY_1 AS DAY_NAME FROM DAY_DATA;
And the same result with UNPIVOT :
SELECT DAY_NAME FROM (
    (SELECT 
        'SUNDAY' AS DAY_0,
        'MONDAY' AS DAY_1
    FROM DUAL)
    UNPIVOT (
        DAY_NAME FOR DAY_COLUMN IN (DAY_0, DAY_1)
    )
)

Great ! Let's move on and add two more columns that we would like to stack :
SELECT 
    'SUNDAY' AS DAY_0,
    0 AS DAY_ORDER_0,
    'MONDAY' AS DAY_1,
    1 AS DAY_ORDER_1
FROM DUAL

Stacking values from the columns DAY_0 and DAY_1 into the column DAY_NAME and values from the columns DAY_ORDER_0 and DAY_ORDER_1 into the column DAY_ORDER with UNION ALL :
WITH 
    DAY_DATA AS 
        (SELECT 
            'SUNDAY' AS DAY_0,
            1 AS DAY_ORDER_0,
            'MONDAY' AS DAY_1,
            1 AS DAY_ORDER_1
        FROM DUAL)
SELECT DAY_0 AS DAY_NAME, DAY_ORDER_0 AS DAY_ORDER FROM DAY_DATA
UNION ALL 
SELECT DAY_1 AS DAY_NAME, DAY_ORDER_1 AS DAY_ORDER FROM DAY_DATA;
And the same result with UNPIVOT
SELECT * FROM (
    (SELECT 
        'SUNDAY' AS DAY_0,
        1 AS DAY_ORDER_0,
        'MONDAY' AS DAY_1,
        1 AS DAY_ORDER_1
    FROM DUAL)
    UNPIVOT (
        (DAY_NAME, DAY_ORDER) 
        FOR DAY_COLUMN IN (
            (DAY_0, DAY_ORDER_0) AS 'DAY_0',
            (DAY_1, DAY_ORDER_1) AS 'DAY_1'
        )
    )
)
And when we have a common classifier :
SELECT 
    WEEK_NUMBER,
    DAY_NAME,
    DAY_ORDER
FROM (
    (SELECT 
        'W01' AS WEEK_NUMBER,
        'SUNDAY' AS DAY_0,
        1 AS DAY_ORDER_0,
        'MONDAY' AS DAY_1,
        1 AS DAY_ORDER_1
    FROM DUAL)
    UNPIVOT (
        (DAY_NAME, DAY_ORDER) 
        FOR DAY_COLUMN IN (
            (DAY_0, DAY_ORDER_0) AS 'DAY_0',
            (DAY_1, DAY_ORDER_1) AS 'DAY_1'
        )
    )
)
CROSS JOIN
Example above uses implicit CROSS JOIN under the hood, you may check an Oracle article about CROSS JOIN
Firstly let's have a simple example of CROSS JOIN :
WITH WEEK_DATA AS (
    SELECT 
        'W01' AS WEEK_NUMBER 
    FROM DUAL),
    DAY_DATA AS (
        SELECT 'SUNDAY' AS DAY_NAME FROM DUAL
    )
SELECT * FROM WEEK_DATA CROSS JOIN DAY_DATA;
Adding another row to the DAY_DATA:
WITH WEEK_DATA AS (
    SELECT 
        'W01' AS WEEK_NUMBER 
    FROM DUAL),
    DAY_DATA AS (
        SELECT 'SUNDAY' AS DAY_NAME FROM DUAL
        UNION ALL
        SELECT 'MONDAY' AS DAY_NAME FROM DUAL
    )
SELECT * FROM WEEK_DATA CROSS JOIN DAY_DATA;
Adding another row to the WEEK_DATA :
WITH WEEK_DATA AS (
        SELECT 'W01' AS WEEK_NUMBER FROM DUAL
        UNION ALL 
        SELECT 'W02' AS WEEK_NUMBER FROM DUAL
    ),
    DAY_DATA AS (
        SELECT 'SUNDAY' AS DAY_NAME FROM DUAL
        UNION ALL
        SELECT 'MONDAY' AS DAY_NAME FROM DUAL
    )
SELECT * FROM WEEK_DATA CROSS JOIN DAY_DATA;
CROSS JOIN with UNPIVOT
Let's combine both CROSS JOIN and UNPIVOT :
WITH WEEK_DATA AS (
        SELECT 'W01' AS WEEK_NUMBER FROM DUAL
    ),
    DAY_DATA AS (
        SELECT 
            DAY_NAME,
            DAY_ORDER
        FROM (
            (SELECT 
                'SUNDAY' AS DAY_0,
                1 AS DAY_ORDER_0,
                'MONDAY' AS DAY_1,
                1 AS DAY_ORDER_1
            FROM DUAL)
            UNPIVOT (
                (DAY_NAME, DAY_ORDER) 
                FOR DAY_COLUMN IN (
                    (DAY_0, DAY_ORDER_0) AS 'DAY_0',
                    (DAY_1, DAY_ORDER_1) AS 'DAY_1'
                )
            )
        )
    )
SELECT * FROM WEEK_DATA CROSS JOIN DAY_DATA;
Now you know how to combine both CROSS JOIN and UNPIVOT, the concepts explained above are semantically the same for the Big Data solutions hosted in different cloud providers, I will show few examples in the following articles. Stay tuned !
 









 
    
Top comments (0)