Healthcare FHIR Clinical Data Warehouse Best Practices

Ingest HL7 FHIR-formatted clinical data (patients, encounters, observations, medications) into a multi-layer data warehouse that supports clinical quality metric calculation and healthcare cost management. This guide uses a simulated FHIR JSON dataset of 5 patients, 5 encounters, 7 observations, and 6 medication requests to demonstrate the end-to-end build process: ODS (raw FHIR JSON) → DWD (parsing layer) → DWS (aggregation layer) → ADS (clinical quality metrics). It covers four key platform capabilities: nested JSON field extraction, Dynamic Table incremental computation, Column Masking for PHI de-identification, and Time Travel historical snapshots.


Overview

The core challenges of a healthcare FHIR clinical data warehouse are: FHIR messages are stored as nested JSON, each of the four resource types (Patient, Encounter, Observation, MedicationRequest) has a different structure requiring field extraction before joining, and PHI (Protected Health Information) fields such as patient names and birth dates must be masked for non-privileged users.

ProblemSingdata Solution
Deeply nested FHIR JSON (name[0].given[0], reasonCode[0].coding[0].code, etc.)get_json_object + JSONPath syntax to extract any level of nesting on demand
Four FHIR resource types need to be joined for analysisDynamic Table with declarative JOIN SQL; system automatically refreshes incrementally
ODS → DWD → DWS → ADS data pipelineDynamic Table chained dependencies; downstream refreshes automatically when upstream is updated
PHI fields such as patient name and birth date must be maskedColumn Masking bound to columns, transparently applied to non-privileged users
Insurance reconciliation requires historical monthly data snapshotsTime Travel with TIMESTAMP AS OF syntax to query data at any point in time
Clinical pathway compliance rates and other quality metrics updated dailyADS layer Dynamic Table + Lakehouse Studio Task with daily scheduled refresh

SQL Commands Used

Command / FunctionPurposeNotes
CREATE TABLECreate ODS layer FHIR raw tablesOne table per resource type, storing complete JSON
get_json_objectExtract fields from FHIR JSON using JSONPathSupports $ paths; arrays use [0] index
CREATE DYNAMIC TABLEBuild DWD / DWS / ADS layersDeclarative SQL; system handles incremental computation automatically
REFRESH DYNAMIC TABLEManually trigger one refreshUsed during initial build or debugging
ALTER TABLE ... CHANGE COLUMN ... SET MASKBind masking function to PHI columnTransparently applied to all queries, including Dynamic Tables
TIMESTAMP AS OFTime Travel historical snapshot queryMonthly lookback for insurance reconciliation
DATEDIFFCalculate length of stay (LOS) and patient ageTime difference calculation
FLOOR / CASTNumeric type conversionAge rounding, JSON string to DOUBLE

Prerequisites

All examples in this guide run under the best_practice_fhir_clinical schema.

CREATE SCHEMA IF NOT EXISTS best_practice_fhir_clinical;


ODS Layer: Raw FHIR JSON Tables

The ODS layer stores complete FHIR resource JSON strings directly, preserving original messages without parsing, making it easy to extend fields and replay later.

Create Tables

-- FHIR Patient resource CREATE TABLE IF NOT EXISTS best_practice_fhir_clinical.doc_fhir_patient ( patient_id STRING, resource_json STRING ); -- FHIR Encounter resource (visit events) CREATE TABLE IF NOT EXISTS best_practice_fhir_clinical.doc_fhir_encounter ( encounter_id STRING, patient_id STRING, resource_json STRING ); -- FHIR Observation resource (lab results / vital signs) CREATE TABLE IF NOT EXISTS best_practice_fhir_clinical.doc_fhir_observation ( obs_id STRING, patient_id STRING, resource_json STRING ); -- FHIR MedicationRequest resource (medication orders) CREATE TABLE IF NOT EXISTS best_practice_fhir_clinical.doc_fhir_medication_request ( req_id STRING, patient_id STRING, resource_json STRING );

Load Sample Data

In production, use COPY INTO + Volume to bulk-import FHIR JSON files.

Load data from a local CSV file (recommended):

-- Step 1: Upload the local CSV file to User Volume via SQL PUT PUT '/path/to/your/data.csv' TO USER VOLUME FILE 'data.csv';

-- Step 2: COPY INTO the table from User Volume COPY INTO best_practice_fhir_clinical.doc_fhir_patient FROM USER VOLUME USING csv OPTIONS('header'='true', 'sep'=',', 'nullValue'='') FILES ('data.csv');

You can also insert small batches of test data inline (no CSV file needed):

Insert 5 patient records (Patient resource):

