Implement a slowly changing dimension with MaxCompute

更新时间:
复制 MD 格式

A slowly changing dimension (SCD) is a data warehousing technique that tracks the complete history of an entity's changes, allowing you to query data state at any point in time. This topic walks through building an ETL process for an SCD table in DataWorks using the MaxCompute engine.

Prerequisites

  1. A DataWorks workspace is created. For more information, see Create and manage workspaces.

  2. A MaxCompute data source is added and is associated with the workspace. For more information, see Add a MaxCompute data source and associate the data source with a workspace.

Notes

In this tutorial, tables are created by running commands. In practice, you can create them visually in DataWorks. For more information, see Create and use MaxCompute tables.

Note

You can also import the tasks in the DataStudio part of this tutorial with a single click using an ETL workflow template. After importing the template, go to the target workspace to perform subsequent operations such as task maintenance.

Scenarios

The SCD technique is ideal for data models with the following characteristics:

  • Large data volume.

  • Some fields in a table are updated.

    Examples include user addresses, product descriptions, order statuses, and phone numbers.

  • You need to view historical snapshots of data for a specific point or period in time.

    For example, you might need to check the status of an order at a past date or see how many times a user's information was updated within a specific period.

  • The proportion or frequency of data changes is low.

    For example, imagine a table with 10 million members, but only about 100,000 are new or updated each day. Storing a full snapshot daily wastes significant storage by repeatedly saving unchanged information.

Key fields

An SCD table typically includes the following key fields:

Field

Description

Primary key

Uniquely identifies each row of data. Also known as a surrogate key.

Business key

The key that uniquely identifies an entity in the source system. For example, a member ID.

Attribute fields

The entity attributes whose changes you want to track. For example, a member's nickname or phone number.

Effective start date

The time when a version of the data becomes valid.

Effective end date

The time when a version of the data becomes invalid. A high-value date, such as '9999-12-31', is often used to indicate that the record is currently active.

Status flag

Indicates whether the record is the most recent version.

Version

Identifies different historical versions of a record that share the same business key.

Note

This topic focuses on SCD implementation and does not cover table design in detail.

Example overview

  • Table design: This example uses two data tables.

    • Order source table: ods_order_di. This table stores daily incremental data synchronized from the business database. It contains the following two fields:

      • id: The order ID, which serves as the business key.

      • status: The order status, which is the attribute to be tracked.

    • Order fact table (SCD table): dwd_order. This table uses the SCD method to store all active and inactive data. It contains the following four fields:

      • id: The order ID, which serves as the business key.

      • status: The order status, which is the attribute being tracked.

      • start_date: The effective start date.

      • end_date: The effective end date.

  • SCD implementation logic.

    • This example uses an SCD table to record all changes in an e-commerce order's status (Created, Paid, Completed) from creation to its current state. For more information about the loading logic, see Data loading logic for the SCD table.

Task development: Preparation

  1. Log on to the DataWorks console. In the target region, click Data Development and O&M > Data Development in the left-side navigation pane. Select a workspace from the drop-down list and click Go to Data Development.

  2. Create a workflow.

    1. Hover over the 新建 icon and click Create Workflow.

    2. In the Create Workflow dialog box, enter a Workflow Name and Description. For this example, name the workflow Tutorial_SCD_Implementation.

    3. Click Create.

  3. Create nodes.

    In the directory tree on the left, double-click the workflow that you created in Step 2 to open its panel. Then, drag components from the left pane and connect them on the canvas to build the workflow.

    This example uses two types of nodes: a zero load node and an ODPS SQL node.

    • The zero load node acts as the starting point for the entire SCD implementation, helping to manage the overall workflow.

    • The ODPS SQL node executes SQL tasks to load data into the SCD table.

  4. Preview the task workflow.

    This tutorial will create the following workflow. For clarity, this example uses the node group feature to group related nodes. The code for each node is provided in the subsequent steps.

    The Tutorial_SCD_Implementation workflow contains the following nodes in order of dependency: the zero load node SCD_Implementation_Description → the ODPS SQL node ods_order_di (in the Prepare_Incremental_Table node group) → the ODPS SQL node dwd_order (in the Implement_SCD_Table node group) → the ODPS SQL node Query_SCD_Table. In DataStudio, after opening the workflow, drag the zero load node and ODPS SQL node from the node list on the left to the canvas and connect them to establish dependencies.

