Loading...

Transforming Your Data Lake: Implementing Slow Change Dimension with Synapse

Transforming Your Data Lake: Implementing Slow Change Dimension with Synapse

As organizations continue to collect and store large volumes of data in their data lakes, managing this data effectively becomes increasingly important. One key aspect of this is implementing Slow Change Dimension type 2, which allows organizations to track historical data by creating multiple records for a given natural key in the dimensional tables with separate surrogate keys and/or different version numbers. In this blog post we will address the following scenario: a customer wants to implement Slow Change Dimension type 2 on top of their data lake.

 

For this example, we will use Serverless SQL Pool to demonstrate how this can be done. Additionally, in the next post, we will explore how the same approach can be used with Spark.


What is Slow Change Dimension Type 2?

Slow Change Dimension type 2: This method tracks historical data by creating multiple records for a given natural key in the dimensional tables with separate surrogate keys and/or different version numbers. Check out this wiki to learn more, https://en.wikipedia.org/wiki/Slowly_changing_dimension

 

When implementing Slow Change Dimension type 2, there are various approaches that can be used depending on how the data is handled at the source. For example, if the source includes row version information or columns with flags for deleted or updated records, a different approach may be used compared to scenarios where this information is not available. In this particular scenario, we will follow a specific approach to address the challenges that are present.

 

Considerations for this Scenario:

  • This scenario is managing UPSERT and file versioning. Please note, updates are not supported on Serverless so I am workaround this with the external tables.
  • The files are not coming from the source with version, or information if the row was updated or deleted. 
  • Deleted rows are not been handled on this solution, only inserted and updated ones. 
  • Those files have a key value that is used to filter if they already exist or not on the destination. If you do not have a key column to compare. You will need to compare all the columns that the business considers as key values to determine if the information is new or not.  
  • The source will send the information on the file regardless if it is a new row to be inserted or updated. In other words, the process of transformation and cleansing needs to understand if the value on the file refers to an updated row or a new row. So this solution tries to understand that scenario and based on that version it accordingly. If your source can send the rows in an accurate way in which it is clear which row is updated, you can change the steps accordingly. 

 

Solution:

Consider as in the example below the folder sourcefiles_folder with all files in parquet format which were sent from the source to be compared to a destination. My destination is the external table: SCD_DimDepartmentGroup.

 

 The file has the following columns:

      

Column

Datatype

 

DepartmentGroupKey

int

Not Null

ParentDepartmentGroupKey

int

NULL

DepartmentGroupName

nvarchar

Not Null

 

Here are the current values for this table - SCD_DimDepartmentGroup as Fig. 1, shows:

The last surrogate key is of the value of 8:

Liliam_Leme_1-1682362152079.png

Fig. 1

 

Code - First Load and Dimension Creation:

 

CREATE EXTERNAL TABLE SCD_DimDepartmentGroup WITH ( LOCATION = '/SCD_DimDepartmentGroup', DATA_SOURCE = SCD_serveless_dim, FILE_FORMAT = Parquet_file ) AS SELECT ROW_NUMBER () OVER (ORDER BY DepartmentGroupKey) ID_Surr ,[DepartmentGroupKey] ,[ParentDepartmentGroupKey] ,[DepartmentGroupName] ,1 ID_valid ,0 ID_Deleted ,Curr_date as From_date ,Null as End_date FROM OPENROWSET( BULK 'https://Storage.blob.core.windows.net/Container/SCD/sourcefiles_folder/', FORMAT = 'PARQUET' ) AS [SCD_DimDepartmentGroup]

 

 

Slow Change Dimension in 5 steps:

The implementation of the SCD is quite simple is basically filtering, persisting the data filtered, and comparing.

 

Current values on the table: 

  • ID_Valid column will show if the row was versioned or not. 1 for a new row, 0 for a versioned row.
  • ID_Delete column will show if the row is still valid or not. 0 for a new not, 1 for an invalid
  • Curr_date is the date where the row was inserted into the table, a column on the SCD will be FromDate
  • ID_Surr is a surrogate key created for this table.  
  • EndDate will manage the version per date

 

1) Step 1 - Create a CETAS for new values

TableA_SCD_DimDepartmentGroup_NEW - In this example, this step will basically insert all the values that exist on the files and do not exist on the destination into an External Table.

 

The column DepartmentGroupKey defines if the row is new or not. You will need a column or columns to understand if this is considered new information or not in your dimension.

 

Please note three columns were added to this example:

  • As Curr_date is the date where the row was inserted into the table. So we are using getdate()
  • 1 ID_Valid  which means this is a new valid row
  • 1 ID_Delete which means this row was not deleted Or versioned.

 

Code - First, let's create the data source that will be used across all the external tables:

 