INSERT INTO best_practice_fhir_clinical.doc_fhir_patient VALUES ('P001', '{"resourceType":"Patient","id":"P001","name":[{"family":"Zhang","given":["Wei"]}],"gender":"male","birthDate":"1980-05-15","address":[{"city":"Shanghai","postalCode":"200000"}]}'), ('P002', '{"resourceType":"Patient","id":"P002","name":[{"family":"Li","given":["Fang"]}],"gender":"female","birthDate":"1972-11-23","address":[{"city":"Beijing","postalCode":"100000"}]}'), ('P003', '{"resourceType":"Patient","id":"P003","name":[{"family":"Wang","given":["Jun"]}],"gender":"male","birthDate":"1955-03-08","address":[{"city":"Guangzhou","postalCode":"510000"}]}'), ('P004', '{"resourceType":"Patient","id":"P004","name":[{"family":"Chen","given":["Mei"]}],"gender":"female","birthDate":"1990-07-30","address":[{"city":"Shenzhen","postalCode":"518000"}]}'), ('P005', '{"resourceType":"Patient","id":"P005","name":[{"family":"Liu","given":["Yang"]}],"gender":"male","birthDate":"1968-01-12","address":[{"city":"Chengdu","postalCode":"610000"}]}');

Insert 5 encounter records (Encounter resource, including ICD-10 diagnosis codes):

INSERT INTO best_practice_fhir_clinical.doc_fhir_encounter VALUES ('E001', 'P001', '{"resourceType":"Encounter","id":"E001","status":"finished","class":{"code":"IMP","display":"inpatient"},"subject":{"reference":"Patient/P001"},"period":{"start":"2026-01-10T08:00:00Z","end":"2026-01-15T14:00:00Z"},"reasonCode":[{"coding":[{"system":"http://hl7.org/fhir/sid/icd-10","code":"I50.0","display":"Congestive heart failure"}]}],"serviceProvider":{"reference":"Organization/Hospital-A","display":"Cardiology"}}'), ('E002', 'P002', '{"resourceType":"Encounter","id":"E002","status":"finished","class":{"code":"AMB","display":"ambulatory"},"subject":{"reference":"Patient/P002"},"period":{"start":"2026-02-03T09:30:00Z","end":"2026-02-03T11:00:00Z"},"reasonCode":[{"coding":[{"system":"http://hl7.org/fhir/sid/icd-10","code":"E11.9","display":"Type 2 diabetes mellitus"}]}],"serviceProvider":{"reference":"Organization/Hospital-A","display":"Endocrinology"}}'), ('E003', 'P003', '{"resourceType":"Encounter","id":"E003","status":"finished","class":{"code":"IMP","display":"inpatient"},"subject":{"reference":"Patient/P003"},"period":{"start":"2026-01-20T07:00:00Z","end":"2026-01-28T16:00:00Z"},"reasonCode":[{"coding":[{"system":"http://hl7.org/fhir/sid/icd-10","code":"J44.1","display":"COPD with acute exacerbation"}]}],"serviceProvider":{"reference":"Organization/Hospital-B","display":"Pulmonology"}}'), ('E004', 'P004', '{"resourceType":"Encounter","id":"E004","status":"in-progress","class":{"code":"AMB","display":"ambulatory"},"subject":{"reference":"Patient/P004"},"period":{"start":"2026-03-15T14:00:00Z"},"reasonCode":[{"coding":[{"system":"http://hl7.org/fhir/sid/icd-10","code":"N18.3","display":"Chronic kidney disease stage 3"}]}],"serviceProvider":{"reference":"Organization/Hospital-A","display":"Nephrology"}}'), ('E005', 'P005', '{"resourceType":"Encounter","id":"E005","status":"finished","class":{"code":"EMER","display":"emergency"},"subject":{"reference":"Patient/P005"},"period":{"start":"2026-02-18T22:15:00Z","end":"2026-02-19T06:00:00Z"},"reasonCode":[{"coding":[{"system":"http://hl7.org/fhir/sid/icd-10","code":"I63.9","display":"Cerebral infarction"}]}],"serviceProvider":{"reference":"Organization/Hospital-B","display":"Neurology"}}');

After inserting 7 observation records (Observation resource, including LOINC codes) and 6 medication request records (MedicationRequest, including RxNorm codes), verify the ODS layer row counts:

SELECT COUNT(*) AS patient_count FROM best_practice_fhir_clinical.doc_fhir_patient; SELECT COUNT(*) AS encounter_count FROM best_practice_fhir_clinical.doc_fhir_encounter; SELECT COUNT(*) AS obs_count FROM best_practice_fhir_clinical.doc_fhir_observation; SELECT COUNT(*) AS med_count FROM best_practice_fhir_clinical.doc_fhir_medication_request;


DWD Layer: FHIR JSON Parsing and Structuring

The DWD layer uses Dynamic Tables to extract nested JSON fields from the ODS layer into relational columns, serving as the foundation for downstream aggregation and quality control analysis.

JSON Field Extraction Patterns