Task development: Prepare incremental data table

  • In the data preparation stage, you need to create two types of nodes:

    • Create a zero load node: SCD_Implementation_Description. This node manages and describes the workflow.

    • Create an ODPS SQL node: ods_order_di. This node generates the source table for order transactions.

  • On the workflow canvas, connect the SCD_Implementation_Description zero load node to the ods_order_di SQL node within the Prepare_Incremental_Table node group to configure the dependency.

ods_order_di

  • Table data: The order source table contains the order ID (business key) and the order status field to track changes. It stores daily new order data.

  • On the editor tab of the ods_order_di node, use the following sample data from ods_order.csv to populate the ods_order_di source table.

    • Test data in ods_order.csv.

      id,gmt_create,gmt_modified,status,pt
      210001,2023-10-04,2023-10-04,Created,2023-10-04
      210002,2023-10-04,2023-10-04,Created,2023-10-04
      210001,2023-10-04,2023-10-05,Paid,2023-10-05
      210003,2023-10-05,2023-10-05,Created,2023-10-05
      210004,2023-10-05,2023-10-05,Created,2023-10-05
      210001,2023-10-04,2023-10-06,Completed,2023-10-06
      210002,2023-10-04,2023-10-06,Paid,2023-10-06
      210004,2023-10-05,2023-10-06,Paid,2023-10-06
      210005,2023-10-06,2023-10-06,Created,2023-10-06
    • When inserting test data, add it to the corresponding partition (pt) as follows:

      -- Note: For testing purposes, we insert three days of new data into partitions '2023-10-04', '2023-10-05', and '2023-10-06' at once.
      --       In a real-world scenario, you would typically update only the partition for the current business date. This can be done by setting the partition date as a scheduling parameter variable.
      INSERT OVERWRITE TABLE ods_order_di PARTITION (pt = '2023-10-04') VALUES
              (210001,DATE'2023-10-04',DATE'2023-10-04','Created')
              ,(210002,DATE'2023-10-04',DATE'2023-10-04','Created')
      ;
      INSERT OVERWRITE TABLE ods_order_di PARTITION (pt = '2023-10-05') VALUES
              (210001,DATE'2023-10-04',DATE'2023-10-05','Paid')
              ,(210003,DATE'2023-10-05',DATE'2023-10-05','Created')
              ,(210004,DATE'2023-10-05',DATE'2023-10-05','Created')
      ;
      INSERT OVERWRITE TABLE ods_order_di PARTITION (pt = '2023-10-06') VALUES
              (210001,DATE'2023-10-04',DATE'2023-10-06','Completed')
              ,(210002,DATE'2023-10-04',DATE'2023-10-06','Paid')
              ,(210004,DATE'2023-10-05',DATE'2023-10-06','Paid')
              ,(210005,DATE'2023-10-06',DATE'2023-10-06','Created')
    • The complete code is as follows:

      -- 1. Create the ods_order_di source table to store daily new order data.
      -- Note: For clarity, the order creation and modification time fields in this example are at a daily granularity. It is assumed that an order is processed only once per day.
      --       In real-world scenarios, time fields usually have a finer granularity, and multiple operations may occur on an order within a single day.
      CREATE TABLE IF NOT EXISTS ods_order_di
      (
          id            BIGINT COMMENT 'Order ID'
          ,gmt_create   DATE COMMENT 'Creation time, in yyyy-mm-dd format'
          ,gmt_modified DATE COMMENT 'Update time, in yyyy-mm-dd format'
          ,`status`     STRING COMMENT 'Status'
      )
      COMMENT 'Source table for transaction orders'
      PARTITIONED BY 
      (
          pt            STRING COMMENT 'Date, in yyyy-mm-dd format'
      )
      LIFECYCLE 7
      ;
      -- 2. Initialize historical new order data.
      -- Note: For testing purposes, we insert three days of new data into partitions '2023-10-04', '2023-10-05', and '2023-10-06' at once.
      --       In a real-world scenario, you would typically update only the partition for the current business date. This can be done by setting the partition date as a scheduling parameter variable.
      INSERT OVERWRITE TABLE ods_order_di PARTITION (pt = '2023-10-04') VALUES
              (210001,DATE'2023-10-04',DATE'2023-10-04','Created')
              ,(210002,DATE'2023-10-04',DATE'2023-10-04','Created')
      ;
      INSERT OVERWRITE TABLE ods_order_di PARTITION (pt = '2023-10-05') VALUES
              (210001,DATE'2023-10-04',DATE'2023-10-05','Paid')
              ,(210003,DATE'2023-10-05',DATE'2023-10-05','Created')
              ,(210004,DATE'2023-10-05',DATE'2023-10-05','Created')
      ;
      INSERT OVERWRITE TABLE ods_order_di PARTITION (pt = '2023-10-06') VALUES
              (210001,DATE'2023-10-04',DATE'2023-10-06','Completed')
              ,(210002,DATE'2023-10-04',DATE'2023-10-06','Paid')
              ,(210004,DATE'2023-10-05',DATE'2023-10-06','Paid')
              ,(210005,DATE'2023-10-06',DATE'2023-10-06','Created')

