Learn 03~11 min
The data we model
The project turns GP clinical records, commissioning datasets and shared reference data into a supported analytical estate: people, clinical populations, results, activity, pathways, measures and governed data products.
The project has two main data families
Most person-level work in the project draws from one or both of two broad families. OLIDS provides the detailed GP clinical record. Commissioning data provides hospital activity, waiting lists, prescribing, community and mental-health activity, referrals and other administrative flows. Shared reference models add organisations, geographies, deprivation and clinical classifications to both.
The distinction is visible in the repository. OLIDS staging models live in models/staging/olids/. National and local commissioning feeds live under models/staging/commissioning/, with a folder for each source. Their downstream models remain separated where provenance matters and are joined where the project has a supported person-level relationship.
You rarely need to begin in either staging directory. The project already contains reporting models for common clinical populations, demographics, recent activity and waiting-list measures. Understanding the source families helps you interpret those models; it should not send every new question back to the source.
What the project creates from those sources
The dbt project does more than clean the incoming tables. It creates named, tested relations in the modelling, reporting and published databases. Each family removes a different piece of work that would otherwise be repeated in individual analyses.
| What the project creates | Models to recognise | What they provide |
|---|---|---|
| Person spines and descriptive dimensions | dim_person_demographics, dim_person_demographics_historical, dim_person_pseudo | Current and historical OLIDS demographics, registration, geography, and the bridge used where cross-dataset linkage is available |
| Clinical populations | fct_person_*_register | Reusable definitions of diabetes, asthma, COPD, CKD and other clinical registers, with the evidence behind membership |
| Condition histories | fct_person_condition_episodes | Diagnosis-based episodes with start and end dates, including repeated cycles where a condition can resolve and recur |
| Latest clinical states | int_*_latest | One selected HbA1c, blood pressure, eGFR, smoking status or other qualifying result per person |
| Activity facts | fct_person_sus_*_recent, fct_person_gp_recent | Recent urgent care, admitted care, outpatient and GP activity at a documented person grain |
| Waiting-list facts and summaries | int_wl_current, fct_person_wl_current_count_*, fct_provider_wl_current_count_total | The current open-pathway population and supported counts by person, provider and treatment function |
| Wide analytical marts | person_month_analysis_base, obt_person_activity, fct_person_resource_index | Person-month population-health history and other related reporting facts composed at convenient analytical grains |
| Data-quality outputs | dq_* | Records that fail a documented quality expectation and need investigation rather than ordinary analysis |
| Products for named uses | models/published/ | Tables and views composed for specific dashboards, reports, extracts and operational processes |
These outputs are the project's accumulated domain knowledge. Staging makes the inputs dependable enough to model; modelling and reporting create reusable concepts; published models assemble those concepts for a named use. The rest of this page shows how the two source families feed that estate.
OLIDS supplies the GP clinical record
OLIDS delivers the GP record as a set of related entities: a patient and person spine, clinical events such as observations, medication orders, encounters and referrals, appointment and practitioner records, and the concept tables that resolve source codes. The feed arrives already conformed and tested upstream, so the staging models — stg_olids_observation and its neighbours — are deliberately thin: the project's names and types, and nothing more.
That preparation is our own work, done upstream in a separate dbt project, dbt-olids, which conforms and tests the OneLondon feed before it lands in this project's data lake. It mints the patient and person identifiers used throughout these models — a local identity, not the one OneLondon supplies — and maps every observation and medication order to its SNOMED concept (mapped_concept_code, mapped_concept_display), with medication orders additionally carrying the full BNF hierarchy: code, chapter, section and product name. Filtering by clinical concept or drug class therefore needs no terminology join; the codes are usable columns from the first query. This handbook does not cover dbt-olids itself — from here, its outputs are simply the source.
The investment sits in the modelling and reporting layers, where several hundred models turn those events into reusable clinical meaning. Unlike most domains, which occupy one folder, OLIDS is split into a folder per family — and because folder paths become schemas, this table is also a map of the warehouse:
| Folder | What it holds | Example |
|---|---|---|
modelling/olids/observations | ~50 clinical measurements as _all/_latest pairs (96 models) | int_hba1c_latest |
modelling/olids/medications | Drug-class order histories and polypharmacy (33 models) | int_statin_medications_all |
modelling/olids/diagnoses | Diagnosis and resolution evidence per condition (39 models) | int_diabetes_diagnoses_all |
modelling/olids/person_attributes | Ethnicity, smoking, housebound, registrations | int_ethnicity_all |
modelling/olids/risk_stratification | Frailty and case-management scores | int_efi2_scores |
modelling/olids/programme/* | Programme logic composing the shared blocks (~250 models: immunisations, screening, LTC case-finding, SMI…) | programme/flu |
reporting/olids/person_demographics | The person spine, current and historical | dim_person_demographics |
reporting/olids/person_status | Care home, housebound, carer, opt-out and other statuses | dim_person_care_home |
reporting/olids/disease_registers | 21 QOF registers, each with a pit_ point-in-time twin, ~18 further registers, condition episodes | fct_person_diabetes_register |
reporting/olids/measures | Care-process and control measures | fct_person_bp_control |
reporting/olids/data_quality | Records failing a documented quality expectation | dq_hba1c_issues |
reporting/olids/person_analytics | The person-month analysis mart | person_month_analysis_base |
The measurement models also handle screening and standardisation: implausible values are filtered out, HbA1c results are resolved to IFCC or DCCT, and results are categorised where thresholds are agreed. The medication models do the equivalent — diabetes medication orders are grouped by type and drug class, with GLP-1, SGLT2 and DPP-4 drugs resolved to active ingredients. Polypharmacy is counted against the NHSBSA 5+ and 10+ thresholds.
fct_person_bp_control is a typical composition of these blocks. It scores each person's latest reading against their NICE NG136 target, selected by age, Type 2 diabetes, CKD and ACR status from the register models — home and ambulatory readings are scored against the lower thresholds that apply to them — then stages hypertension and flags elevated readings in people not on the hypertension register.
Because these families follow the naming grammar, what OLIDS already covers can be enumerated by search:
| What you need | Search for | Example |
|---|---|---|
| Every recorded value of a measurement | int_*_all | int_hba1c_all |
| The latest qualifying value per person | int_*_latest | int_egfr_latest |
| Medication orders for a drug class | int_*_medications_all | int_sglt2_medications_all |
| Diagnosis evidence for a condition | int_*_diagnoses_all | int_ckd_diagnoses_all |
| Current register membership | fct_person_*_register | fct_person_diabetes_register |
| A register as at a reference date | pit_*_register | pit_diabetes_register |
| A person attribute or status | dim_person_* | dim_person_care_home |
| Records failing a quality expectation | dq_* | dq_hba1c_issues |
Each register applies its qualifying codes, dates and exclusions in one shared model, with the supporting evidence retained for inspection.
OLIDS is delivered in current, historical and analytical shapes
The same clinical concepts are delivered in several shapes, each making a different claim about time. dim_person_demographics gives the supported current person view. dim_person_demographics_historical keeps one row per person per change period, recording when practice registration, ethnicity or geography changed. Use it when an analysis needs the attributes that applied at an earlier point rather than today's values.
Conditions also have more than a current-register shape. fct_person_condition_episodes creates one row per clinical condition episode, with start and end dates and repeated cycles where the condition can resolve and recur. It is not restricted to the current QOF register definition. This makes it suitable for analysing onset, duration, resolution and a person's clinical history.
person_month_analysis_base composes those histories into a wide person-month mart. It contains one row per actively registered person per month for the latest five years, with the demographics that applied in that month, calendar and financial-period fields, active-condition flags and new-episode flags. It is the practical starting point for population trends, prevalence, incidence and inequalities analysis that would otherwise have to rebuild the same temporal joins for every measure.
These shapes serve different questions. Use the current dimension for a current population, the historical dimension for change periods, the episode fact for condition journeys, and the person-month mart when the analysis itself is monthly. Choosing among them is usually more important than writing another transformation.
Commissioning models cover activity and pathways
Commissioning feeds are commonly submission-based. Providers submit files on a reporting schedule and may later send corrections, while each national collection has its own grain and vocabulary. The staging models make each feed consistent for downstream use; modelling and reporting models then express the analytical concepts we use repeatedly.
| Folder | What we use it for | Examples downstream |
|---|---|---|
sus | Admitted care, outpatients and urgent and emergency care | fct_person_sus_apc_recent, fct_person_sus_op_recent, fct_person_sus_uec_recent |
wl | Referral-to-treatment waiting-list snapshots | int_wl_current, fct_provider_wl_current_count_total |
epd | Primary-care prescribing in the English Prescribing Dataset | Practice- and organisation-level prescribing analysis |
csds | Community services referrals, contacts and activity | Community-service pathway and activity models |
mhsds | Mental-health referrals, contacts, spells and ward stays | Mental-health pathway and activity models |
slam | Provider contract-monitoring activity and actual costs from cumulative submissions | int_cost_index_slam_activity_monthly, fct_person_resource_index |
ers | NHS e-Referral Service referrals and actions | Referral models linked through UBRN and person keys |
asc_cld | Adult social care client-level records | Adult social care analysis |
These are the most-used feeds; the staging layer covers thirteen source folders in all. Above their source-specific preparation, the modelling and reporting layers settle the concepts the feeds only imply:
| Folder | What it holds | Examples |
|---|---|---|
modelling/commissioning/encounters | SUS, CSDS and MHSDS activity standardised into encounters and spells — including merging and imputing admitted-patient spells | int_sus_apc_merged_spellsint_csds_encountersint_mhsds_spell_encounters |
modelling/commissioning/demographics | A person view assembled from PDS and other national datasets | int_person_pmi_combinedint_person_pds_demographics |
modelling/commissioning/activities | Named activity groupings defined once | int_comm_ambulatory_sensitive_nelint_comm_cancerint_comm_maternity |
modelling/commissioning/cost_index | Monthly costed activity per person across the activity feeds | int_person_cost_index_actual_monthlyint_cost_index_csds_activity_monthlyint_cost_index_mhsds_activity_monthly |
reporting/commissioning/person_level | Person-grain recent-activity facts and current waiting-list counts | fct_person_sus_apc_recentfct_person_gp_recentfct_person_wl_current_count_total |
reporting/commissioning/events | Event-grain wide tables for admitted, outpatient and emergency care | obt_encounter_apcobt_encounter_uecobt_appointment_op |
reporting/commissioning/person_history | Monthly person-level activity history | fct_person_activity_by_month |
reporting/commissioning/resource_index | Actual versus expected resource use, with area, deprivation and borough breakdowns | fct_person_resource_indexfct_resource_index_by_areafct_resource_index_by_imd_quintile |
The folder names are useful search terms. If work concerns emergency attendances, search for sus_uec; if it concerns current waiting, search for wl_current. That normally leads to a reporting model before it leads to staging. The Finding models lesson turns this into a repeatable search process.
OLIDS uses person_id; cross-dataset linkage uses sk_patient_id
| Key | What it is | Use it for |
|---|---|---|
person_id | Our own person identity, minted upstream — not derived from an NHS number, so it covers everyone in OLIDS | Every join within OLIDS |
sk_patient_id | The pseudonymised NHS number | Linking to commissioning records: SUS activity, waiting lists, prescribing |
One person can hold several patient records across practice registrations; stg_olids_patient_person resolves them, and the dim_person_* models are already at person grain.
To cross into commissioning data, join dim_person_demographics: it carries sk_patient_id alongside active status, practice, PCN, neighbourhood and borough, so the identifier crossing and the person context arrive in one join (dim_person_pseudo is the bare mapping if that is all you need). People without an NHS number sit outside that bridge — report the linkage coverage your join achieves, and test uniqueness at your model's grain.
Use the taxonomy to find a more specific model
The overview above describes the main kinds of output. Within them, model names let you search for the exact question the project has already settled:
| Question already settled | Models to search for |
|---|---|
| Who is the person and where are they registered or resident? | dim_person_demographics, dim_person_current_practice, dim_person_residence |
| Does the person meet a clinical register definition? | fct_person_*_register |
| When was a condition active, resolved or diagnosed again? | fct_person_condition_episodes |
| What did the population look like in each recent month? | person_month_analysis_base |
| What is their latest qualifying clinical result? | int_*_latest |
| What recent contact have they had with services? | fct_person_sus_*_recent, fct_person_gp_recent, obt_person_activity |
| Who is currently waiting and where? | int_wl_current, fct_person_wl_current_count_*, fct_provider_wl_current_count_total |
| How does service use compare with expectation? | fct_person_resource_index |
The table is deliberately a search guide rather than a complete catalogue. This is why model names matter so much in this project. The prefix tells you the model's role; the entity and subject tell you what it describes; suffixes such as _latest or _current tell you which shape of the concept it offers. The model taxonomy lesson explains the grammar, while Finding models shows how to use it as the project's index.
A new question should compose these models
Suppose the request is for people on the diabetes register, their latest HbA1c and their recent emergency-care use. The project already provides the three main blocks: fct_person_diabetes_register, int_hba1c_latest and fct_person_sus_uec_recent. The register and HbA1c model join within OLIDS on person_id. dim_person_demographics then supplies sk_patient_id for the people who can be linked to the SUS activity model — along with the practice and status context the output will want anyway.
It should not return to stg_olids_observation to derive diabetes or HbA1c again, nor to the SUS emergency-care source to recount attendances. Doing that would fork definitions the project already owns. If an existing model is missing evidence or expresses the wrong contract, improve that shared model deliberately; otherwise, reuse it.
The result is the practical experience this project is designed to create: source cleaning, clinical coding, identity resolution and activity deduplication have already been handled upstream. Most downstream work should feel like composing named domain concepts rather than rebuilding the warehouse from raw records.
The data layers lesson explains where each of those responsibilities is settled. When answering a new question, use the model-discovery method to begin from these supported outputs rather than tracing back to source tables.
Published products apply the required controls
Models used for a specific report, dashboard, extract or operational process are assembled under models/published/. The project separates direct_care/ and secondary_use/ products so that purpose and audience are visible in the repository rather than left to a dashboard filter.
Where an approved secondary-use product needs the project's opt-out population, it can use dim_person_secondary_use_allowed when composing that product. The precise controls follow the product's approved governance and data sources; they should not be inferred simply from the fact that data is pseudonymised or belongs to a broad source family. Shared reporting models remain reusable, while the published model owns the requirements of the product it serves.
Check the project
0/3 answered1You need recent emergency attendances for people on the diabetes register. Where should you start?
2Which model is designed for monthly population-health trends with historical demographics and condition flags already aligned?
3Where should rules required only by a particular product's purpose or audience be applied?