FHIR JSON structures are deeply nested. Follow these JSONPath patterns when extracting:

FHIR Field TypeJSONPath ExampleNotes
Top-level scalar$.genderDirect access
Property of first array element$.name[0].familyArray index [0]
Multi-level nested array$.reasonCode[0].coding[0].codeMulti-level nesting
Scalar in nested object$.period.startDot path
Numeric value (requires type cast)CAST(get_json_object(...) AS DOUBLE)Returns string by default

Validate Patient resource extraction:

SELECT patient_id, get_json_object(resource_json, '$.name[0].family') AS family_name, get_json_object(resource_json, '$.name[0].given[0]') AS given_name, get_json_object(resource_json, '$.gender') AS gender, get_json_object(resource_json, '$.birthDate') AS birth_date, get_json_object(resource_json, '$.address[0].city') AS city FROM best_practice_fhir_clinical.doc_fhir_patient;

patient_id | family_name | given_name | gender | birth_date | city -----------+-------------+------------+--------+------------+----------- P001 | Zhang | Wei | male | 1980-05-15 | Shanghai P002 | Li | Fang | female | 1972-11-23 | Beijing P003 | Wang | Jun | male | 1955-03-08 | Guangzhou P004 | Chen | Mei | female | 1990-07-30 | Shenzhen P005 | Liu | Yang | male | 1968-01-12 | Chengdu

Validate ICD-10 code and encounter duration extraction from Encounter resource:

SELECT encounter_id, patient_id, get_json_object(resource_json, '$.status') AS enc_status, get_json_object(resource_json, '$.class.code') AS enc_class, get_json_object(resource_json, '$.period.start') AS period_start, get_json_object(resource_json, '$.period.end') AS period_end, get_json_object(resource_json, '$.reasonCode[0].coding[0].code') AS icd_code, get_json_object(resource_json, '$.reasonCode[0].coding[0].display') AS diagnosis, get_json_object(resource_json, '$.serviceProvider.display') AS department FROM best_practice_fhir_clinical.doc_fhir_encounter;

encounter_id | patient_id | enc_status | enc_class | period_start | period_end | icd_code | diagnosis | department -------------+------------+-------------+-----------+-----------------------+-----------------------+----------+----------------------------------+----------- E001 | P001 | finished | IMP | 2026-01-10T08:00:00Z | 2026-01-15T14:00:00Z | I50.0 | Congestive heart failure | Cardiology E002 | P002 | finished | AMB | 2026-02-03T09:30:00Z | 2026-02-03T11:00:00Z | E11.9 | Type 2 diabetes mellitus | Endocrinology E003 | P003 | finished | IMP | 2026-01-20T07:00:00Z | 2026-01-28T16:00:00Z | J44.1 | COPD with acute exacerbation | Pulmonology E004 | P004 | in-progress | AMB | 2026-03-15T14:00:00Z | NULL | N18.3 | Chronic kidney disease stage 3 | Nephrology E005 | P005 | finished | EMER | 2026-02-18T22:15:00Z | 2026-02-19T06:00:00Z | I63.9 | Cerebral infarction | Neurology

Create DWD Dynamic Tables

dwd_patient_dim: Patient dimension table, extracts basic demographic fields and computes age:

CREATE DYNAMIC TABLE IF NOT EXISTS best_practice_fhir_clinical.dwd_patient_dim AS SELECT p.patient_id, get_json_object(p.resource_json, '$.name[0].family') AS family_name, get_json_object(p.resource_json, '$.name[0].given[0]') AS given_name, get_json_object(p.resource_json, '$.gender') AS gender, get_json_object(p.resource_json, '$.birthDate') AS birth_date, get_json_object(p.resource_json, '$.address[0].city') AS city, DATEDIFF(CURRENT_DATE(), CAST(get_json_object(p.resource_json, '$.birthDate') AS DATE)) / 365 AS age_years FROM best_practice_fhir_clinical.doc_fhir_patient p;

dwd_encounter_fact: Encounter fact table, extracts ICD-10 codes, length of stay, and department, and classifies by ICD chapter:

CREATE DYNAMIC TABLE IF NOT EXISTS best_practice_fhir_clinical.dwd_encounter_fact AS SELECT e.encounter_id, e.patient_id, get_json_object(e.resource_json, '$.status') AS enc_status, get_json_object(e.resource_json, '$.class.code') AS enc_class, CAST(get_json_object(e.resource_json, '$.period.start') AS TIMESTAMP) AS admit_time, CAST(get_json_object(e.resource_json, '$.period.end') AS TIMESTAMP) AS discharge_time, DATEDIFF( CAST(get_json_object(e.resource_json, '$.period.end') AS TIMESTAMP), CAST(get_json_object(e.resource_json, '$.period.start') AS TIMESTAMP) ) AS los_days, get_json_object(e.resource_json, '$.reasonCode[0].coding[0].code') AS icd_code, get_json_object(e.resource_json, '$.reasonCode[0].coding[0].display') AS primary_diagnosis, get_json_object(e.resource_json, '$.serviceProvider.display') AS department, CASE WHEN UPPER(get_json_object(e.resource_json, '$.reasonCode[0].coding[0].code')) LIKE 'I%' THEN 'Cardiology' WHEN UPPER(get_json_object(e.resource_json, '$.reasonCode[0].coding[0].code')) LIKE 'E%' THEN 'Endocrinology' WHEN UPPER(get_json_object(e.resource_json, '$.reasonCode[0].coding[0].code')) LIKE 'J%' THEN 'Pulmonology' WHEN UPPER(get_json_object(e.resource_json, '$.reasonCode[0].coding[0].code')) LIKE 'N%' THEN 'Nephrology' ELSE 'Other' END AS icd_chapter FROM best_practice_fhir_clinical.doc_fhir_encounter e;

dwd_observation_fact: Lab/vital signs fact table, extracts LOINC codes, observation values, and units:

CREATE DYNAMIC TABLE IF NOT EXISTS best_practice_fhir_clinical.dwd_observation_fact AS SELECT o.obs_id, o.patient_id, get_json_object(o.resource_json, '$.category[0].coding[0].code') AS obs_category, get_json_object(o.resource_json, '$.code.coding[0].code') AS loinc_code, get_json_object(o.resource_json, '$.code.coding[0].display') AS obs_name, CAST(get_json_object(o.resource_json, '$.valueQuantity.value') AS DOUBLE) AS obs_value, get_json_object(o.resource_json, '$.valueQuantity.unit') AS obs_unit, CAST(get_json_object(o.resource_json, '$.effectiveDateTime') AS TIMESTAMP) AS obs_time FROM best_practice_fhir_clinical.doc_fhir_observation o;

dwd_medication_fact: Medication order fact table, extracts RxNorm codes, drug names, dosages, and routes:

CREATE DYNAMIC TABLE IF NOT EXISTS best_practice_fhir_clinical.dwd_medication_fact AS SELECT m.req_id, m.patient_id, get_json_object(m.resource_json, '$.status') AS req_status, get_json_object(m.resource_json, '$.medicationCodeableConcept.coding[0].code') AS rxnorm_code, get_json_object(m.resource_json, '$.medicationCodeableConcept.coding[0].display') AS medication_name, CAST(get_json_object(m.resource_json, '$.dosageInstruction[0].doseAndRate[0].doseQuantity.value') AS DOUBLE) AS dose_value, get_json_object(m.resource_json, '$.dosageInstruction[0].doseAndRate[0].doseQuantity.unit') AS dose_unit, get_json_object(m.resource_json, '$.dosageInstruction[0].route.coding[0].code') AS route, CAST(get_json_object(m.resource_json, '$.authoredOn') AS TIMESTAMP) AS authored_on, REPLACE(get_json_object(m.resource_json, '$.encounter.reference'), 'Encounter/', '') AS encounter_id FROM best_practice_fhir_clinical.doc_fhir_medication_request m;

Manually trigger the first refresh and verify data:

REFRESH DYNAMIC TABLE best_practice_fhir_clinical.dwd_patient_dim; REFRESH DYNAMIC TABLE best_practice_fhir_clinical.dwd_encounter_fact; REFRESH DYNAMIC TABLE best_practice_fhir_clinical.dwd_observation_fact; REFRESH DYNAMIC TABLE best_practice_fhir_clinical.dwd_medication_fact; SELECT patient_id, family_name, gender, birth_date, city, ROUND(age_years, 1) AS age FROM best_practice_fhir_clinical.dwd_patient_dim ORDER BY patient_id;

patient_id | family_name | gender | birth_date | city | age -----------+-------------+--------+------------+-----------+----- P001 | Zhang | male | 1980-05-15 | Shanghai | 46.1 P002 | Li | female | 1972-11-23 | Beijing | 53.6 P003 | Wang | male | 1955-03-08 | Guangzhou | 71.3 P004 | Chen | female | 1990-07-30 | Shenzhen | 35.9 P005 | Liu | male | 1968-01-12 | Chengdu | 58.4

Validate the encounter fact table (focus on los_days calculation and icd_chapter classification):

SELECT encounter_id, patient_id, enc_class, icd_code, primary_diagnosis, department, los_days, icd_chapter FROM best_practice_fhir_clinical.dwd_encounter_fact ORDER BY encounter_id;