Task development: Implement the SCD table

  • Create an ODPS SQL node: dwd_order. This node generates the order fact table.

  • Connect the SCD_Implementation_Description zero load node to the ods_order_di ODPS SQL node in the Prepare_Incremental_Table group. Then, connect ods_order_di to the dwd_order ODPS SQL node in the Implement_SCD_Table group.

dwd_order

  • Table data: The order fact table (SCD table) stores all active and inactive data. It includes the effective start date (start_date), effective end date (end_date), business key ID, and the status field for tracking order status changes.

  • On the editor tab of the dwd_order node, enter the following sample code:

    -- 1. Create the order fact table to store all historical data using the SCD method.
    -- Note: The SCD table adds lifecycle fields: start_date (effective date) and end_date (expiration date). 
    --       An end_date of '9999-12-31' indicates that the record is currently valid.
    CREATE TABLE IF NOT EXISTS dwd_order
    (
        ID            BIGINT COMMENT 'Order ID'
        ,gmt_create   DATE COMMENT 'Creation time, in yyyy-mm-dd format'
        ,gmt_modified DATE COMMENT 'Update time, in yyyy-mm-dd format'
        ,`status`     STRING COMMENT 'Status'
        ,start_date   DATE COMMENT 'Effective date, in yyyy-mm-dd format'
        ,end_date     DATE COMMENT 'Expiration date, in yyyy-mm-dd format. 9999-12-31 indicates the record is active.'
    )
    COMMENT 'Fact detail table for transaction orders (SCD table)'
    ;
    -- 2. SCD storage logic: Insert all new or updated data from ods_order_di for the current business date into dwd_order as new, valid records.
    --    Update the previously active records in dwd_order that have been changed to be inactive.
    INSERT OVERWRITE TABLE dwd_order
    SELECT  id
            ,gmt_create
            ,gmt_modified
            ,`status`
            ,start_date
            ,end_date
    FROM    (
                SELECT  id
                        ,gmt_create
                        ,gmt_modified
                        ,`status`
                        ,start_date
                        ,end_date
                        -- 2.3 Support re-runs: Use a window function to partition by id, gmt_create, gmt_modified, and `status`. Sort by end_date in descending order within the partition and take only the first row.
                        ,ROW_NUMBER() OVER (DISTRIBUTE BY id,gmt_create,gmt_modified,`status` SORT BY end_date DESC ) AS row_num
                FROM    (
                            -- 2.1 Invalidate old records: For records in dwd_order that have been updated, change their status from active to inactive.
                            SELECT  a.id AS id
                                    ,a.gmt_create AS gmt_create
                                    ,a.gmt_modified AS gmt_modified
                                    ,a.`status` AS `status`
                                    ,a.start_date AS start_date
                                    ,CASE   WHEN b.id IS NOT NULL
                                                -- AND CAST(a.end_date AS STRING) > '${biz_date}' THEN DATE'${biz_date}' 
                                                AND CAST(a.end_date AS STRING) == '9999-12-31' THEN DATE'${biz_date}' 
                                            ELSE a.end_date
                                    END AS end_date
                            -- 2.4 Note on scheduling dependencies: Because the calculation for dwd_order on the current day depends on its own output from the previous day, we configure a dependency on "Instances of Current Node" in the Properties > Cross-cycle Dependency section. 
                            FROM    dwd_order AS a
                            LEFT JOIN   (
                                            SELECT  id
                                                    ,gmt_create
                                                    ,gmt_modified
                                                    ,`status`
                                            FROM    ods_order_di
                                            WHERE   pt == '${biz_date}'
                                        ) b
                            ON      a.id == b.id
                            UNION ALL 
                            -- 2.2 Insert new and updated records: Insert all new or updated records from ods_order_di for the current business date into dwd_order as active records.
                            SELECT  id
                                    ,gmt_create
                                    ,gmt_modified
                                    ,`status`
                                    ,DATE'${biz_date}' AS start_date
                                    ,DATE'9999-12-31' AS end_date
                            FROM    ods_order_di
                            WHERE   pt == '${biz_date}'
                        ) 
            ) with_row_num_tb
    -- Support re-runs
    WHERE   row_num == 1
    ;
  • Configure scheduling. The following are the key configurations. You can keep the default values for other parameters.

    • Scheduling Parameter: In the Scheduling > Scheduling Parameter section, click Load Parameters in Code to automatically generate the parameter configuration. In the Parameter Value field, enter $[yyyy-mm-dd-1]. For more information about supported formats for scheduling parameters, see Supported formats of scheduling parameters.

      Note

      You can also click Define by expression to manually add the scheduling parameter biz_date=$[yyyy-mm-dd-1]

    • Configure the Cross-cycle Scheduling Dependency. The data processing for the dwd_order table on a given day depends on its own output from the previous day. To ensure data accuracy and continuity, you must configure a self-dependency.

      In the Scheduling > Scheduling Dependency > Cross-cycle Dependency (Original Previous-cycle Dependency) section, select the This node checkbox. This ensures that the data for the current cycle is loaded only after the data from the previous cycle has been successfully processed. This ensures the integrity of the data processing flow.

