<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:taxo="http://purl.org/rss/1.0/modules/taxonomy/" version="2.0">
  <channel>
    <title>article How to implement Slowly Changing Dimensions when you have duplicates - Part 2: Delta Live Tables in Technical Blog</title>
    <link>https://community.databricks.com/t5/technical-blog/how-to-implement-slowly-changing-dimensions-when-you-have/ba-p/43937</link>
    <description>&lt;H3&gt;&lt;STRONG&gt;Introduction&lt;/STRONG&gt;&lt;/H3&gt;
&lt;P&gt;&lt;SPAN&gt;In Part 1 of this blog series, we explored the various types of duplicates, considerations for remediation, and the impacts of unchecked duplicated records on strategic decision-making.&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;If you want to learn more about this, please read: &lt;/SPAN&gt;&lt;A href="https://community.databricks.com/t5/technical-blog/how-to-implement-slowly-changing-dimensions-when-you-have/ba-p/40568" target="_blank" rel="noopener"&gt;How to implement Slowly Changing Dimensions when you have duplicates - Part 1: What to look out for?&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;In this blog, we will focus on leveraging &lt;/SPAN&gt;&lt;A href="https://www.databricks.com/product/delta-live-tables" target="_blank" rel="noopener"&gt;&lt;SPAN&gt;Delta Live Tables&lt;/SPAN&gt;&lt;/A&gt;&lt;SPAN&gt; pipelines as a robust solution for handling duplicates and building an efficient data pipeline to maintain your Slowly Changing Dimensions.&lt;/SPAN&gt;&lt;/P&gt;
&lt;H3&gt;&lt;STRONG&gt;Table of Contents&lt;/STRONG&gt;&lt;/H3&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;A href="#h_99442612331694082558192" target="_self"&gt;About your data pipeline&lt;/A&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;A href="#h_253153268211694082566206" target="_self"&gt;Loading your Bronze table with Databricks Auto Loader&lt;/A&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;A href="#h_85486507381694082572607" target="_self"&gt;Cleanse from other data quality issues in the Silver Layer&lt;/A&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;A href="#h_964121769541694082585144" target="_self"&gt;Maintaining your slowly changing dimensions in your Gold Layer&lt;/A&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;A href="#h_277270263691694082591221" target="_self"&gt;Dropping Intra-batch duplicates&lt;/A&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;A href="#h_420888401831694082598746" target="_self"&gt;Handling Inter-batch duplicates&lt;/A&gt;&lt;/LI&gt;
&lt;/UL&gt;
&lt;/LI&gt;
&lt;LI&gt;&lt;A href="#h_597995523961694082605592" target="_self"&gt;Configuring your Delta Live Tables pipeline&lt;/A&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;A href="#h_7002050581081694082613529" target="_self"&gt;Let’s run some tests!&lt;/A&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;A href="#h_5934493061191694082620948" target="_self"&gt;Test #1 - Initial Load with Intra-Batch Exact Duplicates&lt;/A&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;A href="#h_9513327451321694082627426" target="_self"&gt;Test #2 - Next batch with Inter-Batch, Exact Duplicates&lt;/A&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;A href="#h_5875921001411694082634968" target="_self"&gt;Test #3 - Load with Intra- &amp;amp; Inter- Batch Same Business Meaning Duplicates&lt;/A&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;A href="#h_270263761491694082641326" target="_self"&gt;Test #4 - Load with Intra- &amp;amp; Inter- Batch Out of Order Records&lt;/A&gt;&lt;/LI&gt;
&lt;/UL&gt;
&lt;/LI&gt;
&lt;LI&gt;&lt;A href="#h_4448571301561694082648876" target="_self"&gt;Conclusion&lt;/A&gt;&lt;STRONG&gt;&lt;BR /&gt;&lt;/STRONG&gt;&lt;/LI&gt;
&lt;/UL&gt;
&lt;H3&gt;&lt;STRONG&gt;About your data pipeline&lt;/STRONG&gt;&lt;/H3&gt;
&lt;P&gt;Your data pipeline implements the &lt;A href="https://www.databricks.com/glossary/medallion-architecture" target="_blank" rel="noopener"&gt;Medallion Architecture&lt;/A&gt; which loads source data into a Bronze table, applies &lt;A href="https://docs.databricks.com/delta-live-tables/expectations.html" target="_blank" rel="noopener"&gt;data quality rules and &lt;I&gt;expectations&lt;/I&gt;&lt;/A&gt; in the Silver table, and maintains the slowly changing dimensions in a Gold table.&lt;/P&gt;
&lt;P class="lia-align-center"&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Avnish_Jain_0-1694079325009.png" style="width: 720px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/3547iA181B442688C5FDB/image-dimensions/720x236?v=v2" width="720" height="236" role="button" title="Avnish_Jain_0-1694079325009.png" alt="Avnish_Jain_0-1694079325009.png" /&gt;&lt;/span&gt;&lt;FONT size="2"&gt;&lt;I&gt;&lt;SPAN&gt;Figure 1 - High level solution architecture diagram of the sample data pipeline&lt;BR /&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;/I&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;H3&gt;&lt;STRONG&gt;Loading your Bronze table with Databricks Autoloader&lt;/STRONG&gt;&lt;/H3&gt;
&lt;P&gt;&lt;SPAN&gt;The data pipeline begins with the incremental loading of source data with Databricks &lt;/SPAN&gt;&lt;A href="https://docs.databricks.com/ingestion/auto-loader/index.html" target="_blank" rel="noopener"&gt;&lt;SPAN&gt;Auto Loader&lt;/SPAN&gt;&lt;/A&gt;&lt;SPAN&gt; into a Bronze table. The Bronze table acts as the initial landing zone for incoming &lt;/SPAN&gt;&lt;A href="https://www.databricks.com/product/data-streaming" target="_blank" rel="noopener"&gt;&lt;SPAN&gt;streaming&lt;/SPAN&gt;&lt;/A&gt;&lt;SPAN&gt; data, where records are inserted into in an append-only fashion.&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;To aid with the identification of all &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;kinds&lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt; of duplicates, we will append a new column called &lt;/SPAN&gt;&lt;STRONG&gt;“&lt;/STRONG&gt;&lt;STRONG&gt;data_hash&lt;/STRONG&gt;&lt;STRONG&gt;”&lt;/STRONG&gt;&lt;SPAN&gt; which we can specify to be calculated on all columns or a select subset. Records with the same &lt;/SPAN&gt;&lt;SPAN&gt;data_hash&lt;/SPAN&gt;&lt;SPAN&gt; value will indicate that they are duplicate records.&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;If opting for a select subset of records - such as omitting technical columns to identify same business meaning duplicates - ensure you have determined and agreed this with your business stakeholders.&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;LI-CODE lang="python"&gt;import dlt
from pyspark.sql.functions import concat_ws, md5, col, current_timestamp