CREATE EXTERNAL DATA SOURCE SCD_serveless_dim WITH ( LOCATION = 'https://Storage.blob.core.windows.net/Container/SCD/transformation_folder/', CREDENTIAL = [MSI] )

 

 

Now, let's create the external table for Step 1:

 

Please note that I am using the maximum value of the surrogate key column ID_Surr from the main table SCD_DimDepartmentGroup and adding 1 to it to generate new surrogate values for rows that require them. If some values repeat at this point, it is okay because I will reuse this information with the Department Key to create a new surrogate key in the last step (Step 5), thus ensuring a unique value for each row. I am using the max+1 approach to ensure that existing surrogate keys are not affected.

 

DECLARE @ID_Surr AS INT SELECT @ID_Surr = MAX (ID_Surr)+1 FROM SCD_DimDepartmentGroup CREATE EXTERNAL TABLE TableA_SCD_DimDepartmentGroup_NEW WITH ( LOCATION = '/TableA_SCD_DimDepartmentGroup_NEW', DATA_SOURCE = SCD_serveless_dim, FILE_FORMAT = Parquet_file ) AS SELECT @ID_Surr as ID_Surr ,[DepartmentGroupKey] ,[ParentDepartmentGroupKey] ,[DepartmentGroupName] ,1 ID_valid ,0 ID_Deleted ,Getdate() as Curr_date FROM OPENROWSET( BULK 'https://Storage.blob.core.windows.net/Container/SCD/sourcefiles_folder/', FORMAT = 'PARQUET' ---DELTA can be used here ) AS [SCD_DimDepartmentGroup_Silver] WHERE NOT EXISTS ( SELECT 1 FROM SCD_DimDepartmentGroup WHERE SCD_DimDepartmentGroup_Silver.[DepartmentGroupKey] = SCD_DimDepartmentGroup.DepartmentGroupKey )

 

 

Results in Fig. 2 - Step 1:

Liliam_Leme_0-1682360777656.png

Fig. 2 - Step 1.

 

2) Step 2 - Create a CETAS for values that will be updated/versioned.

 

TableB_SCD_DimDepartmentGroup_OLD will be the next external table to be created by inserting all values that exist on the file exported from the source and do exist on the destination. Those value also should not exist on the external table created in Step 1 (A).

 

In this scenario, the data that will be versioned is not a new row (Step 1); it is rows that already exist in the destination, but something has changed. The DepartmentGroupKey is the key that will always remain the same, so if there is a new key, it means a new row, and the comparison will happen for any other column. Additionally, in this scenario, it is possible that the source may send the same information again without any changes. Therefore, comparing the data is necessary to ensure that the row sent is indeed a row to be versioned.

 

Please note for the rows to be versioned:

  • Flag ID_Valid was changed to 0
  • ID_Deleted was invalidated by changing the value to 1. 
  • Curr_date here is the date the row was inserted on the table  SCD_DimDepartmentGroup, so the From_date column is re-used.
  • NULL columns are been handled properly(ISNULL) to find the ones that in fact changed.

 

Code:

Please note I re-used my surrogate key column ID_Surr of the main table SCD_DimDepartmentGroup for the values that will be versioned.

 

CREATE EXTERNAL TABLE TableB_SCD_DimDepartmentGroup_OLD WITH ( LOCATION = '/TableB_SCD_DimDepartmentGroup_OLD', DATA_SOURCE = SCD_serveless_dim, FILE_FORMAT = Parquet_file ) AS CREATE EXTERNAL TABLE TableB_SCD_DimDepartmentGroup_OLD WITH ( LOCATION = '/TableB_SCD_DimDepartmentGroup_OLD', DATA_SOURCE = SCD_serveless_dim, FILE_FORMAT = Parquet_file ) AS SELECT ID_Surr ,[DepartmentGroupKey] ,[ParentDepartmentGroupKey] ,[DepartmentGroupName] ,0 ID_valid ,1 ID_Deleted ,From_date FROM [SCD_DimDepartmentGroup] WHERE NOT EXISTS ( SELECT 1 FROM OPENROWSET( BULK 'https://Storage.blob.core.windows.net/Container/SCD/sourcefiles_folder', FORMAT = 'PARQUET' ) AS [SCD_DimDepartmentGroup_Silver] WHERE SCD_DimDepartmentGroup_Silver.[DepartmentGroupKey] = SCD_DimDepartmentGroup.DepartmentGroupKey AND (ISNULL(SCD_DimDepartmentGroup_Silver.[ParentDepartmentGroupKey], 1) = ISNULL(SCD_DimDepartmentGroup.[ParentDepartmentGroupKey], 1) AND SCD_DimDepartmentGroup_Silver.[DepartmentGroupName] = SCD_DimDepartmentGroup.[DepartmentGroupName]) ) AND NOT EXISTS (SELECT 1 FROM TableA_SCD_DimDepartmentGroup_NEW WHERE TableA_SCD_DimDepartmentGroup_NEW.[DepartmentGroupKey] = SCD_DimDepartmentGroup.DepartmentGroupKey)

 

 

