From 54d040751d3a787266f3ae3b846673a05e5a8ff7 Mon Sep 17 00:00:00 2001
From: shyamraodb <129457417+shyamraodb@users.noreply.github.com>
Date: Tue, 25 Feb 2025 16:37:21 -0800
Subject: [PATCH 01/43] Create readme
---
product_demos/DBSQL-Datawarehousing/dbsql-for-etl/readme | 1 +
1 file changed, 1 insertion(+)
create mode 100644 product_demos/DBSQL-Datawarehousing/dbsql-for-etl/readme
diff --git a/product_demos/DBSQL-Datawarehousing/dbsql-for-etl/readme b/product_demos/DBSQL-Datawarehousing/dbsql-for-etl/readme
new file mode 100644
index 00000000..8b137891
--- /dev/null
+++ b/product_demos/DBSQL-Datawarehousing/dbsql-for-etl/readme
@@ -0,0 +1 @@
+
From ed4caf6f6f920425cb89b62d73ed83e15845579e Mon Sep 17 00:00:00 2001
From: shyamraodb <129457417+shyamraodb@users.noreply.github.com>
Date: Tue, 25 Feb 2025 16:38:28 -0800
Subject: [PATCH 02/43] Delete
product_demos/DBSQL-Datawarehousing/dbsql-for-etl directory
---
product_demos/DBSQL-Datawarehousing/dbsql-for-etl/readme | 1 -
1 file changed, 1 deletion(-)
delete mode 100644 product_demos/DBSQL-Datawarehousing/dbsql-for-etl/readme
diff --git a/product_demos/DBSQL-Datawarehousing/dbsql-for-etl/readme b/product_demos/DBSQL-Datawarehousing/dbsql-for-etl/readme
deleted file mode 100644
index 8b137891..00000000
--- a/product_demos/DBSQL-Datawarehousing/dbsql-for-etl/readme
+++ /dev/null
@@ -1 +0,0 @@
-
From a185326b804094dbba0daed1249b1fc5c51420b2 Mon Sep 17 00:00:00 2001
From: shyamraodb <129457417+shyamraodb@users.noreply.github.com>
Date: Tue, 25 Feb 2025 16:38:50 -0800
Subject: [PATCH 03/43] Create readme
---
product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/readme | 1 +
1 file changed, 1 insertion(+)
create mode 100644 product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/readme
diff --git a/product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/readme b/product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/readme
new file mode 100644
index 00000000..8b137891
--- /dev/null
+++ b/product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/readme
@@ -0,0 +1 @@
+
From 4d991f6b3b149488ad6a9fee835cd1633d849573 Mon Sep 17 00:00:00 2001
From: shyamraodb <129457417+shyamraodb@users.noreply.github.com>
Date: Tue, 25 Feb 2025 17:36:28 -0800
Subject: [PATCH 04/43] Create _util
---
product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/_util | 1 +
1 file changed, 1 insertion(+)
create mode 100644 product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/_util
diff --git a/product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/_util b/product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/_util
new file mode 100644
index 00000000..8b137891
--- /dev/null
+++ b/product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/_util
@@ -0,0 +1 @@
+
From 6dbf4bb6cfa96fe7e1d7bb46e3b67cfdb254d45a Mon Sep 17 00:00:00 2001
From: shyamraodb <129457417+shyamraodb@users.noreply.github.com>
Date: Tue, 25 Feb 2025 17:36:56 -0800
Subject: [PATCH 05/43] Delete
product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/_util
---
product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/_util | 1 -
1 file changed, 1 deletion(-)
delete mode 100644 product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/_util
diff --git a/product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/_util b/product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/_util
deleted file mode 100644
index 8b137891..00000000
--- a/product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/_util
+++ /dev/null
@@ -1 +0,0 @@
-
From 18e47c179cdc8e5786096444e16006798ad11211 Mon Sep 17 00:00:00 2001
From: shyamraodb <129457417+shyamraodb@users.noreply.github.com>
Date: Tue, 25 Feb 2025 17:40:32 -0800
Subject: [PATCH 06/43] Add files via upload
---
.../dbsql-for-dim-etl/00-Setup/Initialize.sql | 50 ++
.../dbsql-for-dim-etl/00-Setup/Setup.sql | 39 ++
.../01-Create/Code Table.sql | 38 ++
.../01-Create/ETL Log Table.sql | 22 +
.../01-Create/Patient Tables.sql | 150 ++++++
.../01-Patient-Dimension-ETL-Introduction.sql | 90 ++++
.../02-Create-SQL-Warehouse-Workflows-Job.py | 329 ++++++++++++
.../Patient Dimension ETL w CDF.sql | 486 ++++++++++++++++++
.../02-Populate/Patient Dimension ETL.sql | 453 ++++++++++++++++
.../dbsql-for-dim-etl/04-Utility/Log Run.sql | 26 +
.../_util/browse current load.sql | 54 ++
.../_util/initialize-staging.py | 46 ++
...stage source file - incremental load #1.py | 28 +
.../_util/stage source file - initial load.py | 30 ++
14 files changed, 1841 insertions(+)
create mode 100644 product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/00-Setup/Initialize.sql
create mode 100644 product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/00-Setup/Setup.sql
create mode 100644 product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/01-Create/Code Table.sql
create mode 100644 product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/01-Create/ETL Log Table.sql
create mode 100644 product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/01-Create/Patient Tables.sql
create mode 100644 product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/01-Patient-Dimension-ETL-Introduction.sql
create mode 100644 product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/02-Create-SQL-Warehouse-Workflows-Job.py
create mode 100644 product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/02-Populate/Patient Dimension ETL w CDF.sql
create mode 100644 product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/02-Populate/Patient Dimension ETL.sql
create mode 100644 product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/04-Utility/Log Run.sql
create mode 100644 product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/_util/browse current load.sql
create mode 100644 product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/_util/initialize-staging.py
create mode 100644 product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/_util/stage source file - incremental load #1.py
create mode 100644 product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/_util/stage source file - initial load.py
diff --git a/product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/00-Setup/Initialize.sql b/product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/00-Setup/Initialize.sql
new file mode 100644
index 00000000..e785dd99
--- /dev/null
+++ b/product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/00-Setup/Initialize.sql
@@ -0,0 +1,50 @@
+-- Databricks notebook source
+-- MAGIC %md-sandbox
+-- MAGIC **Configure settings**
+-- MAGIC
+-- MAGIC 1. Specify Catalog to create demo schemas
+-- MAGIC
+-- MAGIC 2. Specify Schema to create data warehouse tables, staging volume
+-- MAGIC
+-- MAGIC 3. Specify whether to enable Predictive Optimization for DW schema
+-- MAGIC
+-- MAGIC NOTE:
+-- MAGIC The catalog and schema can be create beforehand. If not, ensure that the user running the workflow has permissions to create catalog and schema.
+
+-- COMMAND ----------
+
+-- DBTITLE 1,dimension schema
+/*
+Manually update the following, to use a different catalog / schema:
+*/
+
+declare or replace variable catalog_nm string = 'dbsqldemos';
+declare or replace variable schema_nm string = 'clinical_star';
+
+-- COMMAND ----------
+
+-- enable PO at schema level? else inherit from account setting
+declare or replace variable enable_po_for_schema boolean = true;
+
+-- COMMAND ----------
+
+-- MAGIC %md
+-- MAGIC **Additional settings**
+
+-- COMMAND ----------
+
+declare or replace variable run_log_table string;
+declare or replace variable code_table string;
+
+-- COMMAND ----------
+
+set variable (run_log_table, code_table) = (select catalog_nm || '.' || schema_nm || '.' || 'elt_run_log', catalog_nm || '.' || schema_nm || '.' || 'code_m');
+
+-- COMMAND ----------
+
+declare or replace variable volume_name string = 'staging';
+
+-- COMMAND ----------
+
+declare or replace variable staging_path string;
+set variable staging_path = '/Volumes/' || catalog_nm || "/" || schema_nm || "/" || volume_name;
\ No newline at end of file
diff --git a/product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/00-Setup/Setup.sql b/product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/00-Setup/Setup.sql
new file mode 100644
index 00000000..e39c4747
--- /dev/null
+++ b/product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/00-Setup/Setup.sql
@@ -0,0 +1,39 @@
+-- Databricks notebook source
+-- MAGIC %run "./Initialize"
+
+-- COMMAND ----------
+
+declare or replace variable sqlstr string; -- variable to hold any sql statement for EXECUTE IMMEDIATE
+
+-- COMMAND ----------
+
+-- MAGIC %md
+-- MAGIC Create Catalog and Schema(s) if required
+
+-- COMMAND ----------
+
+set variable sqlstr = "create catalog if not exists " || catalog_nm;
+execute immediate sqlstr;
+
+-- COMMAND ----------
+
+set variable sqlstr = "create schema if not exists " || catalog_nm || "." || schema_nm;
+
+-- COMMAND ----------
+
+execute immediate sqlstr;
+
+-- COMMAND ----------
+
+set variable sqlstr = "alter schema " || catalog_nm || "." || schema_nm || if(enable_po_for_schema, ' enable', ' inherit') || ' predictive optimization';
+execute immediate sqlstr;
+
+-- COMMAND ----------
+
+-- MAGIC %md
+-- MAGIC Create Volume for staging source data files
+
+-- COMMAND ----------
+
+set variable sqlstr = "create volume if not exists " || catalog_nm || "." || schema_nm || "." || volume_name;
+execute immediate sqlstr;
\ No newline at end of file
diff --git a/product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/01-Create/Code Table.sql b/product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/01-Create/Code Table.sql
new file mode 100644
index 00000000..e1ac1bb5
--- /dev/null
+++ b/product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/01-Create/Code Table.sql
@@ -0,0 +1,38 @@
+-- Databricks notebook source
+-- MAGIC %run "../00-Setup/Initialize"
+
+-- COMMAND ----------
+
+-- MAGIC %md
+-- MAGIC # Create Table
+
+-- COMMAND ----------
+
+-- MAGIC %md
+-- MAGIC ##Master Data
+-- MAGIC Standardized codes used for coded attributes
+
+-- COMMAND ----------
+
+drop table if exists identifier(code_table);
+
+-- COMMAND ----------
+
+-- LC options - m_code, m_type
+
+create table identifier(code_table) (
+ m_code string comment 'code',
+ m_desc string comment 'name or description for the code',
+ m_type string comment 'attribute type utilizing code'
+)
+comment 'master table for coded attributes'
+
+-- COMMAND ----------
+
+insert into identifier(code_table)
+values
+ ('M', 'Male', 'GENDER'),
+ ('F', 'Female', 'GENDER'),
+ ('hispanic', 'Hispanic', 'ETHNICITY'),
+ ('nonhispanic', 'Not Hispanic', 'ETHNICITY')e
+;
\ No newline at end of file
diff --git a/product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/01-Create/ETL Log Table.sql b/product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/01-Create/ETL Log Table.sql
new file mode 100644
index 00000000..89bda650
--- /dev/null
+++ b/product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/01-Create/ETL Log Table.sql
@@ -0,0 +1,22 @@
+-- Databricks notebook source
+-- MAGIC %run "../00-Setup/Initialize"
+
+-- COMMAND ----------
+
+-- MAGIC %md
+-- MAGIC # Create Tables
+
+-- COMMAND ----------
+
+-- MAGIC %md
+-- MAGIC ## Config/Log Table for ELT
+-- MAGIC This table captures the metadata for a given table that includes the table name, load start time and load end time.
+
+-- COMMAND ----------
+
+drop table if exists identifier(run_log_table);
+
+-- COMMAND ----------
+
+create table identifier(run_log_table) (data_source string, table_name string, load_start_time timestamp, locked boolean, load_end_time timestamp, num_inserts int, num_updates int, process_id string)
+;
\ No newline at end of file
diff --git a/product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/01-Create/Patient Tables.sql b/product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/01-Create/Patient Tables.sql
new file mode 100644
index 00000000..f0bd86f6
--- /dev/null
+++ b/product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/01-Create/Patient Tables.sql
@@ -0,0 +1,150 @@
+-- Databricks notebook source
+-- MAGIC %run "../00-Setup/Initialize"
+
+-- COMMAND ----------
+
+declare or replace variable br_table string; -- staging/bronze table identifier
+declare or replace variable si_table string; -- integration/silver table identifier
+declare or replace variable gd_table string; -- dimension table identifier
+
+-- COMMAND ----------
+
+declare or replace variable sqlstr string;
+
+-- COMMAND ----------
+
+set variable (br_table, si_table, gd_table) = (select catalog_nm || '.' || schema_nm || '.' || 'patient_stg', catalog_nm || '.' || schema_nm || '.' || 'patient_int', catalog_nm || '.' || schema_nm || '.' || 'g_patient_d');
+
+-- COMMAND ----------
+
+-- MAGIC %md
+-- MAGIC
+-- MAGIC # Create Tables
+-- MAGIC Create the staging, integration, and dimension tables for patient.
+-- MAGIC The patient dimension is part of the clinical data warehouse (star schema).
+-- MAGIC
+-- MAGIC NOTE: By default, the tables are created in the **catalog dbsqldemos**. To change this, or specify an existing catalog / schema, please see [Configure notebook]($../00-Setup/Configure) for more context.
+
+-- COMMAND ----------
+
+-- MAGIC %md
+-- MAGIC ## Create Staging Table
+-- MAGIC The schema for the staging table will be derived from the source data file(s)
+
+-- COMMAND ----------
+
+drop table if exists identifier(br_table);
+
+-- COMMAND ----------
+
+create table if not exists identifier(br_table)
+comment 'Patient staging table ingesting initial and incremental master data from csv files'
+;
+
+-- COMMAND ----------
+
+-- MAGIC %md
+-- MAGIC ## Create Integration Table
+
+-- COMMAND ----------
+
+drop table if exists identifier(si_table);
+
+-- COMMAND ----------
+
+-- MAGIC %md
+-- MAGIC Potential clustering columns - (data_source, patient_src_id)
+-- MAGIC Also, column src_changed_on_dt will be naturally ordered (ingestion-time clustering) AND data_source will typically be the same for all records in a source file.
+-- MAGIC
+-- MAGIC **Note:** Take advantage of Predictive Optimization (to maintain) and Auto clustering (to automatically cluster your tables based on your evolving workload)!
+-- MAGIC
+-- MAGIC
+-- MAGIC
+
+-- COMMAND ----------
+
+create table if not exists identifier(si_table) (
+ patient_src_id string not null comment 'ID of the record in the source',
+ date_of_birth date comment 'date of birth',
+ ssn string comment 'social security number',
+ drivers_license string comment 'driver\'s license',
+ name_prefix string comment 'name prefix',
+ first_name string comment 'first name of patient',
+ last_name string not null comment 'last name of patient',
+ name_suffix string comment 'name suffix',
+ maiden_name string comment 'maiden name',
+ gender_cd string comment 'code for patient\'s gender',
+ gender_nm string comment 'description of patient\'s gender',
+ marital_status string comment 'marital status',
+ ethnicity_cd string comment 'code for patient\'s ethnicity',
+ ethnicity_nm string comment 'description of patient\'s ethnicity',
+ src_changed_on_dt timestamp comment 'date of last change to record in source',
+ data_source string not null comment 'code for source system',
+ insert_dt timestamp comment 'date record inserted',
+ update_dt timestamp comment 'date record updated',
+ process_id string comment 'Process ID for run',
+ constraint c_int_pk primary key (patient_src_id, data_source) RELY
+)
+comment 'curated integration table for patient data'
+tblproperties (delta.enableChangeDataFeed = true)
+;
+
+-- COMMAND ----------
+
+-- MAGIC %md
+-- MAGIC ## Create Dimension
+
+-- COMMAND ----------
+
+drop table if exists identifier(gd_table);
+
+-- COMMAND ----------
+
+-- MAGIC %md
+-- MAGIC Potential clustering columns - attributes used for filtering in end-user queries. For e.g., Last Name, Gender Code.
+-- MAGIC
+-- MAGIC Additionally, for large dimensions, using the Source ID (patient_src_id) as a cluster key may help with ETL performance.
+-- MAGIC
+-- MAGIC **Note:** For the dimension table, take advantage of Predictive Optimization (to maintain) and Auto clustering (to automatically cluster your tables based on your evolving workload)!
+
+-- COMMAND ----------
+
+create table if not exists identifier(gd_table) (
+ patient_sk bigint generated always as identity comment 'Primary Key (ID)',
+ last_name string NOT NULL comment 'Last name of the person',
+ first_name string NOT NULL comment 'First name of the person',
+ name_prefix string comment 'Prefix of person name',
+ name_suffix string comment 'Suffix of person name',
+ maiden_name string comment 'Maiden name',
+ gender_code string comment 'Gender code',
+ gender string comment 'gender description',
+ date_of_birth timestamp comment 'Birth date and time',
+ marital_status string comment 'Marital status',
+ ethnicity_code string,
+ ethnicity string,
+ ssn string comment 'Patient SSN',
+ other_identifiers map comment 'Identifier type (passport number, license number except mrn, ssn) and value',
+ uda map comment 'User Defined Attributes',
+ patient_src_id string comment 'Unique reference to the source record',
+ effective_start_date timestamp comment 'SCD2 effective start date for version',
+ effective_end_date timestamp comment 'SCD2 effective start date for version',
+ checksum string comment 'Checksum for the record',
+ data_source string comment 'Code for source system',
+ insert_dt timestamp comment 'record inserted time',
+ update_dt timestamp comment 'record updated time',
+ process_id string comment 'Process ID for run',
+ constraint c_d_pk primary key (patient_sk) RELY
+)
+cluster by auto
+comment 'Patient dimension'
+tblproperties (
+ delta.deletedFileRetentionDuration = 'interval 30 days'
+)
+;
+
+
+-- COMMAND ----------
+
+-- FK to integration table
+set variable sqlstr = 'alter table ' || gd_table || ' add constraint c_d_int_source_fk foreign key (patient_src_id, data_source) references ' || si_table || '(patient_src_id, data_source) not enforced rely';
+execute immediate sqlstr;
\ No newline at end of file
diff --git a/product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/01-Patient-Dimension-ETL-Introduction.sql b/product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/01-Patient-Dimension-ETL-Introduction.sql
new file mode 100644
index 00000000..6a1dd15f
--- /dev/null
+++ b/product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/01-Patient-Dimension-ETL-Introduction.sql
@@ -0,0 +1,90 @@
+-- Databricks notebook source
+-- MAGIC %md-sandbox
+-- MAGIC # Demo: Create and Populate Patient Dimension
+-- MAGIC
+-- MAGIC The demo will illustrate the data architecture and data workflow that creates and populates a dimension in a Star Schema using **Databricks SQL**.
+-- MAGIC This will utilize a Patient dimension in the Healthcare domain.
+-- MAGIC
+-- MAGIC The demo will illustrate all facets of an end-to-end ETL to transform, validate, and load an SCD2 dimension.
+-- MAGIC
+-- MAGIC NOTE: The ETL assumes that the source data is extracted to cloud storage as incremental CSV files.
+-- MAGIC
+
+-- COMMAND ----------
+
+-- MAGIC %md
+-- MAGIC ## What we will build
+-- MAGIC
+-- MAGIC #### This end-to-end demo builds a Databricks Workflows Job that will perform the following tasks:
+-- MAGIC
+-- MAGIC **1. Create Tables**
+-- MAGIC
+-- MAGIC A. Global Configuration
+-- MAGIC - **ETL Log table**: This table captures the runtime metadata for a table that includes the table name, load start time and load end time.
+-- MAGIC
+-- MAGIC _See [Create Config Table notebook]($./01-Create/Config Table) to review._
+-- MAGIC
+-- MAGIC B. Standardization
+-- MAGIC - **Codes table**: Master table initialized with standardized codes used for coded attributes in the schema.
+-- MAGIC
+-- MAGIC _See [Create Code Table notebook]($./01-Create/Code Table) to review._
+-- MAGIC
+-- MAGIC C. Patient tables
+-- MAGIC - **Patient Staging table**
+-- MAGIC - **Patient Integration table
**
+-- MAGIC - **Patient Dimension table
**
+-- MAGIC
+-- MAGIC _See [Create Patient Tables notebook]($./01-Create/Patient Tables) to review._
+-- MAGIC
+-- MAGIC **2. Stage Initial Data**
+-- MAGIC This task will download an initial CSV file with patient data onto a staging Volume.
+-- MAGIC
+-- MAGIC **3. Patient load**
+-- MAGIC This will initiate the ETL which will read new files from the staging Volume and populate the staging, integration, and patient dimension tables.
+-- MAGIC
+-- MAGIC **4. Stage Incremental Data**
+-- MAGIC This task will download 2 incremental CSV files with patient data onto the staging Volume.
+-- MAGIC
+-- MAGIC **5. Patient load**
+-- MAGIC This will initiate the ETL which will read new files from the staging Volume and populate the staging, integration, and patient dimension tables.
+-- MAGIC
+-- MAGIC _See [Patient Dimension ETL notebook]($./02-Populate/Patient Dimension ETL) to review._
+-- MAGIC
+-- MAGIC
+-- MAGIC
+-- MAGIC You can also browse the results of each ETL run. This will show the data that is present in the log, exceptions, and patient tables, as it appears at the end of the initial load and each incremental load. Click on each of the 'Browse Results' task.
+
+-- COMMAND ----------
+
+-- MAGIC %md-sandbox
+-- MAGIC ## Patient tables
+-- MAGIC
+-- MAGIC 
+-- MAGIC
+-- MAGIC You can view the tables within the catalog.schema that is specified in notebook 00-Setup/Initialize.
+
+-- COMMAND ----------
+
+-- MAGIC %md-sandbox
+-- MAGIC ## Sample Source Data
+-- MAGIC
+-- MAGIC 
+
+-- COMMAND ----------
+
+-- MAGIC %md-sandbox
+-- MAGIC ## Data Flow
+-- MAGIC
+-- MAGIC 
+
+-- COMMAND ----------
+
+-- MAGIC %md
+-- MAGIC #Create Workflows Job
+-- MAGIC
+-- MAGIC Open and Run notebook [02-Create-SQL-Warehouse-Workflows-Job]($./02-Create-SQL-Warehouse-Workflows-Job)
+
+-- COMMAND ----------
+
+-- MAGIC %md
+-- MAGIC
\ No newline at end of file
diff --git a/product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/02-Create-SQL-Warehouse-Workflows-Job.py b/product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/02-Create-SQL-Warehouse-Workflows-Job.py
new file mode 100644
index 00000000..46943a38
--- /dev/null
+++ b/product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/02-Create-SQL-Warehouse-Workflows-Job.py
@@ -0,0 +1,329 @@
+# Databricks notebook source
+# MAGIC %md
+# MAGIC #View the Introduction notebook
+# MAGIC
+# MAGIC If not already viewed, browse through [01-Patient-Dimension-ETL-Introduction]($./01-Patient-Dimension-ETL-Introduction)
+
+# COMMAND ----------
+
+# MAGIC %md
+# MAGIC #What will be create
+# MAGIC 1. Serverless SQL Warehouse
+# MAGIC If **not previously created** (with name staring "dbsqldemo-patient-dimension-etl-")
+# MAGIC 2. New Workflows Job to run the ETL demo
+# MAGIC
+# MAGIC ##Required Privileges
+# MAGIC
+# MAGIC To run the demo, the following privileges are required:
+# MAGIC 1. Create Catalog, Schema, Table, Volume
+# MAGIC Note that the privilege to Create Catalog and Schema is not required if using precreated objects. See [Configure notebook]($./00-Setup/Configure)
+# MAGIC 2. Create SQL Warehouse, Workflows Job
+# MAGIC
+# MAGIC Additionally, the data files are downloaded via the internet.
+# MAGIC
+# MAGIC
+
+# COMMAND ----------
+
+# MAGIC %md
+# MAGIC #Initialize
+
+# COMMAND ----------
+
+import requests
+from requests import Response
+import time
+
+# COMMAND ----------
+
+def get_dbutils_tags_safe():
+ import json
+ return json.loads(dbutils.notebook.entry_point.getDbutils().notebook().getContext().safeToJson())['attributes']
+
+def get_current_url():
+ try:
+ return "https://"+dbutils.notebook.entry_point.getDbutils().notebook().getContext().browserHostName().get()
+ except:
+ try:
+ return "https://"+get_dbutils_tags_safe()['browserHostName']
+ except:
+ return "local"
+
+
+# COMMAND ----------
+
+def get_current_pat_token():
+ try:
+ token = dbutils.notebook.entry_point.getDbutils().notebook().getContext().apiToken().get()
+ except Exception as e:
+ raise Exception("Couldn't get a PAT Token: "+str(e))
+ if len(token) == 0:
+ raise Exception("Empty PAT Token.")
+ return token
+
+# COMMAND ----------
+
+def get_json_result(url: str, r: Response, print_auth_error = True):
+ if r.status_code == 403:
+ if print_auth_error:
+ print(f"Unauthorized call. Check your PAT token {r.text} - {r.url} - {url}")
+ try:
+ return r.json()
+ except Exception as e:
+ print(f"API CALL ERROR - can't read json. status: {r.status_code} {r.text} - URL: {url} - {e}")
+ raise e
+
+
+# COMMAND ----------
+
+def post(url: str, headers: str, json: dict = {}, retry = 0):
+ with requests.post(url, headers = headers, json=json, timeout=60) as r:
+ #TODO: should add that in the requests lib adapter for all requests.
+ if r.status_code == 429 and retry < 2:
+ import time
+ import random
+ wait_time = 15 * (retry+1) + random.randint(2*retry, 10*retry)
+ print(f'WARN: hitting api request limit 429 error: {url}. Sleeping {wait_time}sec and retrying...')
+ time.sleep(wait_time)
+ print('Retrying call.')
+ return post(url, headers, json, retry+1)
+ else:
+ return get_json_result(url, r)
+
+# COMMAND ----------
+
+# MAGIC %md
+# MAGIC #Create SQL Warehouse
+# MAGIC
+# MAGIC If one doesn't exist for this demo
+
+# COMMAND ----------
+
+from databricks.sdk import WorkspaceClient
+from databricks.sdk.service import sql
+
+# Initialize the WorkspaceClient
+w = WorkspaceClient()
+
+all_wh = w.warehouses.list()
+demo_wh_exist = [wh for wh in all_wh if wh.name.startswith("dbsqldemo-patient-dimension-etl-")]
+
+if len(demo_wh_exist) > 0:
+ warehouse_id = demo_wh_exist[0].id
+
+ print(f"Using existing warehouse: {demo_wh_exist[0].name} with id: {warehouse_id}")
+else:
+ # create new
+ wh_name = f'dbsqldemo-patient-dimension-etl-{time.time_ns()}'
+
+ # Create SQL warehouse
+ created = w.warehouses.create(
+ name=wh_name,
+ cluster_size="Small",
+ max_num_clusters=1,
+ auto_stop_mins=1,
+ tags=sql.EndpointTags(
+ custom_tags=[sql.EndpointTagPair(key="purpose", value="dbsqldemo")]
+ )
+ ).result()
+
+ warehouse_id = created.id
+
+ print(f"Created warehouse: {wh_name} with id: {warehouse_id}")
+
+# COMMAND ----------
+
+# MAGIC %md
+# MAGIC #Create Job
+# MAGIC
+# MAGIC Rough edge: warehouse_id parameter to NotebookTask not working at this time. Hence using API.
+
+# COMMAND ----------
+
+nb_path = dbutils.entry_point.getDbutils().notebook().getContext().notebookPath().getOrElse(None)
+nb_dir = "/Workspace" + "/".join(nb_path.split("/")[:-1]) + "/"
+
+task_setup_catalog_schema = {
+ "task_key": "SETUP_CATALOG",
+ "notebook_task": {
+ "notebook_path": nb_dir + "00-Setup/Setup",
+ "warehouse_id": warehouse_id
+ }
+}
+
+task_create_code_table = {
+ "task_key": "CREATE_TABLE_CODE",
+ "depends_on": [
+ {
+ "task_key": "SETUP_CATALOG"
+ }
+ ],
+ "run_if": "ALL_SUCCESS",
+ "notebook_task": {
+ "notebook_path": nb_dir + "01-Create/Code Table",
+ "warehouse_id": warehouse_id
+ }
+}
+
+task_create_config_table = {
+ "task_key": "CREATE_TABLE_CONFIG",
+ "depends_on": [
+ {
+ "task_key": "SETUP_CATALOG"
+ }
+ ],
+ "run_if": "ALL_SUCCESS",
+ "notebook_task": {
+ "notebook_path": nb_dir + "01-Create/ETL Log Table",
+ "warehouse_id": warehouse_id
+ }
+}
+
+task_create_patient_tables = {
+ "task_key": "CREATE_TABLE_PATIENT",
+ "depends_on": [
+ {
+ "task_key": "SETUP_CATALOG"
+ }
+ ],
+ "run_if": "ALL_SUCCESS",
+ "notebook_task": {
+ "notebook_path": nb_dir + "01-Create/Patient Tables",
+ "warehouse_id": warehouse_id
+ }
+}
+
+task_stage_source_file_initial = {
+ "task_key": "demo_StgSrcFileInit",
+ "depends_on": [
+ {
+ "task_key": "SETUP_CATALOG"
+ }
+ ],
+ "run_if": "ALL_SUCCESS",
+ "notebook_task": {
+ "notebook_path": nb_dir + "_util/stage source file - initial load",
+ }
+}
+
+task_patient_initial_load = {
+ "task_key": "INITIAL_LOAD_PATIENT",
+ "description": "Initial load of Patient Tables",
+ "depends_on": [
+ {
+ "task_key": "CREATE_TABLE_PATIENT"
+ },
+ {
+ "task_key": "CREATE_TABLE_CODE"
+ },
+ {
+ "task_key": "CREATE_TABLE_CONFIG"
+ },
+ {
+ "task_key": "demo_StgSrcFileInit"
+ }
+ ],
+ "run_if": "ALL_SUCCESS",
+ "notebook_task": {
+ "notebook_path": nb_dir + "02-Populate/Patient Dimension ETL",
+ "base_parameters": {
+ "p_process_id": "{{job.id}}-{{job.run_id}}"
+ },
+ "warehouse_id": warehouse_id
+ },
+}
+
+task_browse_result_initial_load = {
+ "task_key": "demo_BrowseResultInit",
+ "description": "Browse results of the initial load",
+ "depends_on": [
+ {
+ "task_key": "INITIAL_LOAD_PATIENT"
+ }
+ ],
+ "run_if": "ALL_SUCCESS",
+ "notebook_task": {
+ "notebook_path": nb_dir + "_util/browse current load",
+ "warehouse_id": warehouse_id
+ }
+}
+
+task_stage_source_file_incr_1 = {
+ "task_key": "demo_StgSrcFileIncr",
+ "depends_on": [
+ {
+ "task_key": "INITIAL_LOAD_PATIENT"
+ }
+ ],
+ "run_if": "ALL_SUCCESS",
+ "notebook_task": {
+ "notebook_path": nb_dir + "_util/stage source file - incremental load #1",
+ }
+}
+
+task_patient_load_incr1 = {
+ "task_key": "INCREMENTAL_LOAD_PATIENT",
+ "depends_on": [
+ {
+ "task_key": "demo_StgSrcFileIncr"
+ }
+ ],
+ "run_if": "ALL_SUCCESS",
+ "notebook_task": {
+ "notebook_path": nb_dir + "02-Populate/Patient Dimension ETL",
+ "base_parameters": {
+ "p_process_id": "{{job.id}}-{{job.run_id}}"
+ },
+ "warehouse_id": warehouse_id
+ }
+}
+
+task_browse_result_incr1 = {
+ "task_key": "demo_BrowseResultIncr",
+ "depends_on": [
+ {
+ "task_key": "INCREMENTAL_LOAD_PATIENT"
+ }
+ ],
+ "run_if": "ALL_SUCCESS",
+ "notebook_task": {
+ "notebook_path": nb_dir + "_util/browse current load",
+ "warehouse_id": warehouse_id
+ }
+}
+
+tasks = [task_setup_catalog_schema, task_create_code_table, task_create_config_table, task_create_patient_tables, task_stage_source_file_initial, task_patient_initial_load, task_browse_result_initial_load, task_stage_source_file_incr_1, task_patient_load_incr1, task_browse_result_incr1]
+
+json = {
+ "name": f"dbsqldemo-patient-dimension-etl-{time.time_ns()}",
+ "tasks": tasks,
+ "format": "MULTI_TASK"
+}
+
+
+# COMMAND ----------
+
+pat_token = get_current_pat_token()
+headers = {"Authorization": "Bearer " + pat_token, 'Content-type': 'application/json', 'User-Agent': 'dbsqldemos'}
+workspace_url = get_current_url()
+
+job = post(workspace_url+"/api/2.2/jobs/create", headers, json)
+
+print(job)
+
+# COMMAND ----------
+
+# MAGIC %md
+# MAGIC #Access the ETL Job
+# MAGIC
+# MAGIC As mentioned in the Introduction, the JOB:
+# MAGIC 1. Creates the config and patient tables (including the dimension table).
+# MAGIC 2. Runs the ETL 3 times, to showcase the various features: a) Initial Load b) Incremental Load #1 c) Incremental Load #3.
+# MAGIC 3. Includes the "browse_..." tasks after each load. Click on the task to view the results of the corresponding load.
+# MAGIC
+# MAGIC **Run the Job and view the results in each of the "browse ..." tasks**
+# MAGIC
+
+# COMMAND ----------
+
+print(f"Demo workflow available: {get_current_url()}/#job/{job['job_id']}/tasks")
\ No newline at end of file
diff --git a/product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/02-Populate/Patient Dimension ETL w CDF.sql b/product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/02-Populate/Patient Dimension ETL w CDF.sql
new file mode 100644
index 00000000..02eca3dc
--- /dev/null
+++ b/product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/02-Populate/Patient Dimension ETL w CDF.sql
@@ -0,0 +1,486 @@
+-- Databricks notebook source
+-- MAGIC %md-sandbox
+-- MAGIC # Patient Dimension ETL
+-- MAGIC This notebook contains the code to load the patient dimension which is part of the clinical star schema.
+-- MAGIC The same pattern can be used to load any of your business dimensions.
+-- MAGIC
+-- MAGIC **Initial and Incremental load of Patient dimension**
+-- MAGIC The notebook performs the following tasks:
+-- MAGIC -> Load staging table
+-- MAGIC -> Curate and load integration table
+-- MAGIC -> Transform and load dimension table using SCD2
+-- MAGIC
+-- MAGIC The staging table is loaded from files extracted to cloud storage.
+-- MAGIC These files contain incremental data extracts.
+-- MAGIC Zero, one, or more new files loaded during each run.
+
+-- COMMAND ----------
+
+-- MAGIC %md
+-- MAGIC **The code incorporates the following design elements:**
+-- MAGIC - Versioning of data (SCD Type 2)
+-- MAGIC - Checksum
+-- MAGIC - Code standardization
+-- MAGIC
+-- MAGIC Simply re-run Job to recover from a runtime error.
+-- MAGIC
+-- MAGIC
+-- MAGIC _The code uses temporary views and single DML for each of the tables._
+
+-- COMMAND ----------
+
+-- MAGIC %md
+-- MAGIC # Configuration Settings
+-- MAGIC Set the catalog and schema where the tables will be created.
+
+-- COMMAND ----------
+
+-- MAGIC %run "../00-Setup/Initialize"
+
+-- COMMAND ----------
+
+-- MAGIC %md
+-- MAGIC # Set variables
+-- MAGIC Variables used in the queries.
+
+-- COMMAND ----------
+
+declare or replace variable br_table string = catalog_nm || '.' || schema_nm || '.' || 'patient_stg'; -- staging table identifier
+declare or replace variable si_table string = catalog_nm || '.' || schema_nm || '.' || 'patient_int'; -- integration table
+declare or replace variable gd_table string = catalog_nm || '.' || schema_nm || '.' || 'g_patient_d'; -- dimension table
+
+-- COMMAND ----------
+
+declare or replace variable data_source string = 'ABC Systems'; -- source system code
+declare or replace variable process_id string; -- a process id to associate with the load, for e.g., session id, run id
+
+-- pass Workflows {{job.id}}-{{job.run_id}} to notebook parameter
+-- to set process_id
+-- Optional
+set variable process_id = :p_process_id;
+
+-- COMMAND ----------
+
+-- for logging the run
+declare or replace variable load_table string;
+declare or replace variable load_start_time timestamp;
+declare or replace variable load_end_time timestamp;
+
+-- COMMAND ----------
+
+declare or replace variable sqlstr string; -- variable to hold any sql statement for EXECUTE IMMEDIATE
+
+-- COMMAND ----------
+
+-- MAGIC %md-sandbox
+-- MAGIC **Set variables for incremental load to Integration and Dimension tables.**
+-- MAGIC
+-- MAGIC
+-- MAGIC Note:
+-- MAGIC The code guards against runtime failures. If the load end time failed to update in the config/log table, the next time round, the target table is checked to detrmine the last load time.
+-- MAGIC
+-- MAGIC To set the last load date-
+-- MAGIC 1. Query log table
+-- MAGIC 2. If no value found, query actual table to get the largest Update Date value
+-- MAGIC 3. If it is the initial load, proceed with default value
+-- MAGIC
+
+-- COMMAND ----------
+
+declare or replace variable si_last_load_date timestamp default '1990-01-01';
+declare or replace variable gd_last_load_date timestamp default '1990-01-01';
+
+-- COMMAND ----------
+
+-- to get table_changes since integration table last loaded
+set variable si_last_load_date = coalesce((select max(load_end_time) from identifier(session.run_log_table) where data_source = session.data_source and table_name = session.si_table), (select max(update_dt) from identifier(session.si_table)), session.si_last_load_date);
+
+-- to get table_changes since dimension table last loaded
+set variable gd_last_load_date = coalesce((select max(load_end_time) from identifier(session.run_log_table) where data_source = session.data_source and table_name = session.gd_table), (select max(update_dt) from identifier(session.gd_table)), session.gd_last_load_date);
+
+
+-- COMMAND ----------
+
+-- MAGIC %md
+-- MAGIC # Load staging table
+-- MAGIC **Load the incremental (cdc) source files to staging table**
+-- MAGIC
+-- MAGIC The initial and incremental source CSV files are uploaded to a staging location.
+-- MAGIC
+-- MAGIC The staging table is insert-only.
+-- MAGIC
+
+-- COMMAND ----------
+
+-- MAGIC %md
+-- MAGIC ## Log load start
+-- MAGIC Update load start time for staging table in the log table.
+
+-- COMMAND ----------
+
+set variable load_table = session.br_table;
+set variable load_start_time = current_timestamp();
+set variable load_end_time = null;
+
+-- COMMAND ----------
+
+-- MAGIC %run "../04-Utility/Log Run"
+
+-- COMMAND ----------
+
+-- MAGIC %md
+-- MAGIC ## COPY INTO staging table
+
+-- COMMAND ----------
+
+-- staging path is path to "staging" volume
+declare or replace variable file_stage string = session.staging_path || "/patient";
+
+-- COMMAND ----------
+
+set variable sqlstr = "
+copy into " || session.br_table || "
+from (
+ select
+ *,
+ session.data_source as data_source,
+ _metadata.file_name as file_name,
+ current_timestamp() as insert_dt,
+ session.process_id as process_id
+ from '" || session.file_stage || "'
+)
+fileformat = CSV
+format_options ('header' = 'true', 'inferSchema' = 'true', 'mergeSchema' = 'true')
+copy_options ('mergeSchema' = 'true')
+;
+"
+;
+
+-- load staging table
+execute immediate sqlstr;
+
+-- COMMAND ----------
+
+-- increase version of staging table to handle the case when no new files are copied
+-- By setting a custom table property - "latest_run" time
+-- (The property could be set for integration and dimension as well, for informational purposes)
+
+declare or replace variable sqlstr string;
+set variable sqlstr = 'alter table identifier(br_table) set tblproperties (\'elt.latest_run\' = \'' || current_timestamp() || '\')';
+execute immediate sqlstr;
+
+
+-- COMMAND ----------
+
+-- MAGIC %md
+-- MAGIC ## Log load end
+-- MAGIC Update load end time in the log table.
+
+-- COMMAND ----------
+
+set variable load_end_time = current_timestamp();
+
+-- COMMAND ----------
+
+-- MAGIC %run "../04-Utility/Log Run"
+
+-- COMMAND ----------
+
+-- MAGIC %md
+-- MAGIC # Populate integration table
+-- MAGIC Validate, curate, and load incremental data into the integration table.
+
+-- COMMAND ----------
+
+-- MAGIC %md
+-- MAGIC ## _Placeholder_
+-- MAGIC
+-- MAGIC _Validate incoming data
+-- MAGIC Handle errors in the newly inserted data, before populating the curated data in the integration table._
+-- MAGIC _Exception records (refs) can be captured in common table error table elt_error_table._
+-- MAGIC
+-- MAGIC
+-- MAGIC _Steps would involve:_
+-- MAGIC - _Checking business rules / mandatory data, and quarantining records_
+-- MAGIC
+-- MAGIC _For e.g.,_
+-- MAGIC - _ID is null_
+-- MAGIC - _CHANGEDONDATE is null_
+-- MAGIC - _Older version_
+
+-- COMMAND ----------
+
+-- MAGIC %md
+-- MAGIC Create table_changes temporary view to get unprocessed rows
+
+-- COMMAND ----------
+
+-- staging table changes
+set var sqlstr = "create or replace temporary view tc_br_table_tv as with vars as (select session.si_last_load_date) select * from table_changes('" || session.br_table || "', '" || session.si_last_load_date || "') where _change_type = 'insert'";
+
+execute immediate sqlstr;
+
+-- COMMAND ----------
+
+-- MAGIC %md
+-- MAGIC ## Log load start
+-- MAGIC
+-- MAGIC Update load start time for integration table in the log table.
+
+-- COMMAND ----------
+
+set variable load_table = session.si_table;
+set variable load_start_time = current_timestamp();
+set variable load_end_time = null;
+
+-- COMMAND ----------
+
+-- MAGIC %run "../04-Utility/Log Run"
+
+-- COMMAND ----------
+
+-- MAGIC %md
+-- MAGIC ## Create transformation view
+-- MAGIC The temporary view curates the data as follows:
+-- MAGIC - Transforms columns
+-- MAGIC - Standardizes code description for gender and ethnicity
+-- MAGIC - Omits exception records
+-- MAGIC
+-- MAGIC The integration table is being treated as insert only.
+
+-- COMMAND ----------
+
+-- transform ingested source data
+create or replace temporary view si_transform_tv
+as
+with br_cdc as (
+ select * from tc_br_table_tv
+),
+vars as (select session.si_table, session.br_table, session.si_last_load_date, session.code_table) -- required for identity(si_table) to work
+select
+ id as patient_src_id,
+ birthdate as date_of_birth,
+ ssn as ssn,
+ drivers as drivers_license,
+ initcap(prefix) as name_prefix,
+ first as first_name,
+ last as last_name,
+ suffix as name_suffix,
+ maiden as maiden_name,
+ gender as gender_cd,
+ ifnull(code_gr.m_desc, gender) as gender_nm,
+ marital as marital_status,
+ ethnicity as ethnicity_cd,
+ ifnull(code_ethn.m_desc, ethnicity) as ethnicity_nm,
+ CHANGEDONDATE as src_changed_on_dt,
+ data_source,
+ current_timestamp() as insert_dt,
+ current_timestamp() as update_dt,
+ session.process_id as process_id
+from br_cdc
+left outer join identifier(session.code_table) code_gr on code_gr.m_code = br_cdc.gender and code_gr.m_type = 'GENDER'
+left outer join identifier(session.code_table) code_ethn on code_ethn.m_code = br_cdc.ethnicity and code_ethn.m_type = 'ETHNICITY'
+where
+ id is not null and CHANGEDONDATE is not null -- these 2 conditions could be part of exception handling
+;
+
+
+-- COMMAND ----------
+
+-- MAGIC %md
+-- MAGIC ## Insert data
+-- MAGIC Insert data into the integration table using transformation view.
+-- MAGIC
+-- MAGIC Note: The design is to retain all versions of data, hence Insert. Else Merge.
+
+-- COMMAND ----------
+
+
+-- Insert new and changed data
+insert into identifier(si_table)
+select * from si_transform_tv
+;
+
+
+-- COMMAND ----------
+
+-- MAGIC %md
+-- MAGIC ## Log load end
+-- MAGIC Update load end time in the log table.
+
+-- COMMAND ----------
+
+set variable load_end_time = current_timestamp();
+
+-- COMMAND ----------
+
+-- MAGIC %run "../04-Utility/Log Run"
+
+-- COMMAND ----------
+
+-- MAGIC %md
+-- MAGIC # Populate dimension table
+-- MAGIC The dimension table g_patient_d is created as a SCD2 dimension.
+
+-- COMMAND ----------
+
+-- MAGIC %md
+-- MAGIC ## Log load start
+-- MAGIC
+-- MAGIC Update load start time for dimension table in the log table.
+
+-- COMMAND ----------
+
+set variable load_table = session.gd_table;
+set variable load_start_time = current_timestamp();
+set variable load_end_time = null;
+
+-- COMMAND ----------
+
+-- MAGIC %run "../04-Utility/Log Run"
+
+-- COMMAND ----------
+
+-- MAGIC %md
+-- MAGIC Create table_changes temporary view to get unprocessed rows
+
+-- COMMAND ----------
+
+-- integration table changes
+set var sqlstr = "create or replace temporary view tc_si_table_tv as with vars as (select session.gd_last_load_date) select * from table_changes('" || session.si_table || "', '" || session.gd_last_load_date || "') where _change_type = 'insert'";
+
+execute immediate sqlstr;
+
+-- COMMAND ----------
+
+-- MAGIC %md
+-- MAGIC ## Create transformation view
+-- MAGIC The view performs multiple functions:
+-- MAGIC - Transform incoming rows as required
+-- MAGIC - Create checksum
+-- MAGIC - Handle new and changed instances
+-- MAGIC - Handle multiple changes in a single batch
+-- MAGIC - Ignore consecutive versions if no changes to business attributes of interest
+-- MAGIC
+-- MAGIC Note: The effective start date for a version is based on the CHANGEDONDATE as recieved from the source. This needs to be taken into consideration when doing a lookup of the patient table.
+
+-- COMMAND ----------
+
+-- MAGIC %md
+-- MAGIC The view includes the following elements:
+-- MAGIC 1. **CTE si_tc**
+-- MAGIC This is used to transform any columns from the integration table
+-- MAGIC 2. **CTE curr_v**
+-- MAGIC Identify the Current Versions of all Patient instances corresponding to incoming data in this run.
+-- MAGIC 3. **CTE ins_upd_rows**
+-- MAGIC This CTE isolates new patients and new versions of existing patients for insert. This also identifies existing versions which need to be updated (potentially), to include an effective_end_date.
+-- MAGIC Finally there is-
+-- MAGIC 4. CTE no_dup_ver
+-- MAGIC This is used to eliminate any updated records from the source for which there are no changes to business attributes of interest. For e.g., drivers_license is not being populated in the dimension. Suppose the only update in the source is to drivers_license, it will simply turn out to be a duplicate record.
+
+-- COMMAND ----------
+
+create or replace temporary view dim_transform_tv
+as
+with vars as (select session.gd_table, session.gd_last_load_date), -- select the variables for use in later clauses
+si_tc as (
+ select
+ last_name, first_name, name_prefix, name_suffix, maiden_name,
+ gender_cd as gender_code, gender_nm as gender,
+ date_of_birth, nvl(marital_status, 'Not Available') as marital_status,
+ ethnicity_cd as ethnicity_code, ethnicity_nm as ethnicity,
+ ssn, null as other_identifiers, null as uda,
+ patient_src_id, src_changed_on_dt as effective_start_date,
+ md5(ifnull(last_name, '#') || ifnull(first_name, '#') || ifnull(name_prefix, '#') || ifnull(name_suffix, '#') || ifnull(maiden_name, '#') ||
+ ifnull(gender_cd, '#') || ifnull(gender_nm, '#') || ifnull(date_of_birth, '#') || ifnull(marital_status, '#') || ifnull(ethnicity_cd, '#') || ifnull(ethnicity_nm, '#') || ifnull(ssn, '#')) as checksum,
+ data_source
+ from tc_si_table_tv
+),
+curr_v as (
+ -- GET current version records in dimension table, if any, corresponding to incoming data
+ select gd.* except (effective_end_date, insert_dt, update_dt, process_id)
+ from identifier(session.gd_table) gd
+ where effective_end_date is null and
+ exists (select 1 from si_tc where si_tc.patient_src_id = gd.patient_src_id AND si_tc.data_source = gd.data_source)
+),
+ins_upd_rows as (
+ -- ISOLATE new patients and new versions
+ select null as patient_sk, * from si_tc
+ union all
+ -- use this to update effective_end_date of existing version in gd
+ select * from curr_v
+),
+no_dup_ver as (
+ -- IGNORE consecutive versions if no changes to business attributes of interest
+ select
+ *,
+ lag(checksum, 1, null) over (partition by patient_src_id, data_source order by effective_start_date asc) as checksum_next
+ from ins_upd_rows
+ qualify checksum <> ifnull(checksum_next, '#') -- initially no records (for checksum_next)
+)
+-- FINAL set (new patients and new versions, existing versions for updating effective_end_date)
+select
+ *,
+ lead(effective_start_date, 1, null) over (partition by patient_src_id order by effective_start_date asc) as effective_end_date
+from no_dup_ver
+;
+
+
+-- COMMAND ----------
+
+-- MAGIC %md
+-- MAGIC ## Merge data
+-- MAGIC Update the dimension table by:
+-- MAGIC - Merge new and changed records.
+-- MAGIC - Version existing patient records (by updating effective_end_date).
+
+-- COMMAND ----------
+
+merge into identifier(session.gd_table) d
+using dim_transform_tv tr
+on d.patient_sk = tr.patient_sk
+when matched then update
+ -- update end date for existing version of patient
+ set d.effective_end_date = tr.effective_end_date,
+ update_dt = current_timestamp(),
+ process_id = session.process_id
+when not matched then insert (
+ -- insert new vesrions and new patients
+ last_name,
+ first_name,
+ name_prefix,
+ name_suffix,
+ maiden_name,
+ gender_code,
+ gender,
+ date_of_birth,
+ marital_status,
+ ethnicity_code,
+ ethnicity,
+ ssn,
+ patient_src_id,
+ effective_start_date,
+ effective_end_date,
+ checksum,
+ data_source,
+ insert_dt,
+ update_dt,
+ process_id)
+ values (last_name, first_name, name_prefix, name_suffix, maiden_name, gender_code, gender, date_of_birth, marital_status, ethnicity_code,
+ ethnicity, ssn, patient_src_id, effective_start_date, effective_end_date, checksum, data_source, current_timestamp(), current_timestamp(), session.process_id)
+;
+
+
+-- COMMAND ----------
+
+-- MAGIC %md
+-- MAGIC ## Log load end
+-- MAGIC Update load end time in the log table.
+
+-- COMMAND ----------
+
+set variable load_end_time = current_timestamp();
+
+-- COMMAND ----------
+
+-- MAGIC %run "../04-Utility/Log Run"
\ No newline at end of file
diff --git a/product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/02-Populate/Patient Dimension ETL.sql b/product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/02-Populate/Patient Dimension ETL.sql
new file mode 100644
index 00000000..a43c8b91
--- /dev/null
+++ b/product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/02-Populate/Patient Dimension ETL.sql
@@ -0,0 +1,453 @@
+-- Databricks notebook source
+-- MAGIC %md-sandbox
+-- MAGIC # Patient Dimension ETL
+-- MAGIC This notebook contains the code to load the patient dimension which is part of the clinical star schema.
+-- MAGIC The same pattern can be used to load any of your business dimensions.
+-- MAGIC
+-- MAGIC **Initial and Incremental load of Patient dimension**
+-- MAGIC The notebook performs the following tasks:
+-- MAGIC -> Load staging table
+-- MAGIC -> Curate and load integration table
+-- MAGIC -> Transform and load dimension table using SCD2
+-- MAGIC
+-- MAGIC The staging table is loaded from files extracted to cloud storage.
+-- MAGIC These files contain incremental data extracts.
+-- MAGIC Zero, one, or more new files loaded during each run.
+
+-- COMMAND ----------
+
+-- MAGIC %md
+-- MAGIC **The code incorporates the following design elements:**
+-- MAGIC - Versioning of data (SCD Type 2)
+-- MAGIC - Checksum
+-- MAGIC - Code standardization
+-- MAGIC
+-- MAGIC Simply re-run Job to recover from a runtime error.
+-- MAGIC
+-- MAGIC
+-- MAGIC _The code uses temporary views and single DML for each of the tables._
+
+-- COMMAND ----------
+
+-- MAGIC %md
+-- MAGIC # Configuration Settings
+-- MAGIC Set the catalog and schema where the tables will be created.
+
+-- COMMAND ----------
+
+-- MAGIC %run "../00-Setup/Initialize"
+
+-- COMMAND ----------
+
+-- MAGIC %md
+-- MAGIC # Set variables
+-- MAGIC Variables used in the queries.
+
+-- COMMAND ----------
+
+declare or replace variable br_table string; -- staging table identifier
+declare or replace variable si_table string; -- integration table
+declare or replace variable gd_table string; -- dimension table
+
+-- COMMAND ----------
+
+set variable (br_table, si_table, gd_table) = (select catalog_nm || '.' || schema_nm || '.' || 'patient_stg', catalog_nm || '.' || schema_nm || '.' || 'patient_int', catalog_nm || '.' || schema_nm || '.' || 'g_patient_d');
+
+-- COMMAND ----------
+
+declare or replace variable data_source string default 'ABC Systems'; -- source system code
+declare or replace variable process_id string; -- a process id to associate with the load, for e.g., session id, run id
+
+-- COMMAND ----------
+
+declare or replace variable sqlstr string; -- variable to hold any sql statement for EXECUTE IMMEDIATE
+
+-- COMMAND ----------
+
+-- for logging the run
+declare or replace variable load_table string;
+declare or replace variable load_start_time timestamp;
+declare or replace variable load_end_time timestamp;
+
+-- COMMAND ----------
+
+-- pass Workflows {{job.id}}-{{job.run_id}} to notebook parameter
+-- to set process_id
+-- Optional
+set variable process_id = :p_process_id;
+
+-- COMMAND ----------
+
+-- MAGIC %md-sandbox
+-- MAGIC **Set variables for incremental load to Integration and Dimension tables.**
+-- MAGIC
+-- MAGIC
+-- MAGIC Note:
+-- MAGIC The code guards against runtime failures. If the load end time failed to update in the config/log table, the next time round, the target table is checked to detrmine the last load time.
+-- MAGIC
+-- MAGIC To set the last load date-
+-- MAGIC 1. Query log table
+-- MAGIC 2. If no value found, query actual table to get the largest Update Date value
+-- MAGIC 3. If it is the initial load, proceed with default value
+-- MAGIC
+
+-- COMMAND ----------
+
+declare or replace variable si_last_load_date timestamp default '1990-01-01';
+declare or replace variable gd_last_load_date timestamp default '1990-01-01';
+
+-- COMMAND ----------
+
+-- to get table_changes since integration table last loaded
+set variable si_last_load_date = coalesce((select max(load_end_time) from identifier(session.run_log_table) where data_source = session.data_source and table_name = session.si_table), (select max(update_dt) from identifier(session.si_table)), session.si_last_load_date);
+
+-- to get table_changes since dimension table last loaded
+set variable gd_last_load_date = coalesce((select max(load_end_time) from identifier(session.run_log_table) where data_source = session.data_source and table_name = session.gd_table), (select max(update_dt) from identifier(session.gd_table)), session.gd_last_load_date);
+
+
+-- COMMAND ----------
+
+-- MAGIC %md
+-- MAGIC # Load staging table
+-- MAGIC **Load the incremental (cdc) source files to staging table**
+-- MAGIC
+-- MAGIC The initial and incremental source CSV files are uploaded to a staging location.
+-- MAGIC
+-- MAGIC The staging table is insert-only.
+-- MAGIC
+
+-- COMMAND ----------
+
+-- MAGIC %md
+-- MAGIC ## Log load start
+-- MAGIC Update load start time for staging table in the log table.
+
+-- COMMAND ----------
+
+set variable (load_table, load_start_time, load_end_time) = (select session.br_table, current_timestamp(), null);
+
+-- COMMAND ----------
+
+-- MAGIC %run "../04-Utility/Log Run"
+
+-- COMMAND ----------
+
+-- MAGIC %md
+-- MAGIC ## COPY INTO staging table
+
+-- COMMAND ----------
+
+-- staging path is path to "staging" volume
+declare or replace variable file_stage string = session.staging_path || "/patient";
+
+-- COMMAND ----------
+
+set variable sqlstr = "
+copy into " || session.br_table || "
+from (
+ select
+ *,
+ session.data_source as data_source,
+ _metadata.file_name as file_name,
+ current_timestamp() as insert_dt,
+ session.process_id as process_id
+ from '" || session.file_stage || "'
+)
+fileformat = CSV
+format_options ('header' = 'true', 'inferSchema' = 'true', 'mergeSchema' = 'true')
+copy_options ('mergeSchema' = 'true')
+;
+"
+;
+
+-- load staging table
+execute immediate sqlstr;
+
+-- COMMAND ----------
+
+-- MAGIC %md
+-- MAGIC ## Log load end
+-- MAGIC Update load end time in the log table.
+
+-- COMMAND ----------
+
+set variable load_end_time = current_timestamp();
+
+-- COMMAND ----------
+
+-- MAGIC %run "../04-Utility/Log Run"
+
+-- COMMAND ----------
+
+-- MAGIC %md
+-- MAGIC # Populate integration table
+-- MAGIC Validate, curate, and load incremental data into the integration table.
+
+-- COMMAND ----------
+
+-- MAGIC %md
+-- MAGIC ## _Placeholder_
+-- MAGIC
+-- MAGIC _Validate incoming data
+-- MAGIC Handle errors in the newly inserted data, before populating the curated data in the integration table._
+-- MAGIC _Exception records (refs) can be captured in common table error table elt_error_table._
+-- MAGIC
+-- MAGIC
+-- MAGIC _Steps would involve:_
+-- MAGIC - _Checking business rules / mandatory data, and quarantining records_
+-- MAGIC
+-- MAGIC _For e.g.,_
+-- MAGIC - _ID is null_
+-- MAGIC - _CHANGEDONDATE is null_
+-- MAGIC - _Older version_
+
+-- COMMAND ----------
+
+-- MAGIC %md
+-- MAGIC ## Log load start
+-- MAGIC
+-- MAGIC Update load start time for integration table in the log table.
+
+-- COMMAND ----------
+
+set variable (load_table, load_start_time, load_end_time) = (select session.si_table, current_timestamp(), null);
+
+-- COMMAND ----------
+
+-- MAGIC %run "../04-Utility/Log Run"
+
+-- COMMAND ----------
+
+-- MAGIC %md
+-- MAGIC ## Create transformation view
+-- MAGIC The temporary view curates the data as follows:
+-- MAGIC - Transforms columns
+-- MAGIC - Standardizes code description for gender and ethnicity
+-- MAGIC - Omits exception records
+-- MAGIC
+-- MAGIC The integration table is being treated as insert only.
+
+-- COMMAND ----------
+
+-- transform ingested source data
+create or replace temporary view si_transform_tv
+as
+with vars as (select session.si_table, session.br_table, session.si_last_load_date, session.code_table), -- required for identity(si_table) to work
+br_cdc as (
+ select * from identifier(session.br_table) br
+ where br.insert_dt > session.si_last_load_date
+)
+select
+ id as patient_src_id,
+ birthdate as date_of_birth,
+ ssn as ssn,
+ drivers as drivers_license,
+ initcap(prefix) as name_prefix,
+ first as first_name,
+ last as last_name,
+ suffix as name_suffix,
+ maiden as maiden_name,
+ gender as gender_cd,
+ ifnull(code_gr.m_desc, gender) as gender_nm,
+ marital as marital_status,
+ ethnicity as ethnicity_cd,
+ ifnull(code_ethn.m_desc, ethnicity) as ethnicity_nm,
+ CHANGEDONDATE as src_changed_on_dt,
+ data_source,
+ current_timestamp() as insert_dt,
+ current_timestamp() as update_dt,
+ session.process_id as process_id
+from br_cdc
+left outer join identifier(session.code_table) code_gr on code_gr.m_code = br_cdc.gender and code_gr.m_type = 'GENDER'
+left outer join identifier(session.code_table) code_ethn on code_ethn.m_code = br_cdc.ethnicity and code_ethn.m_type = 'ETHNICITY'
+where
+ -- no error records
+ id is not null and CHANGEDONDATE is not null -- these 2 conditions could be part of exception handling
+;
+
+
+-- COMMAND ----------
+
+-- MAGIC %md
+-- MAGIC ## Insert data
+-- MAGIC Insert data into the integration table using transformation view.
+-- MAGIC
+-- MAGIC Note: The design is to retain all versions of data, hence Insert. Else Merge.
+
+-- COMMAND ----------
+
+-- Insert new and changed data
+insert into identifier(si_table)
+select * from si_transform_tv
+;
+
+
+-- COMMAND ----------
+
+-- MAGIC %md
+-- MAGIC ## Log load end
+-- MAGIC Update load end time in the log table.
+
+-- COMMAND ----------
+
+set variable load_end_time = current_timestamp();
+
+-- COMMAND ----------
+
+-- MAGIC %run "../04-Utility/Log Run"
+
+-- COMMAND ----------
+
+-- MAGIC %md
+-- MAGIC # Populate dimension table
+-- MAGIC The dimension table g_patient_d is created as a SCD2 dimension.
+
+-- COMMAND ----------
+
+-- MAGIC %md
+-- MAGIC ## Log load start
+-- MAGIC
+-- MAGIC Update load start time for dimension table in the log table.
+
+-- COMMAND ----------
+
+set variable (load_table, load_start_time, load_end_time) = (select session.gd_table, current_timestamp(), null);
+
+-- COMMAND ----------
+
+-- MAGIC %run "../04-Utility/Log Run"
+
+-- COMMAND ----------
+
+-- MAGIC %md
+-- MAGIC ## Create transformation view
+-- MAGIC The view performs multiple functions:
+-- MAGIC - Transform incoming rows as required
+-- MAGIC - Create checksum
+-- MAGIC - Handle new and changed instances
+-- MAGIC - Handle multiple changes in a single batch
+-- MAGIC - Ignore consecutive versions if no changes to business attributes of interest
+-- MAGIC
+-- MAGIC Note: The effective start date for a version is based on the CHANGEDONDATE as recieved from the source. This needs to be taken into consideration when doing a lookup of the patient table.
+
+-- COMMAND ----------
+
+-- MAGIC %md
+-- MAGIC The view includes the following elements:
+-- MAGIC 1. **CTE si_tc**
+-- MAGIC This is used to transform any columns from the integration table
+-- MAGIC 2. **CTE curr_v**
+-- MAGIC Identify the Current Versions of all Patient instances corresponding to incoming data in this run.
+-- MAGIC 3. **CTE ins_upd_rows**
+-- MAGIC This CTE isolates new patients and new versions of existing patients for insert. This also identifies existing versions which need to be updated (potentially), to include an effective_end_date.
+-- MAGIC Finally there is-
+-- MAGIC 4. CTE no_dup_ver
+-- MAGIC This is used to eliminate any updated records from the source for which there are no changes to business attributes of interest. For e.g., drivers_license is not being populated in the dimension. Suppose the only update in the source is to drivers_license, it will simply turn out to be a duplicate record.
+
+-- COMMAND ----------
+
+create or replace temporary view dim_transform_tv
+as
+with vars as (select session.si_table, session.gd_table, session.gd_last_load_date), -- select the variables for use in later clauses
+si_tc as (
+ select
+ last_name, first_name, name_prefix, name_suffix, maiden_name,
+ gender_cd as gender_code, gender_nm as gender,
+ date_of_birth, nvl(marital_status, 'Not Available') as marital_status,
+ ethnicity_cd as ethnicity_code, ethnicity_nm as ethnicity,
+ ssn, null as other_identifiers, null as uda,
+ patient_src_id, src_changed_on_dt as effective_start_date,
+ md5(ifnull(last_name, '#') || ifnull(first_name, '#') || ifnull(name_prefix, '#') || ifnull(name_suffix, '#') || ifnull(maiden_name, '#') ||
+ ifnull(gender_cd, '#') || ifnull(gender_nm, '#') || ifnull(date_of_birth, '#') || ifnull(marital_status, '#') || ifnull(ethnicity_cd, '#') || ifnull(ethnicity_nm, '#') || ifnull(ssn, '#')) as checksum,
+ data_source
+ from identifier(session.si_table) si
+ where si.update_dt > session.gd_last_load_date -- CDC
+),
+curr_v as (
+ -- GET current version records in dimension table, if any, corresponding to incoming data
+ select gd.* except (effective_end_date, insert_dt, update_dt, process_id)
+ from identifier(session.gd_table) gd
+ where effective_end_date is null and
+ exists (select 1 from si_tc where si_tc.patient_src_id = gd.patient_src_id AND si_tc.data_source = gd.data_source)
+),
+ins_upd_rows as (
+ -- ISOLATE new patients and new versions
+ select null as patient_sk, * from si_tc
+ union all
+ -- use this to update effective_end_date of existing version in gd
+ select * from curr_v
+),
+no_dup_ver as (
+ -- IGNORE consecutive versions if no changes to business attributes of interest
+ select
+ *,
+ lag(checksum, 1, null) over (partition by patient_src_id, data_source order by effective_start_date asc) as checksum_next
+ from ins_upd_rows
+ qualify checksum <> ifnull(checksum_next, '#') -- initially no records (for checksum_next)
+)
+-- FINAL set (new patients and new versions, existing versions for updating effective_end_date)
+select
+ *,
+ lead(effective_start_date, 1, null) over (partition by patient_src_id order by effective_start_date asc) as effective_end_date
+from no_dup_ver
+;
+
+
+-- COMMAND ----------
+
+-- MAGIC %md
+-- MAGIC ## Merge data
+-- MAGIC Update the dimension table by:
+-- MAGIC - Merge new and changed records.
+-- MAGIC - Version existing patient records (by updating effective_end_date).
+
+-- COMMAND ----------
+
+merge into identifier(session.gd_table) d
+using dim_transform_tv tr
+on d.patient_sk = tr.patient_sk
+when matched then update
+ -- update end date for existing version of patient
+ set d.effective_end_date = tr.effective_end_date,
+ update_dt = current_timestamp(),
+ process_id = session.process_id
+when not matched then insert (
+ -- insert new vesrions and new patients
+ last_name,
+ first_name,
+ name_prefix,
+ name_suffix,
+ maiden_name,
+ gender_code,
+ gender,
+ date_of_birth,
+ marital_status,
+ ethnicity_code,
+ ethnicity,
+ ssn,
+ patient_src_id,
+ effective_start_date,
+ effective_end_date,
+ checksum,
+ data_source,
+ insert_dt,
+ update_dt,
+ process_id)
+ values (last_name, first_name, name_prefix, name_suffix, maiden_name, gender_code, gender, date_of_birth, marital_status, ethnicity_code,
+ ethnicity, ssn, patient_src_id, effective_start_date, effective_end_date, checksum, data_source, current_timestamp(), current_timestamp(), session.process_id)
+;
+
+
+-- COMMAND ----------
+
+-- MAGIC %md
+-- MAGIC ## Log load end
+-- MAGIC Update load end time in the log table.
+
+-- COMMAND ----------
+
+set variable load_end_time = current_timestamp();
+
+-- COMMAND ----------
+
+-- MAGIC %run "../04-Utility/Log Run"
\ No newline at end of file
diff --git a/product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/04-Utility/Log Run.sql b/product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/04-Utility/Log Run.sql
new file mode 100644
index 00000000..b952be3f
--- /dev/null
+++ b/product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/04-Utility/Log Run.sql
@@ -0,0 +1,26 @@
+-- Databricks notebook source
+declare or replace variable op_sql string;
+
+-- COMMAND ----------
+
+-- to obtain the results of the previous DML
+set variable op_sql = "
+ create or replace temporary view metrics_tv
+ as
+ select coalesce(operationMetrics.numTargetRowsInserted, operationMetrics.numOutputRows) as num_inserted, operationMetrics.numTargetRowsUpdated as num_updated from (describe history " || session.load_table || ") where operation in ('MERGE', 'WRITE', 'COPY INTO') limit 1
+";
+
+-- COMMAND ----------
+
+-- create metrics view
+execute immediate op_sql;
+
+-- COMMAND ----------
+
+merge into identifier(session.run_log_table) as t
+using (select * from values (session.data_source, session.load_table, session.load_start_time, session.load_end_time, session.process_id) as
+ source(data_source, table_name, load_start_time, load_end_time, process_id))
+on (t.data_source = source.data_source and t.table_name = source.table_name and t.load_start_time = source.load_start_time)
+when matched then update set t.load_end_time = source.load_end_time, t.num_inserts = (select num_inserted from metrics_tv), t.num_updates = (select num_updated from metrics_tv)
+when not matched then insert (data_source, table_name, load_start_time, process_id) values (source.data_source, source.table_name, source.load_start_time, source.process_id)
+;
\ No newline at end of file
diff --git a/product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/_util/browse current load.sql b/product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/_util/browse current load.sql
new file mode 100644
index 00000000..b7d2966b
--- /dev/null
+++ b/product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/_util/browse current load.sql
@@ -0,0 +1,54 @@
+-- Databricks notebook source
+-- MAGIC %run "../00-Setup/Initialize"
+
+-- COMMAND ----------
+
+declare or replace variable br_table string; -- bronze table identifier
+declare or replace variable si_table string; -- silver table
+declare or replace variable gd_table string; -- gold dimension table
+
+-- COMMAND ----------
+
+set variable (br_table, si_table, gd_table) = (select catalog_nm || '.' || schema_nm || '.' || 'patient_stg', catalog_nm || '.' || schema_nm || '.' || 'patient_int', catalog_nm || '.' || schema_nm || '.' || 'g_patient_d');
+
+-- COMMAND ----------
+
+-- MAGIC %md
+-- MAGIC **Config/Log table**
+
+-- COMMAND ----------
+
+select * from identifier(run_log_table) order by load_start_time;
+
+-- COMMAND ----------
+
+-- MAGIC %md
+-- MAGIC **Patient Staging table**
+
+-- COMMAND ----------
+
+-- DBTITLE 1,Bronze
+select id, CHANGEDONDATE, data_source, * except(id, CHANGEDONDATE, data_source) from identifier(br_table)
+order by data_source, id, CHANGEDONDATE
+
+-- COMMAND ----------
+
+-- MAGIC %md
+-- MAGIC **Patient Integration table**
+
+-- COMMAND ----------
+
+-- DBTITLE 1,Silver
+select patient_src_id, src_changed_on_dt, data_source, * except(patient_src_id, src_changed_on_dt, data_source) from identifier(si_table)
+order by data_source, patient_src_id, src_changed_on_dt
+
+-- COMMAND ----------
+
+-- MAGIC %md
+-- MAGIC **Patient Dimension table**
+
+-- COMMAND ----------
+
+-- DBTITLE 1,Gold
+select patient_sk, patient_src_id, effective_start_date, effective_end_date, data_source, * except(patient_sk, patient_src_id, effective_start_date, effective_end_date, data_source) from identifier(gd_table)
+order by data_source, patient_src_id, effective_start_date
\ No newline at end of file
diff --git a/product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/_util/initialize-staging.py b/product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/_util/initialize-staging.py
new file mode 100644
index 00000000..6a767110
--- /dev/null
+++ b/product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/_util/initialize-staging.py
@@ -0,0 +1,46 @@
+# Databricks notebook source
+# MAGIC %run "../00-Setup/Initialize"
+
+# COMMAND ----------
+
+# DBTITLE 1,config schema
+# MAGIC %sql
+# MAGIC select staging_path
+
+# COMMAND ----------
+
+res = _sqldf.first()
+
+stg_path = res["staging_path"]
+
+# COMMAND ----------
+
+# DBTITLE 1,def function for staging process
+def download_file(down_url, destination):
+ import requests
+
+ with requests.get(down_url, stream=True) as r:
+ r.raise_for_status()
+ with open(destination, 'wb') as f:
+ for chunk in r.iter_content(chunk_size=8192):
+ # If you have chunk encoded response uncomment if
+ # and set chunk_size parameter to None.
+ #if chunk:
+ f.write(chunk)
+
+# COMMAND ----------
+
+def get_file(file_name, entity_name):
+ import requests
+
+ owner = "shyamraodb"
+ repo = "star-schema-elt"
+ path = "seed"
+
+ files = requests.get(f'https://api.github.com/repos/{owner}/{repo}/contents/{path}').json()
+
+ # download url for
+ down_url = [f['download_url'] for f in files if file_name in f['name']][0]
+ destination = stg_path + "/" + entity_name + "/" + file_name
+
+ download_file(down_url, destination)
\ No newline at end of file
diff --git a/product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/_util/stage source file - incremental load #1.py b/product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/_util/stage source file - incremental load #1.py
new file mode 100644
index 00000000..b55a8422
--- /dev/null
+++ b/product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/_util/stage source file - incremental load #1.py
@@ -0,0 +1,28 @@
+# Databricks notebook source
+# DBTITLE 1,initialize config for sgc
+# MAGIC %run "./initialize-staging"
+
+# COMMAND ----------
+
+# MAGIC %md
+# MAGIC ## Stage the source file for first incremental load
+
+# COMMAND ----------
+
+# DBTITLE 1,incremental file name (1)
+file_name = "patients_incr1.csv"
+
+# COMMAND ----------
+
+# DBTITLE 1,stage the file for initial load
+# download to volume staging path
+get_file(file_name, "patient")
+
+# COMMAND ----------
+
+file_name = "patients_incr2.csv"
+
+# COMMAND ----------
+
+# download to volume staging path
+get_file(file_name, "patient")
\ No newline at end of file
diff --git a/product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/_util/stage source file - initial load.py b/product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/_util/stage source file - initial load.py
new file mode 100644
index 00000000..7edccbcd
--- /dev/null
+++ b/product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/_util/stage source file - initial load.py
@@ -0,0 +1,30 @@
+# Databricks notebook source
+# DBTITLE 1,initialize config for sgc
+# MAGIC %run "./initialize-staging"
+
+# COMMAND ----------
+
+# MAGIC %md
+# MAGIC ## Stage the source file for initial load
+# MAGIC
+# MAGIC Download and copy _patients50_init.csv_ to volume staging path (/Volumes/\/\/staging/patient)
+
+# COMMAND ----------
+
+# DBTITLE 1,staging directory
+# clear staging directory
+dbutils.fs.rm(stg_path + "/patient", True)
+
+# if not present
+dbutils.fs.mkdirs(stg_path + "/patient")
+
+# COMMAND ----------
+
+# DBTITLE 1,initial file name
+file_name = "patients50_init.csv"
+
+# COMMAND ----------
+
+# DBTITLE 1,stage the file for initial load
+# download to volume staging path
+get_file(file_name, "patient")
\ No newline at end of file
From b15202a7b7c01b00cbfb6cdda0186624fdbb28df Mon Sep 17 00:00:00 2001
From: shyamraodb <129457417+shyamraodb@users.noreply.github.com>
Date: Tue, 25 Feb 2025 17:44:18 -0800
Subject: [PATCH 07/43] Delete
product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/readme
---
product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/readme | 1 -
1 file changed, 1 deletion(-)
delete mode 100644 product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/readme
diff --git a/product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/readme b/product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/readme
deleted file mode 100644
index 8b137891..00000000
--- a/product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/readme
+++ /dev/null
@@ -1 +0,0 @@
-
From a07de86c3aec09bd273a23d4b58326fa65e4634c Mon Sep 17 00:00:00 2001
From: shyamraodb <129457417+shyamraodb@users.noreply.github.com>
Date: Tue, 25 Feb 2025 17:46:51 -0800
Subject: [PATCH 08/43] Delete
product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/02-Populate/Patient
Dimension ETL w CDF.sql
---
.../Patient Dimension ETL w CDF.sql | 486 ------------------
1 file changed, 486 deletions(-)
delete mode 100644 product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/02-Populate/Patient Dimension ETL w CDF.sql
diff --git a/product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/02-Populate/Patient Dimension ETL w CDF.sql b/product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/02-Populate/Patient Dimension ETL w CDF.sql
deleted file mode 100644
index 02eca3dc..00000000
--- a/product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/02-Populate/Patient Dimension ETL w CDF.sql
+++ /dev/null
@@ -1,486 +0,0 @@
--- Databricks notebook source
--- MAGIC %md-sandbox
--- MAGIC # Patient Dimension ETL
--- MAGIC This notebook contains the code to load the patient dimension which is part of the clinical star schema.
--- MAGIC The same pattern can be used to load any of your business dimensions.
--- MAGIC
--- MAGIC **Initial and Incremental load of Patient dimension**
--- MAGIC The notebook performs the following tasks:
--- MAGIC -> Load staging table
--- MAGIC -> Curate and load integration table
--- MAGIC -> Transform and load dimension table using SCD2
--- MAGIC
--- MAGIC The staging table is loaded from files extracted to cloud storage.
--- MAGIC These files contain incremental data extracts.
--- MAGIC Zero, one, or more new files loaded during each run.
-
--- COMMAND ----------
-
--- MAGIC %md
--- MAGIC **The code incorporates the following design elements:**
--- MAGIC - Versioning of data (SCD Type 2)
--- MAGIC - Checksum
--- MAGIC - Code standardization
--- MAGIC
--- MAGIC Simply re-run Job to recover from a runtime error.
--- MAGIC
--- MAGIC
--- MAGIC _The code uses temporary views and single DML for each of the tables._
-
--- COMMAND ----------
-
--- MAGIC %md
--- MAGIC # Configuration Settings
--- MAGIC Set the catalog and schema where the tables will be created.
-
--- COMMAND ----------
-
--- MAGIC %run "../00-Setup/Initialize"
-
--- COMMAND ----------
-
--- MAGIC %md
--- MAGIC # Set variables
--- MAGIC Variables used in the queries.
-
--- COMMAND ----------
-
-declare or replace variable br_table string = catalog_nm || '.' || schema_nm || '.' || 'patient_stg'; -- staging table identifier
-declare or replace variable si_table string = catalog_nm || '.' || schema_nm || '.' || 'patient_int'; -- integration table
-declare or replace variable gd_table string = catalog_nm || '.' || schema_nm || '.' || 'g_patient_d'; -- dimension table
-
--- COMMAND ----------
-
-declare or replace variable data_source string = 'ABC Systems'; -- source system code
-declare or replace variable process_id string; -- a process id to associate with the load, for e.g., session id, run id
-
--- pass Workflows {{job.id}}-{{job.run_id}} to notebook parameter
--- to set process_id
--- Optional
-set variable process_id = :p_process_id;
-
--- COMMAND ----------
-
--- for logging the run
-declare or replace variable load_table string;
-declare or replace variable load_start_time timestamp;
-declare or replace variable load_end_time timestamp;
-
--- COMMAND ----------
-
-declare or replace variable sqlstr string; -- variable to hold any sql statement for EXECUTE IMMEDIATE
-
--- COMMAND ----------
-
--- MAGIC %md-sandbox
--- MAGIC **Set variables for incremental load to Integration and Dimension tables.**
--- MAGIC
--- MAGIC
--- MAGIC Note:
--- MAGIC The code guards against runtime failures. If the load end time failed to update in the config/log table, the next time round, the target table is checked to detrmine the last load time.
--- MAGIC
--- MAGIC To set the last load date-
--- MAGIC 1. Query log table
--- MAGIC 2. If no value found, query actual table to get the largest Update Date value
--- MAGIC 3. If it is the initial load, proceed with default value
--- MAGIC
-
--- COMMAND ----------
-
-declare or replace variable si_last_load_date timestamp default '1990-01-01';
-declare or replace variable gd_last_load_date timestamp default '1990-01-01';
-
--- COMMAND ----------
-
--- to get table_changes since integration table last loaded
-set variable si_last_load_date = coalesce((select max(load_end_time) from identifier(session.run_log_table) where data_source = session.data_source and table_name = session.si_table), (select max(update_dt) from identifier(session.si_table)), session.si_last_load_date);
-
--- to get table_changes since dimension table last loaded
-set variable gd_last_load_date = coalesce((select max(load_end_time) from identifier(session.run_log_table) where data_source = session.data_source and table_name = session.gd_table), (select max(update_dt) from identifier(session.gd_table)), session.gd_last_load_date);
-
-
--- COMMAND ----------
-
--- MAGIC %md
--- MAGIC # Load staging table
--- MAGIC **Load the incremental (cdc) source files to staging table**
--- MAGIC
--- MAGIC The initial and incremental source CSV files are uploaded to a staging location.
--- MAGIC
--- MAGIC The staging table is insert-only.
--- MAGIC
-
--- COMMAND ----------
-
--- MAGIC %md
--- MAGIC ## Log load start
--- MAGIC Update load start time for staging table in the log table.
-
--- COMMAND ----------
-
-set variable load_table = session.br_table;
-set variable load_start_time = current_timestamp();
-set variable load_end_time = null;
-
--- COMMAND ----------
-
--- MAGIC %run "../04-Utility/Log Run"
-
--- COMMAND ----------
-
--- MAGIC %md
--- MAGIC ## COPY INTO staging table
-
--- COMMAND ----------
-
--- staging path is path to "staging" volume
-declare or replace variable file_stage string = session.staging_path || "/patient";
-
--- COMMAND ----------
-
-set variable sqlstr = "
-copy into " || session.br_table || "
-from (
- select
- *,
- session.data_source as data_source,
- _metadata.file_name as file_name,
- current_timestamp() as insert_dt,
- session.process_id as process_id
- from '" || session.file_stage || "'
-)
-fileformat = CSV
-format_options ('header' = 'true', 'inferSchema' = 'true', 'mergeSchema' = 'true')
-copy_options ('mergeSchema' = 'true')
-;
-"
-;
-
--- load staging table
-execute immediate sqlstr;
-
--- COMMAND ----------
-
--- increase version of staging table to handle the case when no new files are copied
--- By setting a custom table property - "latest_run" time
--- (The property could be set for integration and dimension as well, for informational purposes)
-
-declare or replace variable sqlstr string;
-set variable sqlstr = 'alter table identifier(br_table) set tblproperties (\'elt.latest_run\' = \'' || current_timestamp() || '\')';
-execute immediate sqlstr;
-
-
--- COMMAND ----------
-
--- MAGIC %md
--- MAGIC ## Log load end
--- MAGIC Update load end time in the log table.
-
--- COMMAND ----------
-
-set variable load_end_time = current_timestamp();
-
--- COMMAND ----------
-
--- MAGIC %run "../04-Utility/Log Run"
-
--- COMMAND ----------
-
--- MAGIC %md
--- MAGIC # Populate integration table
--- MAGIC Validate, curate, and load incremental data into the integration table.
-
--- COMMAND ----------
-
--- MAGIC %md
--- MAGIC ## _Placeholder_
--- MAGIC
--- MAGIC _Validate incoming data
--- MAGIC Handle errors in the newly inserted data, before populating the curated data in the integration table._
--- MAGIC _Exception records (refs) can be captured in common table error table elt_error_table._
--- MAGIC
--- MAGIC
--- MAGIC _Steps would involve:_
--- MAGIC - _Checking business rules / mandatory data, and quarantining records_
--- MAGIC
--- MAGIC _For e.g.,_
--- MAGIC - _ID is null_
--- MAGIC - _CHANGEDONDATE is null_
--- MAGIC - _Older version_
-
--- COMMAND ----------
-
--- MAGIC %md
--- MAGIC Create table_changes temporary view to get unprocessed rows
-
--- COMMAND ----------
-
--- staging table changes
-set var sqlstr = "create or replace temporary view tc_br_table_tv as with vars as (select session.si_last_load_date) select * from table_changes('" || session.br_table || "', '" || session.si_last_load_date || "') where _change_type = 'insert'";
-
-execute immediate sqlstr;
-
--- COMMAND ----------
-
--- MAGIC %md
--- MAGIC ## Log load start
--- MAGIC
--- MAGIC Update load start time for integration table in the log table.
-
--- COMMAND ----------
-
-set variable load_table = session.si_table;
-set variable load_start_time = current_timestamp();
-set variable load_end_time = null;
-
--- COMMAND ----------
-
--- MAGIC %run "../04-Utility/Log Run"
-
--- COMMAND ----------
-
--- MAGIC %md
--- MAGIC ## Create transformation view
--- MAGIC The temporary view curates the data as follows:
--- MAGIC - Transforms columns
--- MAGIC - Standardizes code description for gender and ethnicity
--- MAGIC - Omits exception records
--- MAGIC
--- MAGIC The integration table is being treated as insert only.
-
--- COMMAND ----------
-
--- transform ingested source data
-create or replace temporary view si_transform_tv
-as
-with br_cdc as (
- select * from tc_br_table_tv
-),
-vars as (select session.si_table, session.br_table, session.si_last_load_date, session.code_table) -- required for identity(si_table) to work
-select
- id as patient_src_id,
- birthdate as date_of_birth,
- ssn as ssn,
- drivers as drivers_license,
- initcap(prefix) as name_prefix,
- first as first_name,
- last as last_name,
- suffix as name_suffix,
- maiden as maiden_name,
- gender as gender_cd,
- ifnull(code_gr.m_desc, gender) as gender_nm,
- marital as marital_status,
- ethnicity as ethnicity_cd,
- ifnull(code_ethn.m_desc, ethnicity) as ethnicity_nm,
- CHANGEDONDATE as src_changed_on_dt,
- data_source,
- current_timestamp() as insert_dt,
- current_timestamp() as update_dt,
- session.process_id as process_id
-from br_cdc
-left outer join identifier(session.code_table) code_gr on code_gr.m_code = br_cdc.gender and code_gr.m_type = 'GENDER'
-left outer join identifier(session.code_table) code_ethn on code_ethn.m_code = br_cdc.ethnicity and code_ethn.m_type = 'ETHNICITY'
-where
- id is not null and CHANGEDONDATE is not null -- these 2 conditions could be part of exception handling
-;
-
-
--- COMMAND ----------
-
--- MAGIC %md
--- MAGIC ## Insert data
--- MAGIC Insert data into the integration table using transformation view.
--- MAGIC
--- MAGIC Note: The design is to retain all versions of data, hence Insert. Else Merge.
-
--- COMMAND ----------
-
-
--- Insert new and changed data
-insert into identifier(si_table)
-select * from si_transform_tv
-;
-
-
--- COMMAND ----------
-
--- MAGIC %md
--- MAGIC ## Log load end
--- MAGIC Update load end time in the log table.
-
--- COMMAND ----------
-
-set variable load_end_time = current_timestamp();
-
--- COMMAND ----------
-
--- MAGIC %run "../04-Utility/Log Run"
-
--- COMMAND ----------
-
--- MAGIC %md
--- MAGIC # Populate dimension table
--- MAGIC The dimension table g_patient_d is created as a SCD2 dimension.
-
--- COMMAND ----------
-
--- MAGIC %md
--- MAGIC ## Log load start
--- MAGIC
--- MAGIC Update load start time for dimension table in the log table.
-
--- COMMAND ----------
-
-set variable load_table = session.gd_table;
-set variable load_start_time = current_timestamp();
-set variable load_end_time = null;
-
--- COMMAND ----------
-
--- MAGIC %run "../04-Utility/Log Run"
-
--- COMMAND ----------
-
--- MAGIC %md
--- MAGIC Create table_changes temporary view to get unprocessed rows
-
--- COMMAND ----------
-
--- integration table changes
-set var sqlstr = "create or replace temporary view tc_si_table_tv as with vars as (select session.gd_last_load_date) select * from table_changes('" || session.si_table || "', '" || session.gd_last_load_date || "') where _change_type = 'insert'";
-
-execute immediate sqlstr;
-
--- COMMAND ----------
-
--- MAGIC %md
--- MAGIC ## Create transformation view
--- MAGIC The view performs multiple functions:
--- MAGIC - Transform incoming rows as required
--- MAGIC - Create checksum
--- MAGIC - Handle new and changed instances
--- MAGIC - Handle multiple changes in a single batch
--- MAGIC - Ignore consecutive versions if no changes to business attributes of interest
--- MAGIC
--- MAGIC Note: The effective start date for a version is based on the CHANGEDONDATE as recieved from the source. This needs to be taken into consideration when doing a lookup of the patient table.
-
--- COMMAND ----------
-
--- MAGIC %md
--- MAGIC The view includes the following elements:
--- MAGIC 1. **CTE si_tc**
--- MAGIC This is used to transform any columns from the integration table
--- MAGIC 2. **CTE curr_v**
--- MAGIC Identify the Current Versions of all Patient instances corresponding to incoming data in this run.
--- MAGIC 3. **CTE ins_upd_rows**
--- MAGIC This CTE isolates new patients and new versions of existing patients for insert. This also identifies existing versions which need to be updated (potentially), to include an effective_end_date.
--- MAGIC Finally there is-
--- MAGIC 4. CTE no_dup_ver
--- MAGIC This is used to eliminate any updated records from the source for which there are no changes to business attributes of interest. For e.g., drivers_license is not being populated in the dimension. Suppose the only update in the source is to drivers_license, it will simply turn out to be a duplicate record.
-
--- COMMAND ----------
-
-create or replace temporary view dim_transform_tv
-as
-with vars as (select session.gd_table, session.gd_last_load_date), -- select the variables for use in later clauses
-si_tc as (
- select
- last_name, first_name, name_prefix, name_suffix, maiden_name,
- gender_cd as gender_code, gender_nm as gender,
- date_of_birth, nvl(marital_status, 'Not Available') as marital_status,
- ethnicity_cd as ethnicity_code, ethnicity_nm as ethnicity,
- ssn, null as other_identifiers, null as uda,
- patient_src_id, src_changed_on_dt as effective_start_date,
- md5(ifnull(last_name, '#') || ifnull(first_name, '#') || ifnull(name_prefix, '#') || ifnull(name_suffix, '#') || ifnull(maiden_name, '#') ||
- ifnull(gender_cd, '#') || ifnull(gender_nm, '#') || ifnull(date_of_birth, '#') || ifnull(marital_status, '#') || ifnull(ethnicity_cd, '#') || ifnull(ethnicity_nm, '#') || ifnull(ssn, '#')) as checksum,
- data_source
- from tc_si_table_tv
-),
-curr_v as (
- -- GET current version records in dimension table, if any, corresponding to incoming data
- select gd.* except (effective_end_date, insert_dt, update_dt, process_id)
- from identifier(session.gd_table) gd
- where effective_end_date is null and
- exists (select 1 from si_tc where si_tc.patient_src_id = gd.patient_src_id AND si_tc.data_source = gd.data_source)
-),
-ins_upd_rows as (
- -- ISOLATE new patients and new versions
- select null as patient_sk, * from si_tc
- union all
- -- use this to update effective_end_date of existing version in gd
- select * from curr_v
-),
-no_dup_ver as (
- -- IGNORE consecutive versions if no changes to business attributes of interest
- select
- *,
- lag(checksum, 1, null) over (partition by patient_src_id, data_source order by effective_start_date asc) as checksum_next
- from ins_upd_rows
- qualify checksum <> ifnull(checksum_next, '#') -- initially no records (for checksum_next)
-)
--- FINAL set (new patients and new versions, existing versions for updating effective_end_date)
-select
- *,
- lead(effective_start_date, 1, null) over (partition by patient_src_id order by effective_start_date asc) as effective_end_date
-from no_dup_ver
-;
-
-
--- COMMAND ----------
-
--- MAGIC %md
--- MAGIC ## Merge data
--- MAGIC Update the dimension table by:
--- MAGIC - Merge new and changed records.
--- MAGIC - Version existing patient records (by updating effective_end_date).
-
--- COMMAND ----------
-
-merge into identifier(session.gd_table) d
-using dim_transform_tv tr
-on d.patient_sk = tr.patient_sk
-when matched then update
- -- update end date for existing version of patient
- set d.effective_end_date = tr.effective_end_date,
- update_dt = current_timestamp(),
- process_id = session.process_id
-when not matched then insert (
- -- insert new vesrions and new patients
- last_name,
- first_name,
- name_prefix,
- name_suffix,
- maiden_name,
- gender_code,
- gender,
- date_of_birth,
- marital_status,
- ethnicity_code,
- ethnicity,
- ssn,
- patient_src_id,
- effective_start_date,
- effective_end_date,
- checksum,
- data_source,
- insert_dt,
- update_dt,
- process_id)
- values (last_name, first_name, name_prefix, name_suffix, maiden_name, gender_code, gender, date_of_birth, marital_status, ethnicity_code,
- ethnicity, ssn, patient_src_id, effective_start_date, effective_end_date, checksum, data_source, current_timestamp(), current_timestamp(), session.process_id)
-;
-
-
--- COMMAND ----------
-
--- MAGIC %md
--- MAGIC ## Log load end
--- MAGIC Update load end time in the log table.
-
--- COMMAND ----------
-
-set variable load_end_time = current_timestamp();
-
--- COMMAND ----------
-
--- MAGIC %run "../04-Utility/Log Run"
\ No newline at end of file
From eb6a3711427c760545d4c8ca1a21802ece60ece2 Mon Sep 17 00:00:00 2001
From: shyamraodb <129457417+shyamraodb@users.noreply.github.com>
Date: Tue, 25 Feb 2025 17:49:41 -0800
Subject: [PATCH 09/43] Add files via upload
---
.../_data/patients50_init.csv | 51 +++++++++++++++++++
.../_data/patients_incr1.csv | 10 ++++
.../_data/patients_incr2.csv | 10 ++++
3 files changed, 71 insertions(+)
create mode 100644 product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/_data/patients50_init.csv
create mode 100644 product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/_data/patients_incr1.csv
create mode 100644 product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/_data/patients_incr2.csv
diff --git a/product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/_data/patients50_init.csv b/product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/_data/patients50_init.csv
new file mode 100644
index 00000000..9f566345
--- /dev/null
+++ b/product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/_data/patients50_init.csv
@@ -0,0 +1,51 @@
+Id,BIRTHDATE,DEATHDATE,SSN,DRIVERS,PASSPORT,PREFIX,FIRST,LAST,SUFFIX,MAIDEN,MARITAL,RACE,ETHNICITY,GENDER,BIRTHPLACE,ADDRESS,CITY,STATE,COUNTY,ZIP,LAT,LON,HEALTHCARE_EXPENSES,HEALTHCARE_COVERAGE,CHANGEDONDATE
+b9c610cd-28a6-4636-ccb6-c7a0d2a4cb85,2019-02-17,,999-65-3251,,,,Damon455,Langosh790,,,,white,nonhispanic,M,Middleborough Massachusetts US,620 Lynch Tunnel Apt 0,Springfield,Massachusetts,Hampden County,1104,42.08038943,-72.48043145,9039.1645,7964.1255,2024-01-01 0:00:00
+c1f1fcaa-82fd-d5b7-3544-c8f9708b06a8,2005-07-04,,999-49-3323,S99941126,,,Thi53,Wunsch504,,,,white,nonhispanic,F,Danvers Massachusetts US,972 Tillman Branch Suite 48,Bellingham,Massachusetts,Norfolk County,,42.03521336,-71.48251905,402723.415,14064.135,2024-01-01 0:00:00
+339144f8-50e1-633e-a013-f361391c4cff,1998-05-11,,999-10-8743,S99996708,X75063318X,Mr.,Chi716,Greenfelder433,,,,white,nonhispanic,M,Athens Athens Prefecture GR,1060 Bernhard Crossroad Suite 15,Boston,Massachusetts,Suffolk County,2131,42.29255662,-71.06116042,571935.8725,787.5375,2024-01-01 0:00:00
+d488232e-bf14-4bed-08c0-a82f34b6a197,2003-01-28,,999-56-6057,S99929424,,Ms.,Phillis443,Walter473,,,,white,nonhispanic,F,Boston Massachusetts US,677 Ritchie Terrace,Hingham,Massachusetts,Plymouth County,2043,42.20049082,-70.91607558,582557.803,104782.207,2024-01-01 0:00:00
+217f95a3-4e10-bd5d-fb67-0cfb5e8ba075,1993-12-23,,999-91-4320,S99991143,X44132498X,Mr.,Jerrold404,Herzog843,,,M,black,nonhispanic,M,Boston Massachusetts US,276 Bernier Branch,Revere,Massachusetts,Suffolk County,,42.38187488,-70.99928578,475826.855,18067.095,2024-01-01 0:00:00
+faac724a-a9e9-be66-fe1e-3044dc0ba8ea,2020-12-04,,999-31-8261,,,,Brandon214,Watsica258,,,,white,nonhispanic,F,Northborough Massachusetts US,734 Brakus Pathway Unit 28,New Marlborough,Massachusetts,Berkshire County,,42.14702801,-73.2171032,31468.04,206.22,2024-01-01 0:00:00
+23d16ee3-8cd4-eeb8-e77e-1e5fbf4c4159,1990-12-15,,999-60-9905,S99950633,X36202735X,Mr.,Rodrigo242,Arellano2,,,M,black,hispanic,M,Bogota Bogota CO,538 Crona Underpass,Revere,Massachusetts,Suffolk County,2151,42.4432615,-71.02232158,772362.725,2213.085,2024-01-01 0:00:00
+aade3c61-92bd-d079-9d28-0b2b7fde0fbb,1985-06-05,,999-93-3123,S99913090,X72720258X,Mr.,Sal878,Hoppe518,,,M,white,nonhispanic,M,Merrimac Massachusetts US,930 Goldner Stravenue Apt 8,Somerville,Massachusetts,Middlesex County,2138,42.36345987,-71.10225287,924957.2675,874.3725,2024-01-01 0:00:00
+0288c42c-43a1-9878-4a9d-6b96caa12c40,1979-12-17,,999-68-7908,S99962050,X4451825X,Mrs.,Miki234,Kozey370,,Morar593,M,white,nonhispanic,F,Northbridge Massachusetts US,636 Stamm Gateway,Cambridge,Massachusetts,Middlesex County,2472,42.33742542,-71.1066547,1134817.571,226909.9595,2024-01-01 0:00:00
+dc6c06d0-a7d8-100f-c08b-46c93700c188,2006-07-11,,999-30-6555,,,,Taylor21,Gulgowski816,,,,white,nonhispanic,M,East Longmeadow Massachusetts US,1037 Hills Extension,Springfield,Massachusetts,Hampden County,1013,42.13505594,-72.48174986,390675.5775,1826.3325,2024-01-01 0:00:00
+61a2fcc0-d679-764c-7d86-b885b2c4907f,1993-01-22,,999-98-2186,S99988392,X72124702X,Mrs.,Erline657,Metz686,,Strosin214,M,white,nonhispanic,F,Mashpee Massachusetts US,566 Rosenbaum Ferry,Holbrook,Massachusetts,Norfolk County,,42.15188021,-70.95230839,1223457.81,160.14,2024-01-01 0:00:00
+c0219ca9-576f-f7c2-9c44-de030e94969b,1971-12-06,,999-24-2120,S99951566,X2456540X,Mrs.,Beatriz277,Urías338,,Villalpando154,M,white,hispanic,F,Lima Lima Province PE,497 Bahringer Crossing,Somerville,Massachusetts,Middlesex County,2138,42.37578549,-71.09039667,1509753.911,968261.839,2024-01-01 0:00:00
+cb8c092a-99aa-6c9c-1ec1-660f0b00549d,1990-12-28,,999-74-6572,S99980075,X57149020X,Mrs.,Kari181,Douglas31,,Langworth352,M,white,nonhispanic,F,Framingham Massachusetts US,413 Auer Burg Suite 64,Wellesley,Massachusetts,Norfolk County,2457,42.330604,-71.30822396,1040518.45,22454.94,2024-01-01 0:00:00
+97a20cf9-630d-939c-2f50-f13c434aee2f,1984-03-23,,999-16-8479,S99946662,X72015493X,Mrs.,Mayme605,Walter473,,Lesch175,M,white,nonhispanic,F,Blackstone Massachusetts US,441 Rodriguez Bridge,Boston,Massachusetts,Suffolk County,2129,42.36692392,-71.06483074,1622874.118,74751.4725,2024-01-01 0:00:00
+55a6a46e-a1a4-0298-e58f-c0cb27cbb022,1958-09-18,2009-11-13,999-79-5115,S99956983,X26820988X,Mrs.,Maddie576,Gutmann970,,Blanda868,M,white,nonhispanic,F,Boston Massachusetts US,577 Schmeler Frontage road Unit 7,Quincy,Massachusetts,Norfolk County,2169,42.31470216,-71.0270053,2895617.604,312276.306,2024-01-01 0:00:00
+961f61f8-ed32-f113-8450-192064b49aa9,2002-06-13,,999-91-5120,S99910835,,Ms.,Jessia669,Wisoky380,,,,white,nonhispanic,F,Southwick Massachusetts US,106 Kerluke Rest Apt 5,Abington,Massachusetts,Plymouth County,2351,42.15407877,-70.95799629,485313.59,1825.08,2024-01-01 0:00:00
+9acc871f-b577-5530-b8ad-fa95b58cea25,1985-03-24,,999-59-7726,S99918599,X14875434X,Mrs.,Lavinia262,Heaney114,,Maggio310,M,white,nonhispanic,F,Cambridge Massachusetts US,446 Kreiger Route,Brookline,Massachusetts,Norfolk County,2446,42.29712691,-71.14523957,1425692.509,154972.8415,2024-01-01 0:00:00
+1d3d0ba6-5475-07c2-0b40-562ad75fdf26,1960-01-21,,999-26-4708,S99981133,X33683613X,Mrs.,Laurena366,Heidenreich818,,Becker968,M,white,nonhispanic,F,Lawrence Massachusetts US,1039 Gerhold Ramp Apt 20,East Longmeadow,Massachusetts,Hampden County,,42.08768744,-72.52656679,774382.057,247928.613,2024-01-01 0:00:00
+98e4223a-5ad8-94e0-2d73-ee5203dd4e2b,2008-11-09,,999-92-2908,,,,Delfina519,Parisian75,,,,asian,nonhispanic,F,Boston Massachusetts US,752 Heller Vale Apt 5,Everett,Massachusetts,Middlesex County,2149,42.40872191,-71.05759674,162931.8475,1462.6425,2024-01-01 0:00:00
+8fa5a097-1970-9370-4193-a7baa3d235b5,2000-08-25,,999-84-8860,S99941898,X42921765X,Ms.,Verla696,Hamill307,,,,white,nonhispanic,F,Waltham Massachusetts US,804 Mohr Lodge Apt 92,Everett,Massachusetts,Middlesex County,2148,42.45174076,-71.07017883,214600.8025,115026.0475,2024-01-01 0:00:00
+8c2d5e9b-0717-9616-beb9-21296a5b547d,1960-12-15,,999-50-8690,S99926520,X57197008X,Ms.,Zelda766,Ernser583,,,S,white,nonhispanic,F,Amherst Massachusetts US,874 Skiles Club,Haverhill,Massachusetts,Essex County,1830,42.77589674,-71.00661558,2341031.138,177213.0915,2024-01-01 0:00:00
+a7624ac9-6308-e015-1c5c-115c97c631fe,1959-11-02,,999-16-7928,S99982440,X77033125X,Mrs.,Esther279,Runolfsson901,,Feil794,M,white,nonhispanic,F,Billerica Massachusetts US,389 Mraz Pathway Apt 38,Waltham,Massachusetts,Middlesex County,2453,42.3998323,-71.20603836,2286986.704,480203.186,2024-01-01 0:00:00
+122b4063-18dc-f20f-204c-6f2da758717b,2018-09-16,,999-63-4674,,,,Carlota980,Morales679,,,,white,hispanic,F,Santiago de los Caballeros Santiago DO,990 Morissette Parade,Boston,Massachusetts,Suffolk County,,42.29059438,-71.09216721,10334.434,7684.246,2024-01-01 0:00:00
+33c2ca32-ae38-66c5-474f-83273568906e,1955-10-25,,999-85-9194,S99955746,X89300658X,Mr.,Franklyn361,Tromp100,,,M,white,hispanic,M,Westford Massachusetts US,394 Weissnat Meadow,Georgetown,Massachusetts,Essex County,,42.76725123,-71.02479919,1860937.735,52453.715,2024-01-01 0:00:00
+57b53310-98a9-f011-69c0-02068c37d83b,1972-07-13,,999-37-8837,S99918279,X61102329X,Mrs.,Tommye961,Simonis280,,Franecki195,M,white,nonhispanic,F,Newton Massachusetts US,893 McGlynn Street,Plymouth,Massachusetts,Plymouth County,2360,41.88295424,-70.5823612,1973363.844,641759.8265,2024-01-01 0:00:00
+c9b4f8c6-d7be-6563-0560-c90cfe6b12ca,2003-10-08,,999-17-8551,S99937265,,Ms.,Pei116,Williamson769,,,,white,nonhispanic,F,Randolph Massachusetts US,996 Kemmer Port Unit 36,Woburn,Massachusetts,Middlesex County,1890,42.50824308,-71.17262434,1220556.95,0,2024-01-01 0:00:00
+1f528581-7613-2b96-dce3-68c7cf967ea1,1985-04-03,,999-82-4137,S99935035,X77363212X,Mrs.,Bonita405,Bernier607,,Hagenes547,M,white,nonhispanic,F,Dover Massachusetts US,377 Sauer Rapid Apt 46,Whitman,Massachusetts,Plymouth County,,42.07379171,-70.9283541,2223556.528,219554.6725,2024-01-01 0:00:00
+7024858b-f5e2-efee-8e2f-67f503fef6a5,1969-01-03,1997-02-17,999-91-3005,S99927148,X79375706X,Mrs.,Rosalia943,Klocko335,,Herzog843,M,white,nonhispanic,F,Boston Massachusetts US,898 Wisoky Green,Lynn,Massachusetts,Essex County,1906,42.50610365,-70.92554339,686276.755,24752.235,2024-01-01 0:00:00
+1cfa5a70-7f3c-4227-5cf1-e182fcff4cd4,1958-09-18,,999-24-1375,S99923833,X21908759X,Mrs.,Alaine226,Willms744,,Funk324,M,white,nonhispanic,F,North Westport Massachusetts US,112 Metz Track,Quincy,Massachusetts,Norfolk County,2186,42.25000925,-70.97805949,2056353.781,599891.699,2024-01-01 0:00:00
+2f031d4a-b070-ce15-6372-30c8fecf1164,1994-09-30,,999-47-2075,S99936245,X18832457X,Ms.,Magdalene960,Orn563,,,S,asian,nonhispanic,F,Shanghai Shanghai Municipality CN,206 Harber Meadow,Lexington,Massachusetts,Middlesex County,2420,42.4005015,-71.18772152,1565996.562,406247.948,2024-01-01 0:00:00
+9a632ddc-b352-6dd7-0fb3-916fde80c9a3,1989-02-22,,999-66-5814,S99917255,X27930221X,Mr.,Shaun461,Dickens475,,,M,white,hispanic,M,Swampscott Massachusetts US,484 Bernier Orchard,Lunenburg,Massachusetts,Worcester County,,42.56494812,-71.71071868,797287.09,2312.82,2024-01-01 0:00:00
+56bf1a47-8065-1ba5-6d8c-430b37a621bf,2007-06-22,,999-56-2288,,,,Denny560,Watsica258,,,,asian,nonhispanic,M,Boston Massachusetts US,112 Hintz Annex Unit 63,Concord,Massachusetts,Middlesex County,,42.4816292,-71.3956611,328040.88,0,2024-01-01 0:00:00
+7d1a0d44-6f3e-2cd5-3347-0d97e5b57612,1980-12-04,,999-18-4055,S99975869,X57447826X,Mr.,Doyle959,Johns824,,,M,white,nonhispanic,M,Clinton Massachusetts US,828 Jaskolski Well Apt 88,Reading,Massachusetts,Middlesex County,,42.46877404,-71.14837512,1008922.16,443.04,2024-01-01 0:00:00
+8bf6aa92-645f-3c82-ddcd-5851496a6aa8,1967-05-07,,999-57-2811,S99974478,X11431912X,Mrs.,Ethelyn789,Vandervort697,,Kling921,M,white,nonhispanic,F,Lynn Massachusetts US,372 Jakubowski Vista Unit 76,Easton,Massachusetts,Bristol County,,42.06241934,-71.12403926,2008486.939,409753.3915,2024-01-01 0:00:00
+f0660412-c40e-4d5c-5b11-08b6da289331,1975-01-24,,999-42-9701,S99913944,X52330687X,Mr.,Terry864,King743,,,M,white,nonhispanic,M,Lynn Massachusetts US,827 Kling Burg Suite 32,Buzzards Bay,Massachusetts,Barnstable County,2542,41.7787118,-70.57317837,1196488.853,3228.8775,2024-01-01 0:00:00
+53cc5b94-3c84-3ecf-ae94-f98203e3d8ba,2014-03-07,,999-85-1461,,,,Elwood28,Gottlieb798,,,,white,nonhispanic,M,New Bedford Massachusetts US,289 Mraz Knoll,Braintree,Massachusetts,Norfolk County,2184,42.21994826,-70.97768016,189892.04,0,2024-01-01 0:00:00
+060e72d3-912e-55cd-0c92-5faa6cb7a6db,1969-01-03,,999-66-6825,S99931952,X14008939X,Mr.,Benny518,Tillman293,,,S,white,nonhispanic,M,Medford Massachusetts US,941 Rodriguez Trace Suite 61,Quincy,Massachusetts,Norfolk County,2169,42.24005776,-70.98774746,181651.842,262994.998,2024-01-01 0:00:00
+18f70d07-28a0-17f3-2605-bf21cceb194b,2020-02-08,,999-52-7055,,,,Ernestina649,Fahey393,,,,white,nonhispanic,F,Medford Massachusetts US,631 Muller Junction Apt 77,Cambridge,Massachusetts,Middlesex County,2141,42.35348724,-71.06795193,46898.77,56.22,2024-01-01 0:00:00
+a7b903a5-5c6f-3f5e-4035-62d94b0b52b3,1995-12-21,,999-21-2434,S99916617,X56847635X,Ms.,Lavonia8,Abernathy524,,,,white,nonhispanic,F,Boxford Massachusetts US,702 Hane Boulevard,Medford,Massachusetts,Middlesex County,2145,42.41667897,-71.08159303,694925.9575,61101.1725,2024-01-01 0:00:00
+63198606-7c07-e63a-42a3-2f47419c881c,2015-01-22,,999-39-5566,,,,Rashida558,Murray856,,,,white,nonhispanic,F,Westborough Massachusetts US,400 Crooks Orchard,Everett,Massachusetts,Middlesex County,,42.43031721,-71.01885713,188478.56,131.22,2024-01-01 0:00:00
+2dacba2b-f4f3-9726-0f13-2f1a87f69bba,1988-03-15,,999-31-2745,S99997263,X23541953X,Mrs.,Margarite168,Boyer713,,Botsford977,M,white,nonhispanic,F,Southwick Massachusetts US,340 Ullrich Viaduct Unit 53,Waltham,Massachusetts,Middlesex County,2453,42.32625114,-71.21393961,1371909.648,223046.0925,2024-01-01 0:00:00
+7d28d76a-9ac8-67b4-3c88-0a75be3d0851,1969-01-03,,999-55-6195,S99969519,X56639424X,Mrs.,Erna640,Robel940,,Kuhic920,M,white,nonhispanic,F,Boston Massachusetts US,974 Nienow Fork,Lynn,Massachusetts,Essex County,1907,42.48839988,-70.92033042,1849843.516,396119.7145,2024-01-01 0:00:00
+307b6419-5147-4716-10b1-bb458ac191c3,2003-07-19,,999-53-6468,S99966610,,Ms.,Micheal721,Shields502,,,,white,nonhispanic,F,Worcester Massachusetts US,933 Jast Plaza,Fairhaven,Massachusetts,Bristol County,,41.65030477,-70.89413719,486024.1225,2349.0675,2024-01-01 0:00:00
+0afb59a5-12cb-3b6a-eead-3a72d24fb678,1980-12-05,,999-49-6212,S99937895,X21978831X,Mr.,Hershel911,Lang846,MD,,S,white,nonhispanic,M,Webster Massachusetts US,976 Abernathy Fort Unit 80,Holbrook,Massachusetts,Norfolk County,2343,42.20045424,-71.05023864,1034683.757,158.0625,2024-01-01 0:00:00
+38565670-114d-6c46-50b9-76bbffb16ffb,1970-09-14,,999-61-2551,S99975387,X9893460X,Mrs.,Christal240,Glover433,,Ruecker817,M,white,nonhispanic,F,Boston Massachusetts US,448 Davis Drive Unit 60,Lawrence,Massachusetts,Essex County,1840,42.74837964,-71.1691556,1117212.885,197427.3755,2024-01-01 0:00:00
+4f5a07ca-02e0-8981-3c30-4d9924a169a3,1983-12-12,,999-37-6244,S99973987,X64774243X,Mr.,Tony646,Ward668,,,M,white,nonhispanic,M,Worcester Massachusetts US,902 Gutkowski Hollow,Peabody,Massachusetts,Essex County,1940,42.53098046,-70.99559809,907036.61,9573.15,2024-01-01 0:00:00
+6a6a1b30-9966-bd68-e732-b7ce2c0b6ade,2013-11-19,,999-17-5887,,,,Roberto515,Quiroz936,,,,white,hispanic,M,Portsmouth Saint John Parish DM,759 Schimmel Hollow Unit 24,Haverhill,Massachusetts,Essex County,1835,42.77414224,-71.10091261,201925.44,1434.3,2024-01-01 0:00:00
+be82309d-1a8f-df82-4cd6-5f03e1060e8e,1970-03-07,,999-49-3900,S99998879,X20800283X,Mr.,Felton646,Pouros728,,,M,white,nonhispanic,M,Weymouth Massachusetts US,805 Greenfelder Heights,Shirley,Massachusetts,Middlesex County,,42.58359111,-71.60138307,1168474.663,125.5275,2024-01-01 0:00:00
+5e2cd800-901b-55d3-edf4-ab6e3c7524ff,1983-03-13,,999-31-9450,S99941509,X19453034X,Mr.,Jonathon205,Rice937,JD,,S,white,nonhispanic,M,Beverly Massachusetts US,856 Corkery Ferry Apt 50,Somerville,Massachusetts,Middlesex County,2138,42.36139427,-71.11072242,978259.775,23108.145,2024-01-01 0:00:00
+f93c78a3-57b8-4858-cad5-e449f26e3857,2008-12-10,,999-80-8776,,,,Man114,Halvorson124,,,,white,nonhispanic,M,Marblehead Massachusetts US,644 Abshire Park Suite 19,Brockton,Massachusetts,Plymouth County,,42.09331989,-70.94942531,327035.025,2953.305,2024-01-01 0:00:00
diff --git a/product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/_data/patients_incr1.csv b/product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/_data/patients_incr1.csv
new file mode 100644
index 00000000..3f27c38e
--- /dev/null
+++ b/product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/_data/patients_incr1.csv
@@ -0,0 +1,10 @@
+Id,BIRTHDATE,DEATHDATE,SSN,DRIVERS,PASSPORT,PREFIX,FIRST,LAST,SUFFIX,MAIDEN,MARITAL,RACE,ETHNICITY,GENDER,BIRTHPLACE,ADDRESS,CITY,STATE,COUNTY,ZIP,LAT,LON,HEALTHCARE_EXPENSES,HEALTHCARE_COVERAGE,CHANGEDONDATE
+5e2cd800-901b-55d3-edf4-ab6e3c7524ff,1973-03-13,,999-31-9450,S99941509,X19453034X,Mr.,Jonathon205,Rice937,JD,,S,white,nonhispanic,M,Beverly Massachusetts US,856 Corkery Ferry Apt 50,Somerville,Massachusetts,Middlesex County,2138,42.36139427,-71.11072242,978259.775,23108.145,2024-02-12 0:00:00
+62bb08df-1e20-441c-6fea-94c47a30690a,1980-05-21,,999-86-6189,S99916713,X77230309X,Mrs.,Leonie332,Monahan736,,Raynor401,M,white,nonhispanic,F,Smith Mills Massachusetts US,497 Hegmann Gateway Suite 26,Newburyport,Massachusetts,Essex County,,42.80701595,-70.92184927,1273822.643,48322.1175,2024-02-12 0:00:00
+d1822991-eb4f-aafe-6a88-cd5279b4385b,2017-01-09,,999-39-2759,,,,Solomon675,Moore224,,,,white,nonhispanic,M,Peabody Massachusetts US,948 Wilkinson Passage,Lancaster,Massachusetts,Worcester County,,42.52814462,-71.70135048,127579.35,32828.16,2024-02-15 0:00:00
+5b2f8ddd-408a-884c-00e9-047b13e6cc45,1999-07-06,,999-71-4100,S99988351,X19730009X,Mr.,Refugio197,Ratke343,,,,white,nonhispanic,M,Lowell Massachusetts US,692 Lakin View Unit 66,Lynn,Massachusetts,Essex County,1901,42.43587593,-70.99343488,659431.81,31019.67,2024-02-17 0:00:00
+afd63046-5213-0c98-561f-12e6725cd1f5,1956-10-25,,999-88-6383,S99989633,X29496773X,Mrs.,Pattie91,West559,,Littel644,M,black,nonhispanic,F,Lowell Massachusetts US,1020 Dibbert Crossroad,Belmont,Massachusetts,Middlesex County,2472,42.40760795,-71.16850973,2090761.783,420932.647,2024-02-02 0:00:00
+ceec80e3-5c50-be88-198c-e98375c8e8a2,1977-09-12,,999-47-3565,S99925368,X8596122X,Mr.,Gerry91,Treutel973,,,M,white,nonhispanic,M,Lowell Massachusetts US,830 Lebsack Brook,Boston,Massachusetts,Suffolk County,2130,42.31900043,-71.0600982,1459667.655,1421.925,2024-02-22 0:00:00
+19e3f2b0-8fd1-a8ae-2767-f0c89005b8d2,2010-11-27,,999-97-1329,,,,Keena534,Balistreri607,,,,asian,hispanic,F,Holliston Massachusetts US,938 Becker Common Unit 43,Revere,Massachusetts,Suffolk County,,42.39841671,-70.94689598,152495.905,2362.605,2024-02-02 0:00:00
+,2020-02-02,,,,,,,,,,,,,,,,,,,,,,,,2024-02-02 0:00:00
+3a74ce3c-442e-b4ef-fb10-560f58f8e613,1997-03-26,,999-49-2054,S99976277,X9668781X,Mr.,Leif534,Williamson769,,,,white,nonhispanic,M,Boston Massachusetts US,131 Shanahan Hollow Unit 98,Amherst,Massachusetts,Hampshire County,,42.3224843,-72.48889382,79244.404,30103.676,2024-02-07 0:00:00
\ No newline at end of file
diff --git a/product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/_data/patients_incr2.csv b/product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/_data/patients_incr2.csv
new file mode 100644
index 00000000..2bfb2a52
--- /dev/null
+++ b/product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/_data/patients_incr2.csv
@@ -0,0 +1,10 @@
+Id,BIRTHDATE,DEATHDATE,SSN,DRIVERS,PASSPORT,PREFIX,FIRST,LAST,SUFFIX,MAIDEN,MARITAL,RACE,ETHNICITY,GENDER,BIRTHPLACE,ADDRESS,CITY,STATE,COUNTY,ZIP,LAT,LON,HEALTHCARE_EXPENSES,HEALTHCARE_COVERAGE,CHANGEDONDATE
+3a74ce3c-442e-b4ef-fb10-560f58f8e613,1997-03-26,,999-49-2054,S99976277,X9668781X,Mr.,Leif534,Williamson769,III,,S,white,nonhispanic,M,Boston Massachusetts US,131 Shanahan Hollow Unit 98,Amherst,Massachusetts,Hampshire County,,42.3224843,-72.48889382,79244.404,30103.676,2024-03-03 0:00:00
+04f25f73-04b2-469c-3806-540417a0d61c,1969-10-19,2005-11-09,999-93-4258,S99913030,X20390688X,Mrs.,Maye976,Dickinson688,,Glover433,M,white,hispanic,F,Plymouth Massachusetts US,392 Renner Divide Suite 57,Woburn,Massachusetts,Middlesex County,,42.4784717,-71.16214462,2815496.547,7908.8925,2024-03-04 0:00:00
+3ff3637c-86a6-6d45-8e41-f1b2c96fbcf4,2010-01-13,,999-95-3226,,,,Rosana394,Kessler503,,,,white,nonhispanic,F,Mashpee Massachusetts US,276 Deckow Tunnel Unit 19,Lowell,Massachusetts,Middlesex County,,42.60471483,-71.30423463,298852.3775,543.9825,2024-03-03 0:00:00
+6b3a5686-0f63-5ce4-cc03-0bba2052f7b1,1958-10-28,,999-96-8207,S99914224,X43330625X,Mrs.,Lashawnda573,O'Hara248,,Greenfelder433,M,white,nonhispanic,F,Deerfield Massachusetts US,230 Robel Drive Suite 95,Lowell,Massachusetts,Middlesex County,1851,42.63165279,-71.37344386,1210508.126,1210783.844,2024-03-03 0:00:00
+fcca54e8-5abe-cd71-30a0-464f15368bea,1993-12-12,,999-93-3994,S99954518,X33164416X,Mrs.,Oneida64,Stokes453,,Lemke654,M,white,nonhispanic,F,Revere Massachusetts US,1085 Grant Boulevard,Marblehead,Massachusetts,Essex County,,42.49291246,-70.82288754,1814465.263,3730.7175,2024-03-23 0:00:00
+5e2cd800-901b-55d3-edf4-ab6e3c7524ff,1973-03-13,,999-31-9450,S99941509,X19453034X,Mr.,Jonathon205,Rice937,JD,bulbul,M,white,nonhispanic,M,Beverly Massachusetts US,856 Corkery Ferry Apt 50,Somerville,Massachusetts,Middlesex County,2138,42.36139427,-71.11072242,978259.775,23108.145,2024-03-21 0:00:00
+6b3a5686-0f63-5ce4-cc03-0bba2052f7b1,1958-10-28,,999-96-8207,S99914224CA,X43330625X-MODIFIED,Mrs.,Lashawnda573,O'Hara248,,Greenfelder433,M,white,nonhispanic,F,Deerfield Massachusetts US,230 Robel Drive Suite 95,Lowell,Massachusetts,Middlesex County,1851,42.63165279,-71.37344386,1210508.126,1210783.844,2024-03-13 0:00:00
+12345678,,,999-11-1111,,,,,,,,,,,,,,,,,,,,,,
+63198606-7c07-e63a-42a3-2f47419c881c,2015-01-22,,999-39-5566,,,,Rashida558,Murray856,,S,,white,nonhispanic,F,Westborough Massachusetts US,400 Crooks Orchard,Everett,Massachusetts,Middlesex County,2149,42.43031721,-71.01885713,188478.56,131.22,2024-03-23 0:00:00
\ No newline at end of file
From 2bd85c7fe252acc2e7ca4ae97f6e355237205fa4 Mon Sep 17 00:00:00 2001
From: shyamraodb <129457417+shyamraodb@users.noreply.github.com>
Date: Tue, 25 Feb 2025 17:54:22 -0800
Subject: [PATCH 10/43] Update initialize-staging.py
---
.../dbsql-for-dim-etl/_util/initialize-staging.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/_util/initialize-staging.py b/product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/_util/initialize-staging.py
index 6a767110..543f7212 100644
--- a/product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/_util/initialize-staging.py
+++ b/product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/_util/initialize-staging.py
@@ -34,8 +34,8 @@ def get_file(file_name, entity_name):
import requests
owner = "shyamraodb"
- repo = "star-schema-elt"
- path = "seed"
+ repo = "dbdemos-notebooks"
+ path = "product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/_data"
files = requests.get(f'https://api.github.com/repos/{owner}/{repo}/contents/{path}').json()
@@ -43,4 +43,4 @@ def get_file(file_name, entity_name):
down_url = [f['download_url'] for f in files if file_name in f['name']][0]
destination = stg_path + "/" + entity_name + "/" + file_name
- download_file(down_url, destination)
\ No newline at end of file
+ download_file(down_url, destination)
From 58de132028fcb8ceaaa0e518785380126bdd23be Mon Sep 17 00:00:00 2001
From: shyamraodb <129457417+shyamraodb@users.noreply.github.com>
Date: Tue, 25 Feb 2025 18:05:59 -0800
Subject: [PATCH 11/43] Delete
product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/04-Utility directory
---
.../dbsql-for-dim-etl/04-Utility/Log Run.sql | 26 -------------------
1 file changed, 26 deletions(-)
delete mode 100644 product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/04-Utility/Log Run.sql
diff --git a/product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/04-Utility/Log Run.sql b/product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/04-Utility/Log Run.sql
deleted file mode 100644
index b952be3f..00000000
--- a/product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/04-Utility/Log Run.sql
+++ /dev/null
@@ -1,26 +0,0 @@
--- Databricks notebook source
-declare or replace variable op_sql string;
-
--- COMMAND ----------
-
--- to obtain the results of the previous DML
-set variable op_sql = "
- create or replace temporary view metrics_tv
- as
- select coalesce(operationMetrics.numTargetRowsInserted, operationMetrics.numOutputRows) as num_inserted, operationMetrics.numTargetRowsUpdated as num_updated from (describe history " || session.load_table || ") where operation in ('MERGE', 'WRITE', 'COPY INTO') limit 1
-";
-
--- COMMAND ----------
-
--- create metrics view
-execute immediate op_sql;
-
--- COMMAND ----------
-
-merge into identifier(session.run_log_table) as t
-using (select * from values (session.data_source, session.load_table, session.load_start_time, session.load_end_time, session.process_id) as
- source(data_source, table_name, load_start_time, load_end_time, process_id))
-on (t.data_source = source.data_source and t.table_name = source.table_name and t.load_start_time = source.load_start_time)
-when matched then update set t.load_end_time = source.load_end_time, t.num_inserts = (select num_inserted from metrics_tv), t.num_updates = (select num_updated from metrics_tv)
-when not matched then insert (data_source, table_name, load_start_time, process_id) values (source.data_source, source.table_name, source.load_start_time, source.process_id)
-;
\ No newline at end of file
From abd76d5ccf0f9dabaa72ab6b65988d18bc9ca405 Mon Sep 17 00:00:00 2001
From: shyamraodb <129457417+shyamraodb@users.noreply.github.com>
Date: Wed, 26 Feb 2025 10:02:10 -0800
Subject: [PATCH 12/43] Add files via upload
---
.../_images/data_flow_no_excpt.png | Bin 0 -> 343126 bytes
.../dbsql-for-dim-etl/_images/patient_data.png | Bin 0 -> 279417 bytes
.../_images/patient_tables_dw.png | Bin 0 -> 262844 bytes
3 files changed, 0 insertions(+), 0 deletions(-)
create mode 100644 product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/_images/data_flow_no_excpt.png
create mode 100644 product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/_images/patient_data.png
create mode 100644 product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/_images/patient_tables_dw.png
diff --git a/product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/_images/data_flow_no_excpt.png b/product_demos/DBSQL-Datawarehousing/dbsql-for-dim-etl/_images/data_flow_no_excpt.png
new file mode 100644
index 0000000000000000000000000000000000000000..307df5fd0689254e4daca5a6a20652c9ca8db125
GIT binary patch
literal 343126
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