@dlt.table
def bronze_customers():
  return (
    spark.readStream.format("cloudFiles") \
      .option("cloudFiles.format", "csv") \
      .option("cloudFiles.inferColumnTypes", "true") \
      .option("header", "true") \
      .option("timestampFormat", "yyyy-MM-dd HH:mm:ss") \
      .load("cloud-storage-source-data-directory") \
      .withColumn("data_hash", \
        md5(concat_ws('-', col("cust_id"), \ 
        col("cust_name"), \
        col("cust_location")))) \
      .withColumn("file_name", col("_metadata.file_name")) \
      .withColumn("insert_timestamp", current_timestamp())
)&lt;/LI-CODE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;The Bronze table will store all data received from the source &lt;/SPAN&gt;&lt;STRONG&gt;including all duplicates received&lt;/STRONG&gt;&lt;SPAN&gt;. These duplicates need to be effectively processed and resolved in subsequent stages of the pipeline.&lt;BR /&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;TABLE&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD&gt;
&lt;P&gt;&lt;STRONG&gt;Note!&lt;/STRONG&gt; &lt;SPAN&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;SPAN&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;SPAN&gt;Ensure your chosen hash function is &lt;/SPAN&gt;&lt;STRONG&gt;&lt;I&gt;deterministic&lt;/I&gt;&lt;/STRONG&gt;&lt;SPAN&gt; (i.e. given the same input, the same exact output value will always be generated).&lt;/SPAN&gt;&lt;SPAN&gt;&lt;BR /&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TBODY&gt;
&lt;/TABLE&gt;
&lt;H3&gt;&lt;STRONG&gt;&lt;BR /&gt;Cleanse from other data quality issues in the Silver Layer&lt;/STRONG&gt;&lt;/H3&gt;
&lt;P&gt;&lt;SPAN&gt;The next step in the pipeline involves further data cleaning of records as &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;incrementally&lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt; received from the Bronze Table.&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;Here, we apply business data quality (DQ) rules and perform data cleaning operations in the silver table with Delta Live Tables Expectations. Expectations provides you with a simple but powerful DQ framework that will enhance the overall reliability and trustworthiness of your data pipeline.&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;LI-CODE lang="python"&gt;@dlt.table(name="silver_customers")
@dlt.expect_or_drop("valid_cust_id", "cust_id &amp;gt; 0")
def silver_customers():
  return (
    dlt.read_stream("bronze_customers")
      .select("*")
  )&lt;/LI-CODE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;To read more about DLT Expectations - please refer to the following &lt;/SPAN&gt;&lt;A href="https://docs.databricks.com/delta-live-tables/expectations.html" target="_blank" rel="noopener"&gt;&lt;SPAN&gt;documentation&lt;/SPAN&gt;&lt;/A&gt;&lt;SPAN&gt;. &lt;/SPAN&gt;&lt;/P&gt;