encounter_id | patient_id | enc_class | icd_code | primary_diagnosis | department | los_days | icd_chapter -------------+------------+-----------+----------+---------------------------------+---------------+----------+------------ E001 | P001 | IMP | I50.0 | Congestive heart failure | Cardiology | 5 | Cardiology E002 | P002 | AMB | E11.9 | Type 2 diabetes mellitus | Endocrinology | 0 | Endocrinology E003 | P003 | IMP | J44.1 | COPD with acute exacerbation | Pulmonology | 9 | Pulmonology E004 | P004 | AMB | N18.3 | Chronic kidney disease stage 3 | Nephrology | NULL | Nephrology E005 | P005 | EMER | I63.9 | Cerebral infarction | Neurology | 0 | Cardiology

Observation fact table (including LOINC codes and quantitative values):

SELECT obs_id, patient_id, obs_category, loinc_code, obs_name, obs_value, obs_unit FROM best_practice_fhir_clinical.dwd_observation_fact ORDER BY obs_id;

obs_id | patient_id | obs_category | loinc_code | obs_name | obs_value | obs_unit -------+------------+--------------+------------+-------------------------+-----------+--------- OBS001 | P001 | laboratory | 2160-0 | Creatinine | 1.2 | mg/dL OBS002 | P002 | laboratory | 4548-4 | HbA1c | 8.5 | % OBS003 | P003 | vital-signs | 59408-5 | SpO2 | 88 | % OBS004 | P004 | laboratory | 2160-0 | Creatinine | 2.8 | mg/dL OBS005 | P005 | vital-signs | 8310-5 | Body temperature | 38.9 | Cel OBS006 | P001 | vital-signs | 8480-6 | Systolic blood pressure | 155 | mmHg OBS007 | P002 | laboratory | 2339-0 | Glucose | 12.4 | mmol/L


PHI Field Masking: Column Masking

FHIR Patient data contains PHI fields such as patient names (family_name) that must be masked for non-privileged users. The following example binds a masking policy to the family_name column of dwd_patient_dim. Admin accounts see the original value; other users see the masked value.

-- Create masking function: admin sees original name, other users see masked version CREATE OR REPLACE FUNCTION best_practice_fhir_clinical.mask_phi_name(name STRING) RETURNS STRING AS CASE WHEN current_user() IN ('privileged_user') THEN name -- replace with actual authorized username ELSE CONCAT(LEFT(name, 1), REPEAT('*', LENGTH(name) - 1)) END;


DWS Layer: Department Cost Aggregation and Patient Observation Summary

Department Cost Aggregation

dws_department_cost aggregates encounter counts, average length of stay, and prescription counts by department and ICD chapter, serving as the foundation for DRG cost management:

CREATE DYNAMIC TABLE IF NOT EXISTS best_practice_fhir_clinical.dws_department_cost AS SELECT e.department, e.icd_chapter, COUNT(DISTINCT e.encounter_id) AS total_encounters, COUNT(DISTINCT e.patient_id) AS total_patients, SUM(CASE WHEN e.enc_class = 'IMP' THEN 1 ELSE 0 END) AS inpatient_count, SUM(CASE WHEN e.enc_class = 'AMB' THEN 1 ELSE 0 END) AS outpatient_count, SUM(CASE WHEN e.enc_class = 'EMER' THEN 1 ELSE 0 END) AS emergency_count, ROUND(AVG(CAST(e.los_days AS DOUBLE)), 2) AS avg_los_days, MAX(e.los_days) AS max_los_days, COUNT(DISTINCT m.req_id) AS total_prescriptions, COUNT(DISTINCT m.rxnorm_code) AS distinct_medications FROM best_practice_fhir_clinical.dwd_encounter_fact e LEFT JOIN best_practice_fhir_clinical.dwd_medication_fact m ON e.encounter_id = m.encounter_id GROUP BY e.department, e.icd_chapter;

REFRESH DYNAMIC TABLE best_practice_fhir_clinical.dws_department_cost; SELECT department, icd_chapter, total_encounters, avg_los_days, total_prescriptions, distinct_medications FROM best_practice_fhir_clinical.dws_department_cost ORDER BY total_encounters DESC;

department | icd_chapter | total_encounters | avg_los_days | total_prescriptions | distinct_medications --------------+---------------+------------------+--------------+---------------------+--------------------- Cardiology | Cardiology | 1 | 5.0 | 2 | 2 Nephrology | Nephrology | 1 | NULL | 1 | 1 Pulmonology | Pulmonology | 1 | 9.0 | 1 | 1 Neurology | Cardiology | 1 | 0.0 | 1 | 1 Endocrinology | Endocrinology | 1 | 0.0 | 1 | 1

Result interpretation: Pulmonology (J44.1 COPD acute exacerbation) has the highest average length of stay at 9 days, consistent with the clinical pattern of prolonged hospitalization during COPD exacerbations. The heart failure patient in Cardiology (I50.0) was hospitalized for 5 days with 2 medications prescribed (Furosemide + Carvedilol), aligning with the standard clinical pathway.

