Only aggregated LOS values in marts — no individual encounter exports with patient link.
Adapt:Handle outliers (LOS > 30 days) separately to avoid skewing the average.
Diagnosis code frequency (aggregate)
How frequently did diagnosis codes occur in the period (aggregated)?
COUNT(*) FROM diagnosis_code dc JOIN encounter e ON e.id = dc.encounterId WHERE e.admitDateTime IN period GROUP BY dc.icdCode HAVING COUNT(*) >= :k_anonymity_threshold
COUNT(*) FROM diagnosis_code dc JOIN encounter e ON e.id = dc.encounterId WHERE e.admitDateTime IN period GROUP BY dc.icdCode HAVING COUNT(*) >= :k_anonymity_threshold
Document message types (ADT, SIU, ORU) and relevant segments for meta extraction.
Use for
Near-real-time encounter/appointment facts.
Watchouts
HL7 segments often bundle full PHI — extract only allowlisted fields from message parsing.
Object overview with short descriptions and load guidance — not a full schema dump.
Table
Description
Grain
Load
patient
Patient
Patient — MRN, DOB, name; strict PHI, only tokenized identifiers in marts.
One patient (MRN / FHIR id)
Load
encounter
Encounter
Encounter meta — type, admit/discharge, department; no clinical note text.
One encounter (id)
Load
appointment
Appointment
Appointment meta — scheduled time, status; no free-text reason.
One appointment (id)
Load
department
Department
Department/location — name, specialty; dimension.
One department (id)
Load
provider
Provider
Provider — NPI, name, specialty; workforce PII.
One provider (NPI / id)
Load
diagnosis_code
Diagnosis code
Diagnosis code (ICD) per encounter — code only, no free-text description in the mart.
One diagnosis code entry (encounter_id, code)
Optional
procedure_code
Procedure code
Procedure code (CPT) per encounter — code only.
One procedure code entry (encounter_id, code)
Optional
lab_result_meta
Lab result meta
Lab result meta — testCode, resultFlag; no free-text comment/narrative value.
One lab result meta (id)
Optional
Most important fields to load early — not a full schema dump.
Entity
Field
Role
Why
Patient
Patient.id
Key
FHIR patient join (tokenized)
Patient
Patient.mrn
PII
Medical record number — PHI
Patient
Patient.birthDate
PII
Date of birth — PHI
Patient
Patient.name
PII
Name — PHI
Patient
Patient.gender
Dimension
Demographic dim (use in aggregate)
Encounter
Encounter.id
Key
Encounter join
Encounter
Encounter.patientId
Dimension
Patient back-join (tokenized)
Encounter
Encounter.class
Dimension
inpatient / outpatient / emergency
Encounter
Encounter.admitDateTime
Measure
Admit timestamp
Encounter
Encounter.dischargeDateTime
Measure
Discharge timestamp
Encounter
Encounter.departmentId
Dimension
Department back-join
Encounter
Encounter.providerId
Dimension
Provider back-join
Encounter
Encounter.status
Dimension
planned / in-progress / finished
Appointment
Appointment.id
Key
Appointment join
Appointment
Appointment.patientId
Dimension
Patient back-join (tokenized)
Appointment
Appointment.start
Measure
Scheduled time
Appointment
Appointment.status
Dimension
booked / arrived / no-show / cancelled
Appointment
Appointment.departmentId
Dimension
Department back-join
Department
Department.id
Key
Department join
Department
Department.name
Dimension
Department name
Department
Department.specialty
Dimension
Specialty
Provider
Provider.id
Key
Provider join
Provider
Provider.npi
Dimension
National provider identifier
Provider
Provider.name
PII
Provider name / PII
Provider
Provider.specialty
Dimension
Specialty
DiagnosisCode
DiagnosisCode.encounterId
Key
Encounter back-join
DiagnosisCode
DiagnosisCode.icdCode
Dimension
ICD-10 code (not free text)
ProcedureCode
ProcedureCode.encounterId
Key
Encounter back-join
ProcedureCode
ProcedureCode.cptCode
Dimension
CPT code (not free text)
LabResultMeta
LabResultMeta.id
Key
Lab result join
LabResultMeta
LabResultMeta.testCode
Dimension
LOINC test code
LabResultMeta
LabResultMeta.resultFlag
Dimension
normal / abnormal / critical (no value)
System and noise objects/fields you typically should not sync into the warehouse by default.
Skip tables / objects
Table
Category
Why
Clinical / progress notes bodies
System
Strict PHI skip — highest sensitivity, never land.
Imaging / DICOM binaries
System
Imaging binary data — never in the warehouse.
Lab result narrative / free-text values
System
Free-text findings — only flag meta allowed.
Full HL7/FHIR message payload bulk
System
Full messages bundle PHI — extract allowlisted fields only.
Skip fields / content
Clinical/progress notes bodies
— Strict PHI skip in every stage.
Direct patient identifiers in marts (MRN/DOB/name/SSN)
— Only tokenized id in analytics stages.
Imaging / DICOM binaries
— Never in the warehouse.
Lab result narrative / free text
— PHI — flag meta only.
Diagnosis/procedure + patient-level export below k-anonymity threshold
— Re-identification risk — only aggregated categories with a minimum case count.
Warehouse-neutral examples for landing (RAW) and curated models — copy and adapt to your dialect. Not vendor SOQL/API DDL.
Landing (RAW)
RAW patient (tokenized) + encounter landing
FHIR shape — patient tokenized only; no cleartext MRN/DOB/name; no notes.
-- Warehouse-neutral RAW from Epic FHIR extract; patient identifiers tokenized before landing
CREATE TABLE raw_epic_patient (
patient_token VARCHAR, -- tokenized surrogate, never raw MRN
gender VARCHAR,
birth_year INT, -- year only, not full DOB, to reduce re-identification risk
_loaded_at TIMESTAMP
-- no mrn, birth_date, name, address, ssn in any RAW column
);
CREATE TABLE raw_epic_encounter (
encounter_id VARCHAR,
patient_token VARCHAR, -- tokenized
class VARCHAR, -- inpatient | outpatient | emergency
admit_at TIMESTAMP,
discharge_at TIMESTAMP,
department_id VARCHAR,
provider_id VARCHAR,
status VARCHAR,
_loaded_at TIMESTAMP
-- no clinical notes / progress note text
);
RAW appointment + diagnosis/procedure code landing
Appointment without free-text reason; codes only, no description text.
Encounter grain — only tokenized patient reference; no clinical content.
CREATE TABLE curated_fct_epic_encounter AS
SELECT
e.encounter_id,
e.patient_token,
e.class,
e.admit_at,
e.discharge_at,
e.department_id,
e.provider_id,
e.status
FROM raw_epic_encounter e
WHERE e.patient_token IS NOT NULL;
Curated k-anonymous diagnosis aggregate
Only aggregated cells with a minimum case count (k-anonymity) — no patient-level exports.
CREATE TABLE curated_agg_epic_diagnosis_frequency AS
SELECT
dc.icd_code,
e.department_id,
COUNT(*) AS case_count
FROM raw_epic_diagnosis_code dc
JOIN raw_epic_encounter e ON e.encounter_id = dc.encounter_id
GROUP BY dc.icd_code, e.department_id
HAVING COUNT(*) >= :k_anonymity_threshold;
Curated measure SELECTs
Encounters, no-show rate, average LOS — adapt period filters; all results aggregated.
-- Encounters in period
SELECT COUNT(*) AS encounters_count
FROM curated_fct_epic_encounter
WHERE admit_at BETWEEN :period_start AND :period_end;
-- No-show rate in period
SELECT
COUNT(*) FILTER (WHERE status = 'noshow')::FLOAT / COUNT(*) AS no_show_rate
FROM raw_epic_appointment
WHERE scheduled_at BETWEEN :period_start AND :period_end;
-- Average length of stay for inpatient encounters (aggregate only)
SELECT AVG(discharge_at - admit_at) AS avg_length_of_stay
FROM curated_fct_epic_encounter
WHERE class = 'inpatient'
AND discharge_at BETWEEN :period_start AND :period_end;
Tools
Open related Binom-Tools workflows to adapt formulas, PII and measures for this source.