&lt;H3&gt;&lt;STRONG&gt;Maintaining your slowly changing dimensions in your Gold Layer&lt;/STRONG&gt;&lt;/H3&gt;
&lt;P&gt;&lt;SPAN&gt;The final stage of the data pipeline focuses on maintaining slowly changing dimensions in the Gold table which serves as the trusted source for historical analysis and decision-making. Here, we will remove the duplicates in 2 steps: first the intra-batch duplicates in a view, followed by the inter-batch duplicates.&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;H4&gt;&lt;STRONG&gt;Dropping Intra-batch duplicates&lt;/STRONG&gt;&lt;/H4&gt;
&lt;P&gt;&lt;SPAN&gt;In the below code snippet, we are leveraging the native dropDuplicates() function and are passing in the previously calculated &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;data_hash &lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt;column to handle the intra-batch duplicates.&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;This will drop any records with the same value in the&lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt; data_hash&lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt; column. As hashes are deterministic, we can safely remove intra-batch duplicate records including those we identified as having the same business meaning. However, we are still to handle inter-batch duplicates.&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;LI-CODE lang="python"&gt;@dlt.view
def gold_customers_intra_dedup():
  return (
    dlt.read_stream("silver_customers")
      .select("*")
      .dropDuplicates(["data_hash"])
  )&lt;/LI-CODE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;As we have defined this as a DLT View, data will not be persisted.&lt;/SPAN&gt;&lt;/P&gt;
&lt;H4&gt;&lt;STRONG&gt;Handling Inter-batch duplicates&lt;/STRONG&gt;&lt;/H4&gt;
&lt;P&gt;&lt;SPAN&gt;To maintain your SCD Type 1 and Type 2 dimensions, Delta Live Tables provides the APPLY CHANGES INTO syntax which streamlines the process of synchronizing data and maintaining the integrity of the SCD tables. With minimal code, it can efficiently handle any INSERT, UPDATE and/or DELETE operations required when merging changes from the source table. This provides a seamless and scalable solution for managing evolving data in real-time.&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;LI-CODE lang="python"&gt;dlt.create_streaming_table("gold_customers")