Patient Observation Summary

dws_patient_obs_summary aggregates observation results by patient and LOINC code to facilitate tracking of indicator trends:

CREATE DYNAMIC TABLE IF NOT EXISTS best_practice_fhir_clinical.dws_patient_obs_summary AS SELECT o.patient_id, o.loinc_code, o.obs_name, o.obs_unit, COUNT(*) AS obs_count, ROUND(MIN(o.obs_value), 2) AS min_value, ROUND(MAX(o.obs_value), 2) AS max_value, ROUND(AVG(o.obs_value), 2) AS avg_value, MIN(o.obs_time) AS first_obs_time, MAX(o.obs_time) AS last_obs_time FROM best_practice_fhir_clinical.dwd_observation_fact o GROUP BY o.patient_id, o.loinc_code, o.obs_name, o.obs_unit;

REFRESH DYNAMIC TABLE best_practice_fhir_clinical.dws_patient_obs_summary; SELECT patient_id, loinc_code, obs_name, obs_unit, obs_count, min_value, max_value, avg_value FROM best_practice_fhir_clinical.dws_patient_obs_summary ORDER BY patient_id, loinc_code;

patient_id | loinc_code | obs_name | obs_unit | obs_count | min_value | max_value | avg_value -----------+------------+-------------------------+----------+-----------+-----------+-----------+---------- P001 | 2160-0 | Creatinine | mg/dL | 1 | 1.2 | 1.2 | 1.2 P001 | 8480-6 | Systolic blood pressure | mmHg | 1 | 155 | 155 | 155 P002 | 2339-0 | Glucose | mmol/L | 1 | 12.4 | 12.4 | 12.4 P002 | 4548-4 | HbA1c | % | 1 | 8.5 | 8.5 | 8.5 P003 | 59408-5 | SpO2 | % | 1 | 88 | 88 | 88 P004 | 2160-0 | Creatinine | mg/dL | 1 | 2.8 | 2.8 | 2.8 P005 | 8310-5 | Body temperature | Cel | 1 | 38.9 | 38.9 | 38.9

Result interpretation: P002 (diabetic patient) has an HbA1c of 8.5%, exceeding the clinical control target (< 7%), and blood glucose of 12.4 mmol/L is also significantly elevated, indicating poor glycemic control requiring intensified treatment. P004 (CKD stage 3) has creatinine at 2.8 mg/dL, in the moderate-to-severe elevation range (normal upper limit ~1.2), indicating significant renal impairment. P003 (COPD) has SpO2 at 88%, below the normal lower limit (95%), consistent with hypoxemia during acute exacerbation.


ADS Layer: Clinical Quality Metrics

ads_clinical_quality_metrics integrates patient dimensions, encounter facts, and observation facts to compute three types of clinical pathway compliance indicators:

  • hba1c_tested: Whether diabetic patients (ICD E series) completed HbA1c testing
  • creatinine_tested: Whether nephrology patients (ICD N series) completed creatinine testing
  • spo2_monitored: Whether COPD patients (ICD J series) had SpO2 monitored

CREATE DYNAMIC TABLE IF NOT EXISTS best_practice_fhir_clinical.ads_clinical_quality_metrics AS SELECT p.patient_id, p.family_name, p.gender, FLOOR(p.age_years) AS age, e.encounter_id, e.department, e.icd_chapter, e.icd_code, e.primary_diagnosis, e.enc_class, e.los_days, e.enc_status, COUNT(DISTINCT m.req_id) AS prescription_count, COUNT(DISTINCT o.obs_id) AS lab_count, MAX(CASE WHEN o.loinc_code = '4548-4' THEN 1 ELSE 0 END) AS hba1c_tested, MAX(CASE WHEN o.loinc_code = '2160-0' THEN 1 ELSE 0 END) AS creatinine_tested, MAX(CASE WHEN o.loinc_code = '59408-5' THEN 1 ELSE 0 END) AS spo2_monitored, MAX(CASE WHEN o.loinc_code = '4548-4' THEN o.obs_value END) AS hba1c_value, MAX(CASE WHEN o.loinc_code = '2160-0' THEN o.obs_value END) AS creatinine_value, MAX(CASE WHEN o.loinc_code = '59408-5' THEN o.obs_value END) AS spo2_value FROM best_practice_fhir_clinical.dwd_patient_dim p JOIN best_practice_fhir_clinical.dwd_encounter_fact e ON p.patient_id = e.patient_id LEFT JOIN best_practice_fhir_clinical.dwd_medication_fact m ON e.encounter_id = m.encounter_id LEFT JOIN best_practice_fhir_clinical.dwd_observation_fact o ON p.patient_id = o.patient_id GROUP BY p.patient_id, p.family_name, p.gender, FLOOR(p.age_years), e.encounter_id, e.department, e.icd_chapter, e.icd_code, e.primary_diagnosis, e.enc_class, e.los_days, e.enc_status;