SCD table data loading logic

  • Existing active records in the dwd_order table that are updated on the current business date are marked as inactive.

  • All new records from the ods_order_di table for the current business date are inserted into the dwd_order table as active records.

The following steps use the data for the business date of October 5 as an example:

  1. Original data: On October 4, the SCD table contains a record for order ID 210001 with the status Created.

  2. Status change: On October 5, the status of this order is updated to Paid.

  3. Data comparison logic:

    1. Join all active historical data from the SCD table on October 4 with the new data from the 2023-10-05 partition of the incremental table.

    2. Check for status updates: If the order with ID 210001 exists in the SCD table on October 4 with an end_date of 9999-12-31 (meaning it is active), and it also appears in the 2023-10-05 partition of the incremental table, its status has been updated.

  4. Update process:

    1. The end_date of the original record in the SCD table is updated from 9999-12-31 to the current business date, 2023-10-05.

    2. If the order ID does not appear in the 2023-10-05 partition of the incremental table, it means there is no update, and the original record remains unchanged.

  5. Data overwrite:

    After completing these steps, the processed historical data from the SCD table and all new data from the incremental table are written back into the SCD table, producing the final SCD data for October 5.

yuque_diagram.jpg

Task development: Use the SCD table

  • At this stage, create an ODPS SQL node that depends on all the preceding tasks:

    • A node named Query_SCD_Table.

  • Connect the nodes to establish the full dependency chain: the SCD_Implementation_Description zero load node connects to the ods_order_di SQL node, which then connects to the dwd_order SQL node, and finally connects to the Query_SCD_Table SQL node. These connections establish the upstream and downstream dependencies.

  • Query node: After the data loading is complete, create a query node and write SQL queries to view the SCD storage results.

  • On the editor tab for the Query_SCD_Table node, enter the following sample code:

    -- 1. Use the SCD table
    -- 1.1 To get all current and valid data, query the records from the dwd_order table where end_date is '9999-12-31'.
    SELECT  id
            ,gmt_create
            ,gmt_modified
            ,`status`
            ,start_date
            ,end_date
    FROM    dwd_order
    WHERE   end_date == DATE'9999-12-31'
    ORDER BY id DESC
    LIMIT   100
    ;
    -- 1.2 To get all historical snapshots for a specific order, query the dwd_order table by its ID and sort the results by end_date.
    SELECT  id
            ,gmt_create
            ,gmt_modified
            ,`status`
            ,start_date
            ,end_date
    FROM    dwd_order
    WHERE   id == 210001
    ORDER BY end_date DESC
    LIMIT   100
    ;
    -- 1.3 Query all data in the dwd_order table and sort it by order ID and end_date in descending order.
    SELECT  id
            ,gmt_create
            ,gmt_modified
            ,`status`
            ,start_date
            ,end_date
    FROM    dwd_order
    ORDER BY id,end_date DESC
    LIMIT   100
    ;