dlt.apply_changes(
  target = "gold_customers",
  source = "gold_customers_intra_dedup",
  keys = ["cust_id"],
  sequence_by = col("cust_last_updt_ts"),
  stored_as_scd_type = "2",
  track_history_column_list = ["data_hash"]
)&lt;/LI-CODE&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;Let’s walk-through what’s happening in the above code-snippet:&lt;/SPAN&gt;&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;STRONG&gt;target&lt;/STRONG&gt;&lt;STRONG&gt;:&lt;/STRONG&gt;&lt;SPAN&gt; the name of our Gold slowly changing dimension table.&lt;BR /&gt;&lt;/SPAN&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;source&lt;/STRONG&gt;&lt;STRONG&gt;:&lt;/STRONG&gt;&lt;SPAN&gt; the name of our Silver cleansed table.&lt;/SPAN&gt;&lt;SPAN&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;keys&lt;/STRONG&gt;&lt;SPAN&gt;: the column(s) used for joining to identify matching (and unmatching) rows between the target and source tables. This will be the primary key(s) of the Gold/Silver table.&lt;BR /&gt;&lt;/SPAN&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;sequence_by&lt;/STRONG&gt;&lt;STRONG&gt;: &lt;/STRONG&gt;&lt;SPAN&gt;the column that determines the logical order of records. DLT also uses this sequencing to handle change events that arrive out of order. Timestamps are commonly used such as the insert_timestamp (of when the record was ingested by your platform), or a business timestamp as received in the source data.&lt;/SPAN&gt;&lt;SPAN&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;stored_as_scd_type: &lt;/STRONG&gt;&lt;SPAN&gt;Either “1” or “2” to determine which SCD Type the target table will be maintained as.&lt;BR /&gt;&lt;/SPAN&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;SPAN&gt;&lt;STRONG&gt;track_history_column_list&lt;/STRONG&gt;&lt;STRONG&gt;:&lt;/STRONG&gt; For SCD Type 2 tables, we can specify the column(s) that are used to track for history and trigger a changed record. This is the parameter that will help us handle any &lt;I&gt;inter-batch duplicates.&amp;nbsp;&lt;/I&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;&lt;SPAN&gt;As discussed in &lt;/SPAN&gt;&lt;A href="https://community.databricks.com/t5/technical-blog/how-to-implement-slowly-changing-dimensions-when-you-have/ba-p/40568" target="_blank" rel="noopener"&gt;&lt;SPAN&gt;Part 1&lt;/SPAN&gt;&lt;/A&gt;&lt;SPAN&gt; of the blog series, there is no functional need to specifically handle inter-batch duplicates for SCD Type 1 tables as they do not maintain historical versions or track changes over time. However, inter-batch duplicates do need to be handled for SCD Type 2 tables in order to maintain data accuracy or integrity.&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;To mitigate this, the &lt;/SPAN&gt;&lt;STRONG&gt;track_history_column_list&lt;/STRONG&gt;&lt;SPAN&gt; specified on the&lt;/SPAN&gt;&lt;STRONG&gt; data_hash&lt;/STRONG&gt;&lt;SPAN&gt; column tells DLT to only generate a changed,&amp;nbsp;&lt;EM&gt;upserted&lt;/EM&gt; record when there is a different value between source and target. If the values are the same, then just rather hard-update the record (similar to an SCD Type 1).&lt;BR /&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;H3&gt;&lt;STRONG&gt;Configuring your Delta Live Tables pipeline&lt;/STRONG&gt;&lt;/H3&gt;
&lt;P&gt;&lt;SPAN&gt;To use the &lt;/SPAN&gt;&lt;STRONG&gt;track_history_column_list&lt;/STRONG&gt;&lt;SPAN&gt; in Delta Live Tables, you must explicitly enable the feature in your pipeline by adding the following configuration to your Delta Live Tables pipeline settings.&amp;nbsp;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;This can be done by adding in the below configuration in &lt;STRONG&gt;Advanced &lt;/STRONG&gt;section of the Pipeline Settings in the UI.&amp;nbsp;&lt;BR /&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Avnish_Jain_0-1694081511796.png" style="width: 999px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/3548i8F904F6E8B0EC812/image-size/large?v=v2&amp;amp;px=999" role="button" title="Avnish_Jain_0-1694081511796.png" alt="Avnish_Jain_0-1694081511796.png" /&gt;&lt;/span&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;&lt;BR /&gt;You can also modify the DLT JSON configuration:&lt;BR /&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;PRE&gt;&lt;SPAN&gt;{&lt;/SPAN&gt;&lt;BR /&gt;&lt;SPAN&gt;&amp;nbsp;&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN&gt;"configuration"&lt;/SPAN&gt;&lt;SPAN&gt;:&lt;/SPAN&gt; &lt;SPAN&gt;{&lt;/SPAN&gt;&lt;BR /&gt;&lt;SPAN&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN&gt;"pipelines.enableTrackHistory"&lt;/SPAN&gt;&lt;SPAN&gt;:&lt;/SPAN&gt; &lt;SPAN&gt;"true"&lt;/SPAN&gt;&lt;BR /&gt;&lt;SPAN&gt;&amp;nbsp;&amp;nbsp;&lt;/SPAN&gt;&lt;SPAN&gt;}&lt;/SPAN&gt;&lt;BR /&gt;&lt;SPAN&gt;}&lt;/SPAN&gt;&lt;/PRE&gt;
&lt;P&gt;&lt;STRONG&gt;And that is it!&amp;nbsp;&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;You can now save and run your DLT pipeline which will look a little like this!&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-center" image-alt="Avnish_Jain_0-1694081643022.png" style="width: 999px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/3549iC320A35A7ACDB043/image-size/large?v=v2&amp;amp;px=999" role="button" title="Avnish_Jain_0-1694081643022.png" alt="Avnish_Jain_0-1694081643022.png" /&gt;&lt;/span&gt;&lt;/P&gt;
&lt;H3&gt;&lt;STRONG&gt;Let’s run some tests!&lt;/STRONG&gt;&lt;/H3&gt;
&lt;P&gt;&lt;SPAN&gt;In this section, we will analyze the outcomes of executing the Delta Live Tables (DLT) pipeline against each test case mentioned earlier, specifically focusing on the SCD Type 2 tables&lt;/SPAN&gt;&lt;SPAN&gt;.&lt;/SPAN&gt;&lt;/P&gt;
&lt;H4&gt;&lt;STRONG&gt;&lt;BR /&gt;Test #1 - Initial Load with Intra-Batch Exact Duplicates&lt;/STRONG&gt;&lt;/H4&gt;
&lt;P&gt;&lt;SPAN&gt;Our first test will perform the initial load of our Gold SCD Type 2 table with the first batch of data containing an &lt;STRONG&gt;exact, intra-batch duplicate for John Doe.&lt;BR /&gt;&lt;BR /&gt;&lt;/STRONG&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;PRE&gt;cust_id,cust_name,cust_location,cust_last_updt_ts&lt;BR /&gt;1,Jane Smith,United States,2023-03-02 12:31:53&lt;BR /&gt;2,John Doe,Australia,2023-03-02 12:31:53&lt;BR /&gt;2,John Doe,Australia,2023-03-02 12:31:53&lt;/PRE&gt;
&lt;P&gt;&lt;SPAN&gt;After successful execution of the above code, we can see that DLT has only loaded the correct two records.&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-left" image-alt="Avnish_Jain_0-1694081886071.png" style="width: 999px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/3550i1B34F50095A19208/image-size/large?v=v2&amp;amp;px=999" role="button" title="Avnish_Jain_0-1694081886071.png" alt="Avnish_Jain_0-1694081886071.png" /&gt;&lt;/span&gt;&lt;/P&gt;
&lt;H4&gt;&amp;nbsp;&lt;/H4&gt;
&lt;H4&gt;&amp;nbsp;&lt;/H4&gt;
&lt;H4&gt;&amp;nbsp;&lt;/H4&gt;
&lt;H4&gt;&lt;STRONG&gt;&lt;BR /&gt;Test #2 - Next batch with Inter-Batch, Exact Duplicates&lt;/STRONG&gt;&lt;/H4&gt;
&lt;P&gt;&lt;SPAN&gt;Our second batch builds on after the successful completion of the first test but this time we will be loading the below batch of data containing an&lt;STRONG&gt; inter-batch, exact duplicate for Jane Smith;&lt;/STRONG&gt; and a new record for Angela.&lt;BR /&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;PRE&gt;&lt;FONT size="2"&gt;cust_id,cust_name,cust_location,cust_last_updt_ts&lt;/FONT&gt;&lt;BR /&gt;&lt;FONT size="2"&gt;1,Jane Smith,United States,2023-03-02 12:31:53&lt;/FONT&gt;&lt;BR /&gt;&lt;FONT size="2"&gt;3,Angela Hamilton,England,2023-03-02 12:31:59&lt;/FONT&gt;&lt;/PRE&gt;
&lt;P&gt;&lt;SPAN&gt;After successful execution of the above code, we can see that DLT has appropriately handled the duplicate record for Jane.&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Avnish_Jain_1-1694082079933.png" style="width: 999px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/3551iBE3F99AF295F4924/image-size/large?v=v2&amp;amp;px=999" role="button" title="Avnish_Jain_1-1694082079933.png" alt="Avnish_Jain_1-1694082079933.png" /&gt;&lt;/span&gt;&lt;/P&gt;
&lt;H4&gt;&lt;STRONG&gt;&lt;BR /&gt;Test #3 - Load with Intra- &amp;amp; Inter- Batch Same Business Meaning Duplicates&lt;/STRONG&gt;&lt;/H4&gt;
&lt;P&gt;&lt;SPAN&gt;Our next batch continues this time with &lt;/SPAN&gt;&lt;STRONG&gt;intra-&lt;/STRONG&gt;&lt;SPAN&gt; and&lt;/SPAN&gt;&lt;STRONG&gt; inter-batch duplicates&lt;/STRONG&gt;&lt;SPAN&gt; where we have received records that are not exact duplicates &lt;STRONG&gt;as the &lt;/STRONG&gt;&lt;/SPAN&gt;&lt;FONT face="courier new,courier"&gt;cust_last_updt_ts&lt;/FONT&gt;&lt;STRONG&gt; column holds different values.&lt;BR /&gt;&lt;BR /&gt;&lt;/STRONG&gt;&lt;/P&gt;
&lt;PRE&gt;cust_id,cust_name,cust_location,cust_last_updt_ts&lt;BR /&gt;3,Angela Hamilton,England,2023-03-02 13:32:00&lt;BR /&gt;4,Sachin Singh,India,2023-03-02 13:31:59&lt;BR /&gt;4,Sachin Singh,India,2023-03-02 13:32:00&lt;/PRE&gt;
&lt;P&gt;&lt;SPAN&gt;After agreeing with the business that a change only to technical columns, such as &lt;/SPAN&gt;&lt;FONT face="courier new,courier"&gt;&lt;SPAN&gt;cust_last_updt_ts&lt;/SPAN&gt;&lt;/FONT&gt;&lt;SPAN&gt;, our DLT pipeline does not trigger a new ‘change’ record in our SCD Type 2 Gold Table for Angela and only ingests the additional single record for Sachin.&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Avnish_Jain_0-1694082297092.png" style="width: 999px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/3552i8D894FDD1285DAA7/image-size/large?v=v2&amp;amp;px=999" role="button" title="Avnish_Jain_0-1694082297092.png" alt="Avnish_Jain_0-1694082297092.png" /&gt;&lt;/span&gt;&lt;/P&gt;
&lt;H4&gt;&lt;STRONG&gt;&lt;BR /&gt;Test #4 - Load with Intra- &amp;amp; Inter- Batch Out of Order Records&lt;/STRONG&gt;&lt;/H4&gt;
&lt;P&gt;&lt;SPAN&gt;Our final batch does not consist of duplicates but rather &lt;/SPAN&gt;&lt;STRONG&gt;intra-&lt;/STRONG&gt;&lt;SPAN&gt; and&lt;/SPAN&gt;&lt;STRONG&gt; inter-batch &lt;/STRONG&gt;&lt;SPAN&gt;records that have been &lt;STRONG&gt;received out of order.&lt;/STRONG&gt; Not managing these edge-cases correctly could lead to severe issues in data quality that could significantly impact decision making. &lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;SPAN&gt;In the example below, we receive a &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;previous&lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt; record for Jane Smith location prior to moving to the United States and two records for Sachin - a move to Indonesia &lt;/SPAN&gt;&lt;I&gt;&lt;SPAN&gt;before&lt;/SPAN&gt;&lt;/I&gt;&lt;SPAN&gt; his move to India, and a move after to Australia.&lt;BR /&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;/P&gt;
&lt;PRE&gt;&lt;SPAN&gt;cust_id,cust_name,cust_location,cust_last_updt_ts&lt;BR /&gt;1,Jane Smith,England,2022-01-01 00:00:00&lt;BR /&gt;4,Sachin Singh,Indonesia,2022-01-01 00:00:00&lt;BR /&gt;4,Sachin Singh,Australia,2023-06-01 00:00:00&lt;/SPAN&gt;&lt;/PRE&gt;
&lt;P&gt;&lt;SPAN&gt;As you can see from the result set below, DLT was able to remediate and stitch these records correctly into the Gold Table!&lt;/SPAN&gt;&lt;/P&gt;
&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="Avnish_Jain_1-1694082386133.png" style="width: 999px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/3553i21AB91A571004E77/image-size/large?v=v2&amp;amp;px=999" role="button" title="Avnish_Jain_1-1694082386133.png" alt="Avnish_Jain_1-1694082386133.png" /&gt;&lt;/span&gt;&lt;/P&gt;
&lt;H3&gt;&lt;STRONG&gt;&lt;BR /&gt;Conclusion&lt;/STRONG&gt;&lt;/H3&gt;
&lt;P&gt;&lt;SPAN&gt;In conclusion, leveraging &lt;/SPAN&gt;&lt;A href="https://www.databricks.com/product/delta-live-tables" target="_blank" rel="noopener"&gt;&lt;SPAN&gt;Delta Live Tables&lt;/SPAN&gt;&lt;/A&gt;&lt;SPAN&gt; enhances the robustness of your data pipelines and simplifies the management of duplicate records when implementing your slowly changing dimensions. With its provided APPLY CHANGES INTO syntax, you can effectively handle intra-batch, inter-batch, and same business meaning duplicates as well as out-of-order records that could further hinder data quality. With clean and accurate dimensions, organizations can unlock the full potential of their data to enable accurate historical analysis, data-driven decision-making, and better foster trust in the data.&lt;/SPAN&gt;&lt;/P&gt;
&lt;H3 id="toc-hId-1418975055"&gt;&lt;STRONG&gt;Coming up next!&lt;/STRONG&gt;&lt;SPAN&gt;&lt;BR /&gt;&lt;/SPAN&gt;&lt;/H3&gt;
&lt;P&gt;&lt;SPAN&gt;In Part 3 of this blog series, we will walk through a practical implementation of a &lt;A href="https://www.databricks.com/product/data-streaming" target="_self"&gt;Spark Structured Streaming&lt;/A&gt; data pipeline exposed to the same challenges!&lt;/SPAN&gt;&lt;/P&gt;</description>
    <pubDate>Fri, 29 Sep 2023 19:39:47 GMT</pubDate>
    <dc:creator>Avnish_Jain</dc:creator>
    <dc:date>2023-09-29T19:39:47Z</dc:date>
    <item>
      <title>How to implement Slowly Changing Dimensions when you have duplicates - Part 2: Delta Live Tables</title>
      <link>https://community.databricks.com/t5/technical-blog/how-to-implement-slowly-changing-dimensions-when-you-have/ba-p/43937</link>
      <description>&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="pexels-edward-jenner-4252526.jpg" style="width: 999px;"&gt;&lt;img src="https://community.databricks.com/t5/image/serverpage/image-id/3557iF2B055CDFAE7A55C/image-size/large?v=v2&amp;amp;px=999" role="button" title="pexels-edward-jenner-4252526.jpg" alt="pexels-edward-jenner-4252526.jpg" /&gt;&lt;/span&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;</description>
      <pubDate>Fri, 29 Sep 2023 19:39:47 GMT</pubDate>
      <guid>https://community.databricks.com/t5/technical-blog/how-to-implement-slowly-changing-dimensions-when-you-have/ba-p/43937</guid>
      <dc:creator>Avnish_Jain</dc:creator>
      <dc:date>2023-09-29T19:39:47Z</dc:date>
    </item>
    <item>
      <title>Re: How to implement Slowly Changing Dimensions when you have duplicates - Part 2: Delta Live Tables</title>
      <link>https://community.databricks.com/t5/technical-blog/how-to-implement-slowly-changing-dimensions-when-you-have/bc-p/50139#M64</link>
      <description>&lt;P&gt;Hey&amp;nbsp;&lt;a href="https://community.databricks.com/t5/user/viewprofilepage/user-id/46052"&gt;@Avnish_Jain&lt;/a&gt;,&amp;nbsp;we're implementing a very similar inter and intra-batch deduplication process albeit with SCD type-1. However,&amp;nbsp; we are afraid the drop_duplicates() (in your case dropDuplicates(["data_hash"])) might be looking at the whole stream of data as it could be stateful. How do we ensure, the drop_duplicates is only looking at the data inside the microbatch?&lt;/P&gt;</description>
      <pubDate>Tue, 31 Oct 2023 02:17:00 GMT</pubDate>
      <guid>https://community.databricks.com/t5/technical-blog/how-to-implement-slowly-changing-dimensions-when-you-have/bc-p/50139#M64</guid>
      <dc:creator>sandeep_gunda</dc:creator>
      <dc:date>2023-10-31T02:17:00Z</dc:date>
    </item>
    <item>
      <title>Re: How to implement Slowly Changing Dimensions when you have duplicates - Part 2: Delta Live Tables</title>
      <link>https://community.databricks.com/t5/technical-blog/how-to-implement-slowly-changing-dimensions-when-you-have/bc-p/50924#M70</link>
      <description>&lt;P&gt;Thank you for this, when is Part 3 which includes Spark Structured Streaming due?&amp;nbsp;&lt;/P&gt;</description>
      <pubDate>Sat, 11 Nov 2023 05:46:28 GMT</pubDate>
      <guid>https://community.databricks.com/t5/technical-blog/how-to-implement-slowly-changing-dimensions-when-you-have/bc-p/50924#M70</guid>
      <dc:creator>rantav</dc:creator>
      <dc:date>2023-11-11T05:46:28Z</dc:date>
    </item>
  </channel>
</rss>