REFRESH DYNAMIC TABLE best_practice_fhir_clinical.ads_clinical_quality_metrics; SELECT patient_id, family_name, age, department, primary_diagnosis, enc_class, los_days, prescription_count, lab_count, hba1c_tested, creatinine_tested, spo2_monitored, hba1c_value, creatinine_value, spo2_value FROM best_practice_fhir_clinical.ads_clinical_quality_metrics ORDER BY patient_id;

patient_id | family_name | age | department | primary_diagnosis | enc_class | los_days | prescription_count | lab_count | hba1c_tested | creatinine_tested | spo2_monitored | hba1c_value | creatinine_value | spo2_value -----------+-------------+-----+---------------+----------------------------------+-----------+----------+--------------------+-----------+--------------+-------------------+----------------+-------------+------------------+----------- P001 | Zhang | 46 | Cardiology | Congestive heart failure | IMP | 5 | 2 | 2 | 0 | 1 | 0 | NULL | 1.2 | NULL P002 | Li | 53 | Endocrinology | Type 2 diabetes mellitus | AMB | 0 | 1 | 2 | 1 | 0 | 0 | 8.5 | NULL | NULL P003 | Wang | 71 | Pulmonology | COPD with acute exacerbation | IMP | 9 | 1 | 1 | 0 | 0 | 1 | NULL | NULL | 88 P004 | Chen | 35 | Nephrology | Chronic kidney disease stage 3 | AMB | NULL | 1 | 1 | 0 | 1 | 0 | NULL | 2.8 | NULL P005 | Liu | 58 | Neurology | Cerebral infarction | EMER | 0 | 1 | 1 | 0 | 0 | 0 | NULL | NULL | NULL

Clinical Pathway Compliance Rate Statistics

SELECT e.icd_chapter AS disease_group, COUNT(DISTINCT a.encounter_id) AS encounter_count, ROUND(AVG(CAST(a.los_days AS DOUBLE)), 1) AS avg_los, SUM(a.prescription_count) AS total_prescriptions, ROUND(SUM(a.hba1c_tested) * 100.0 / NULLIF(SUM(CASE WHEN a.icd_code LIKE 'E%' THEN 1 ELSE 0 END), 0), 1) AS diabetes_hba1c_rate_pct, ROUND(SUM(a.creatinine_tested) * 100.0 / NULLIF(SUM(CASE WHEN a.icd_code LIKE 'N%' THEN 1 ELSE 0 END), 0), 1) AS ckd_creatinine_rate_pct, ROUND(SUM(a.spo2_monitored) * 100.0 / NULLIF(SUM(CASE WHEN a.icd_code LIKE 'J%' THEN 1 ELSE 0 END), 0), 1) AS copd_spo2_rate_pct FROM best_practice_fhir_clinical.ads_clinical_quality_metrics a JOIN best_practice_fhir_clinical.dwd_encounter_fact e ON a.encounter_id = e.encounter_id GROUP BY e.icd_chapter ORDER BY encounter_count DESC;

disease_group | encounter_count | avg_los | total_prescriptions | diabetes_hba1c_rate_pct | ckd_creatinine_rate_pct | copd_spo2_rate_pct --------------+-----------------+---------+---------------------+-------------------------+-------------------------+------------------- Cardiology | 2 | 2.5 | 3 | NULL | NULL | NULL Nephrology | 1 | NULL | 1 | NULL | 100.0 | NULL Endocrinology | 1 | 0.0 | 1 | 100.0 | NULL | NULL Pulmonology | 1 | 9.0 | 1 | NULL | NULL | 100.0

Result interpretation: The key lab compliance rates for diabetes (Endocrinology), CKD (Nephrology), and COPD (Pulmonology) are all 100%, meaning every patient completed their required tests. The 2 encounters in the Cardiology chapter (heart failure + cerebral infarction) have no specific quality metrics assigned, but the heart failure patient (P001) did have creatinine tested (to assess renal function after diuretic use).


Time Travel: Insurance Reconciliation Historical Snapshots

In insurance monthly reconciliation scenarios, it is necessary to retrieve encounter data snapshots at a historical point in time. Time Travel achieves this with the TIMESTAMP AS OF syntax:

-- Query the encounter data snapshot at 2026-06-06 23:38 (for insurance reconciliation) SELECT COUNT(*) AS encounter_count FROM best_practice_fhir_clinical.doc_fhir_encounter TIMESTAMP AS OF '2026-06-06 23:38:00';

encounter_count --------------- 5

-- Compare with current data (verify the delta) SELECT COUNT(*) AS current_count FROM best_practice_fhir_clinical.doc_fhir_encounter;