Results are Fig. 3 - Step 2 :

Liliam_Leme_1-1682361011777.png

Fig. 3 - Step 2

 

3) Step 3 - Only updated values are to be versioned

TableC_SCD_DimDepartmentGroup_GOLD_OLD_INS - The next external table will get the new rows from the source file. The filter will be the rows that we already know something changed and needs to be versioned from Step 2.

 

 

Code:

 

Please note that I am using the maximum value of the surrogate key column ID_Surr from the main table SCD_DimDepartmentGroup and adding 1 to it to generate new surrogate values for rows that require them. If some values repeat at this point, it is okay because I will reuse this information with the Department Key to create a new surrogate key in the last step (Step 5), thus ensuring a unique value for each row. I am using the max+1 approach to ensure that existing surrogate keys are not affected.

 

DECLARE @ID_Surr AS INT SELECT @ID_Surr = MAX (ID_Surr)+1 FROM SCD_DimDepartmentGroup CREATE EXTERNAL TABLE TableC_SCD_DimDepartmentGroup_GOLD_OLD_INS WITH ( LOCATION = '/TableC_SCD_DimDepartmentGroup_GOLD_OLD_INS', DATA_SOURCE = SCD_serveless_dim, FILE_FORMAT = Parquet_file ) AS SELECT @ID_Surr as ID_Surr ,[DepartmentGroupKey] ,[ParentDepartmentGroupKey] ,[DepartmentGroupName] ,1 ID_valid ,0 ID_Deleted ,getdate() Curr_date FROM OPENROWSET( BULK 'https://Storage.blob.core.windows.net/Container/SCD/sourcefiles_folder/', FORMAT = 'PARQUET' ) AS [SCD_DimDepartmentGroup_Silver] WHERE EXISTS (SELECT 1 FROM TableB_SCD_DimDepartmentGroup_OLD WHERE SCD_DimDepartmentGroup_Silver.[DepartmentGroupKey] = TableB_SCD_DimDepartmentGroup_OLD.DepartmentGroupKey)

 

 

The results are in Fig. 4 - Step 3:

Liliam_Leme_0-1682361350890.png

Fig. 4 - Step 3

 

4) Step 4 - Create another external table by Union 

For all those external tables that were created on the last steps.

  • EndDatate should be null for new rows or new values versioned. If the value was versioned the respective row will contain until when it was valid.
  • ID_valid will be 1 if the row is not versioned, 0 if this row is no longer valid
  • ID_Deleted will be 0 if the row is not versioned, 1 if this row is no longer valid

 

CREATE EXTERNAL TABLE UNION_SCD_DimDepartmentGroup WITH ( LOCATION = '/UNION_SCD_DimDepartmentGroup', DATA_SOURCE = SCD_serveless_dim, FILE_FORMAT = Parquet_file ) AS SELECT ID_Surr ,[DepartmentGroupKey] ,[ParentDepartmentGroupKey] ,[DepartmentGroupName] ,ID_valid ,ID_Deleted ,getdate() as From_date ,Null as End_date FROM TableA_SCD_DimDepartmentGroup_NEW UNION ALL SELECT ID_Surr ,[DepartmentGroupKey] ,[ParentDepartmentGroupKey] ,[DepartmentGroupName] ,ID_valid ,ID_Deleted ,From_date as From_date ,Getdate() as End_date FROM TableB_SCD_DimDepartmentGroup_OLD UNION ALL SELECT ID_Surr ,[DepartmentGroupKey] ,[ParentDepartmentGroupKey] ,[DepartmentGroupName] ,ID_valid ,ID_Deleted ,getdate() as From_date ,Null as End_date FROM TableC_SCD_DimDepartmentGroup_GOLD_OLD_INS

 

 

Results are - Fig. 5 - Union :

Liliam_Leme_2-1682362917824.png

 

Fig. 5 - Union

 

Here we have the new rows, new versioned rows consolidated on the external table.

 

5) Step 5. 

Next, we need to transfer this to our main table, by recreating an external table with the new information and dropping the old one.

1) I need the data from the main table excluding the rows that were versioned on this round

2) I need the data from the external tables Union.

3) I need to keep the historical changes of the old/main table

4) On top of that I will recreate the surrogate key. But for this, we will order per ID_Surr and DepartmentGroupKey. So, the new surrogate key values will start from the max surrogate+1 as we defined in the first Steps.

 