Run the workflow

Because the test case inserts three days of data at once into partitions "2023-10-04", "2023-10-05", and "2023-10-06", you must perform a data backfill in the production environment to process all business dates. Follow these steps in Operation Center.

  1. Commit tasks.

    1. Click the image icon in the toolbar.

    2. In the Submission dialog box, select the nodes you want to commit and enter a Change Description.

    3. Click OK. When the Operation Completed message appears, the tasks are committed successfully.

      If you are using a workspace in standard mode, click Deploy in the upper-right corner after a successful commit. For more information, see Deploy tasks.

  2. Go to Operation Center.

    After the tasks are deployed, click Operation Center in the upper-right corner.

    Alternatively, go to the editor page of the Tutorial_SCD_Implementation workflow and click Go to Operation Center in the toolbar to go to the Operation Center page.

  3. Run a data backfill instance for the Auto Triggered Node.

    1. In the left-side navigation pane, choose Auto Triggered Task O&M > Periodic tasks. On the Periodic tasks page, click the root node of the Tutorial_SCD_Implementation workflow, which is SCD_Implementation_Description.

    2. Right-click the SCD_Implementation_Description node and choose Retroactive Instance > Current and Descendant Nodes Retroactively.

    3. In the Retroactive Instance dialog box, set the Business Date to 2023-10-04~2023-10-06. In the Nodes section, select all nodes, and click Determine. You are automatically redirected to the Data Backfill Instance page.

    4. Click Refresh until all SQL tasks have run successfully.

Note

To avoid incurring ongoing charges after completing the tutorial, you can set the scheduling validity period for the nodes or freeze the root node of the workflow (the zero load node SCD_Implementation_Description).

Query results

After all SQL tasks have run in Operation Center, you can view the results in the log:

  1. On the Backfill Instance page, find the Query_SCD_Table node in the instance list. In the Operation column, click DAG, then click View Log.

  2. Go to the log details page for the Query_SCD_Table node. In the Operation Details > Execution > View Logs section, you can view the run results.

    After the node runs successfully, the Summary area in the log displays the query results. The result table shows status changes (Created, Paid, Completed) for orders 210001 to 210005 over different time periods, with columns for id, gmt_create, gmt_modified, status, start_date, and end_date. Each order has multiple rows, demonstrating the historical snapshot capability of the SCD table.

Note

After all SQL tasks have run in Operation Center, you can also return to DataStudio and run the Query_SCD_Table node independently to view the SCD storage results.

Click Run in the toolbar to execute the SQL. The query uses WHERE end_date == DATE'9999-12-31' to filter for all currently active data in the dwd_order table. After the run completes, view the active record for each order in the results panel below.

Appendix: Using an ETL workflow template

This tutorial is available as a built-in ETL workflow template in DataWorks. Import the template by following these steps:

  1. Log on to the DataWorks console. In the left-side navigation pane, click Big Data Experience > > > ETL Workflow Template to go to the ETL Workflow Template page.

  2. On the ETL Workflow Template page, select the SCD Implementation workflow. Click View Details to go to the template page, then click Load Template.

  3. In the Load Template dialog box, select the target Workspace. In the MaxCompute Configuration section, select a data source from the Data Source Name drop-down list. Click OK to load the template.

Release resources

After testing, release the resources generated by this tutorial. For more information, see the following documents:

Related documents