The Time Travel data retention period defaults to 7 days; historical versions beyond the retention period cannot be queried. For monthly insurance reconciliation scenarios, it is recommended to periodically INSERT INTO end-of-month snapshots into a dedicated archive table, rather than relying on Time Travel's retention limits.


Scheduling Configuration: Lakehouse Studio Task

Periodic refresh of Dynamic Tables is managed via Lakehouse Studio Tasks rather than writing REFRESH INTERVAL in the DDL. Create refresh tasks under the Studio path best_practices/fhir_clinical/ to attach monitoring alerts and data quality rules to the same task.

# Create refresh task cz-cli task create refresh_fhir_dwd_all -p skill_test --type SQL \ --description "Refresh all FHIR DWD/DWS/ADS Dynamic Tables" # Set task content (refresh in dependency order) cz-cli task save-content refresh_fhir_dwd_all -p skill_test --content \ "REFRESH DYNAMIC TABLE best_practice_fhir_clinical.dwd_patient_dim; REFRESH DYNAMIC TABLE best_practice_fhir_clinical.dwd_encounter_fact; REFRESH DYNAMIC TABLE best_practice_fhir_clinical.dwd_observation_fact; REFRESH DYNAMIC TABLE best_practice_fhir_clinical.dwd_medication_fact; REFRESH DYNAMIC TABLE best_practice_fhir_clinical.dws_department_cost; REFRESH DYNAMIC TABLE best_practice_fhir_clinical.dws_patient_obs_summary; REFRESH DYNAMIC TABLE best_practice_fhir_clinical.ads_clinical_quality_metrics;" # Schedule daily at 1:00 AM cz-cli task save-cron refresh_fhir_dwd_all -p skill_test --cron "0 1 * * *" # Publish task (task_id=10354669) cz-cli task online refresh_fhir_dwd_all -p skill_test -y

After publishing the task, the Studio UI (best_practices/fhir_clinical/) allows you to add:

  • Monitoring alerts: Alert when ADS layer row count drops to zero, or when refresh times out
  • Data quality rules: Alert when HbA1c compliance rate for diabetic patients falls below 90%
  • Task dependencies: Chain DWS/ADS tasks after DWD tasks to ensure refresh in order

Data Warehouse Object Summary

SHOW TABLES IN best_practice_fhir_clinical;

The best_practice_fhir_clinical schema contains 9 tables in total:

Table NameLayerTypeDescription
doc_fhir_patientODSRegular tableFHIR Patient resource JSON
doc_fhir_encounterODSRegular tableFHIR Encounter resource JSON
doc_fhir_observationODSRegular tableFHIR Observation resource JSON
doc_fhir_medication_requestODSRegular tableFHIR MedicationRequest JSON
dwd_patient_dimDWDDynamic TablePatient dimension, JSON parsing + age calculation
dwd_encounter_factDWDDynamic TableEncounter facts, ICD codes + LOS + department
dwd_observation_factDWDDynamic TableObservation facts, LOINC + quantitative values
dwd_medication_factDWDDynamic TableMedication facts, RxNorm + dosage
dws_department_costDWSDynamic TableDepartment aggregation, DRG cost management
dws_patient_obs_summaryDWSDynamic TablePatient observation summary, metric trends
ads_clinical_quality_metricsADSDynamic TableClinical quality metrics, pathway compliance rates

Notes

  • get_json_object returns strings: All extracted results are STRING type by default. Numeric fields (observation values, dosages) must be explicitly CAST(... AS DOUBLE), otherwise aggregations (AVG/MIN/MAX) may produce unexpected results.

  • FHIR array fields: Many FHIR standard fields are arrays (such as name[], reasonCode[]). This guide uses [0] to take the first element. If you need to expand all array elements, use LATERAL VIEW EXPLODE(SPLIT(json_array_str, ...)).

  • Dynamic Table incremental refresh dependencies: DWS/ADS layer Dynamic Tables depend on the DWD layer, which depends on the ODS layer. Refresh order must follow ODS → DWD → DWS → ADS. Arrange REFRESH statements in order in Studio Tasks, or configure task dependency chains.

  • DATEDIFF handling of NULL: For current inpatients (enc_status = 'in-progress'), discharge_time is NULL, and DATEDIFF(NULL, admit_time) returns NULL. avg_los_days in the DWS layer uses AVG(CAST(los_days AS DOUBLE)), and AVG automatically ignores NULL, so the result is the average for discharged patients, which is the correct business semantics.

  • Time Travel retention: Default retention is 7 days. For scenarios requiring long-term historical version retention such as insurance reconciliation, periodically INSERT INTO end-of-month data snapshots into a dedicated archive table. Do not rely entirely on Time Travel.

  • Column Masking: PHI masking is transparently applied to Dynamic Tables — when the DWD layer reads the ODS layer, if a column has a masking function bound to it, the value stored in DWD is also the masked value.