SELECT ROW_NUMBER () OVER (ORDER BY ID_Surr, DepartmentGroupKey) ID_Surr ,[DepartmentGroupKey] ,[ParentDepartmentGroupKey] ,[DepartmentGroupName] ,ID_valid ,ID_Deleted ,From_date ,End_date FROM ( SELECT ID_Surr ,[DepartmentGroupKey] ,[ParentDepartmentGroupKey] ,[DepartmentGroupName] ,ID_valid ,ID_Deleted ,From_date ,End_date FROM SCD_DimDepartmentGroup WHERE NOT EXISTS ( SELECT 1 FROM TableB_SCD_DimDepartmentGroup_OLD WHERE SCD_DimDepartmentGroup.DepartmentGroupKey = TableB_SCD_DimDepartmentGroup_OLD.DepartmentGroupKey ) UNION SELECT ID_Surr ,[DepartmentGroupKey] ,[ParentDepartmentGroupKey] ,[DepartmentGroupName] , ID_valid ,ID_Deleted ,From_date ,End_date FROM UNION_SCD_DimDepartmentGroup UNION SELECT ID_Surr ,[DepartmentGroupKey] ,[ParentDepartmentGroupKey] ,[DepartmentGroupName] ,ID_valid ,ID_Deleted ,From_date ,End_date FROM SCD_DimDepartmentGroup WHERE SCD_DimDepartmentGroup.ID_valid = 0 )NEW_SCD

 

 

Results are in Fig. 6 - New Table. Please note in green there are the rows that are versioned, and in blue are the new rows:

Liliam_Leme_0-1682361908171.png

Fig. 6 - New Table

 

This new table should replace the current SCD_DimDepartmentGroup, by recreating a new external table and dropping this old one. 

 

Summary

Please review the considerations for the scenario to understand what was relevant for this implementation. As I mentioned earlier, there are different approaches that can be used to create a Slow Change Dimension, depending on how the data is handled at the source. In this post, I have provided examples of how to read new data, version the old data that has changed, and recreate new external tables with the updated information for a Slow Change Dimension type 2.

 

In the next post, I will demonstrate how to achieve the same using Spark. Stay tuned for more!

 

Liliam, UK. 

Published on:

Learn more
Azure Synapse Analytics Blog articles
Azure Synapse Analytics Blog articles

Azure Synapse Analytics Blog articles

Share post:

Related posts

We're moving!

We’re moving to the Analytics on Azure Tech Community! All new Azure Synapse Analytics content will be published there. In the next few days a...

1 year ago

Upgrade to Azure Synapse runtimes for Apache Spark 3.4 & previous runtimes deprecation

It is important to stay ahead of the curve and keep services up to date. That's why we encourage all Azure Synapse customers with Apache ...

2 years ago

ADF\Synapse Analytics - Replace Columns names using Rule based mapping in Mapping data flows

In real time, the column names from source might not be uniform, some columns will have a space in it, some other columns will not. For exampl...

2 years ago

Interpreting Script activity output json with Azure Data Factory\Synapse analytics

Script activity in Azure Data Factory\ Synapse analytics is very helpful to run queries against data sources mentioned here in this document.&...

2 years ago

Synapse Connectivity Series Part #4 - Advanced network troubleshooting and network trace analysis

Continuing the series of this blog posts I would like to go more advanced on troubleshooting connectivity issues. I would like to thank also&n...

2 years ago

Boost your CICD automation for Synapse SQL Serverless by taking advantage of SSDT and SqlPackage CLI

Introduction   Azure Synapse Analytics Serverless SQL is a query service mostly used over the data in your data lake, for data discovery,...

2 years ago

Metadata-Based Ingestion in Synapse with Delta Lake

  Overview     The crucial first step in any ETL (extract, transform, load) process or data engineering program is ingestion, w...

3 years ago

Missing Fields Added to Dedicated SQL pool Diagnostic Settings Logs

Over the past year, customers have informed the team there were a set of key columns missing in the standalone Dedicated SQL pools (formerly S...

3 years ago

Using Azure DevOps with Synapse Workspaces to create hot fixes in production environments

Have you ever deployed a release to production only to find out a bug has escaped your testing process and now users are being severely impact...

3 years ago

Azure Synapse MVP Corner - March 2023

About this blog series Microsoft Most Valuable Professionals, or MVPs, are technology experts who passionately share their knowledge with the ...

3 years ago

Newsletter

Get the latest Dynamics 365 and Power Platform content in your inbox

A curated digest of community blogs, product news, videos, and podcasts — delivered without the noise.

Weekly updates Unsubscribe anytime Fresh community picks
We use your email only for the newsletter and you can unsubscribe at any time.
By subscribing, you agree to the privacy policy.