LCM Logo

Common data models

OMOP, PCORnet, i2b2, Sentinel, and CDISC - schemas for analyzing health data at scale.

Sources last verified

A common data model (CDM) is a fixed schema that many institutions transform their data into, so that one analysis script runs unchanged everywhere. The point is not the schema itself — it is that the analysis becomes portable. Send the code to the data, get aggregate results back, and no patient-level data ever leaves the site.

That distributed-network property is why CDMs exist at all, and it is the thing to hold onto when comparing them.

The one distinction that matters

Every CDM makes a trade between harmonization and fidelity, and the two big US models sit on opposite sides of it:

  • OMOP maps everything to standard concepts. An ICD-10-CM diagnosis becomes a SNOMED CT concept. Analyses are portable across countries and coding systems, but the mapping is lossy and you inherit someone else's judgment about equivalence.
  • PCORnet keeps the source codes. An ICD-10-CM code stays an ICD-10-CM code. Closer to what the clinician recorded, but you do the harmonization yourself in every study.

OMOP always retains the original value in a _source_value column, so the loss is recoverable — but the default analysis path uses the mapped concept, and most people never look back.

OMOP CDM

The dominant model for observational research, maintained by the OHDSI community.

  • Introduced: OMOP began in 2008 as an FDA-backed public-private partnership; the OHDSI collaborative took over stewardship in 2014
  • Purpose: Generate reliable evidence from observational data at scale — originally drug safety surveillance, now everything from characterization to patient-level prediction
  • Current version: v5.5.0, released 25 August 2026. v5.4 remains the most widely deployed. v6.0 was published but never gained adoption and is effectively shelved — do not start there
  • Encoding: Relational tables, in whatever database you like. DuckDB, PostgreSQL, Snowflake, BigQuery, Databricks and Spark are all common
  • Status today: The largest network of its kind — hundreds of databases across dozens of countries, on the order of a billion patient records. EHDEN in Europe and N3C in the US both standardized on it
  • Competing formats: PCORnet and Sentinel in the US; OMOP has clearly won internationally

The model is person-centric: every clinical fact points back to a person_id and carries a date, which makes longitudinal queries natural.

omop-core-tables.sql
-- Clinical event tables, all keyed to person_id
person(person_id, gender_concept_id, year_of_birth, race_concept_id, ...)
observation_period(person_id, observation_period_start_date, observation_period_end_date)
visit_occurrence(visit_occurrence_id, person_id, visit_concept_id, visit_start_date, ...)
condition_occurrence(condition_occurrence_id, person_id, condition_concept_id, ...)
drug_exposure(drug_exposure_id, person_id, drug_concept_id, drug_exposure_start_date, ...)
measurement(measurement_id, person_id, measurement_concept_id, value_as_number, unit_concept_id, ...)

-- Vocabulary tables: the same in every OMOP instance on earth
concept(concept_id, concept_name, domain_id, vocabulary_id, concept_code, standard_concept)
concept_relationship(concept_id_1, concept_id_2, relationship_id)
concept_ancestor(ancestor_concept_id, descendant_concept_id, min_levels_of_separation)

The concept_ancestor table is the reason people put up with the ETL. It is a pre-computed transitive closure of the SNOMED hierarchy, so "every patient with any form of diabetes, however specifically coded" is one join rather than a hand-maintained code list:

descendants-query.sql
-- All patients with type 2 diabetes or any descendant concept.
-- 201826 = "Type 2 diabetes mellitus" (SNOMED, via OMOP standard concepts)
SELECT DISTINCT co.person_id
FROM condition_occurrence co
JOIN concept_ancestor ca
  ON co.condition_concept_id = ca.descendant_concept_id
WHERE ca.ancestor_concept_id = 201826;
RSQLPythonJuliaRustTypeScript

R is the first-class language for OMOP by a wide margin, and this is the strongest argument for choosing OMOP in the first place. The gap between the R and Python stacks is larger here than anywhere else in health data.

LanguagePackageNotes
RCDMConnectorThe modern entry point. Gives you a cdm object of lazy dbplyr tables — start here
RDatabaseConnector, SqlRenderThe OHDSI database layer. SqlRender translates one SQL dialect into ten, which is what makes network studies possible
RCohortGenerator, CirceR, CaprBuild and materialize cohort definitions. Capr writes them as R code instead of ATLAS JSON
RCodelistGeneratorBuilds concept sets from the vocabulary tables systematically, rather than by hand
RIncidencePrevalence, CohortSurvival, DrugUtilisationStandard epidemiological analyses, pre-built and network-ready
RCohortMethod, FeatureExtractionComparative effectiveness with propensity scores; large-scale covariate construction
RAchilles, DataQualityDashboard, OmopSketchDatabase characterization and data quality. Run these before trusting an ETL
REunomia, omockA small pre-built CDM and a mock-data generator. The fastest way to a queryable CDM
Pythonohdsi-common, ohdsi-database-connector, ohdsi-sqlrender, ohdsi-circeOfficial OHDSI bindings — but they are JPype wrappers around the R/Java packages, not native ports
PythonpyomopNative SQLAlchemy ORM for the CDM. Independent of OHDSI
JuliaOMOPCDMCohortCreator, OMOPCDMDatabaseConnectorReal packages from JuliaHealth, in the General registry. Narrow scope, small community
SQLCommonDataModelThe official DDL for every CDM version and dialect
CLIATLAS / BroadseaThe web UI for cohort definition; Broadsea is the Docker deployment of the whole stack

PCORnet CDM

The model behind PCORnet, the US National Patient-Centered Clinical Research Network.

  • Introduced: 2014, funded by PCORI
  • Purpose: Pragmatic clinical trials and observational studies across a US health-system network
  • Current version: v6.x
  • Encoding: Relational tables, historically SAS datasets
  • Status today: Healthy within its network — a large group of US health systems — but essentially US-only and not used outside PCORnet
  • Competing formats: OMOP, directly. Several institutions maintain both, and mapping between them is a recurring topic

Structurally it is encounter-centric rather than person-centric, and it preserves source vocabularies. DIAGNOSIS keeps the ICD-10-CM code, with a DX_TYPE column recording which code system it came from.

SASSQLRPythonJuliaRust

PCORnet tooling is SAS-first and largely unpublished. The Data Curation Query Package is SAS, distributed to network members rather than released openly, and there is no CRAN or PyPI package for the model. If you work with PCORnet outside the network's own tooling, you are writing SQL against a documented schema and reading XPT files with haven or pyreadstat.

i2b2

Informatics for Integrating Biology and the Bedside — the cohort-discovery workhorse of academic medical centers.

  • Introduced: ~2004, as an NIH-funded National Center for Biomedical Computing, from what is now Mass General Brigham
  • Purpose: Let investigators self-serve feasibility counts — "how many patients here match these criteria?" — without a data request to the analytics team
  • Encoding: A star schema in a relational database, built on entity-attribute-value
  • Status today: Still very widely deployed, and often the fastest route to a count at a US academic center. Losing ground to OMOP for analysis, but its query UI remains hard to beat for its specific job
  • Related: SHRINE federates i2b2 queries across institutions

The whole model is essentially one fact table:

i2b2-star-schema.sql
observation_fact(encounter_num, patient_num, concept_cd, start_date, modifier_cd, ...)
patient_dimension(patient_num, birth_date, sex_cd, race_cd, ...)
concept_dimension(concept_path, concept_cd, name_char)
visit_dimension(encounter_num, patient_num, start_date, end_date, location_cd)

The flexibility is real — any concept fits without a schema change — but so is the cost: everything is a string comparison against concept_cd, and there is no type safety anywhere.

JavaSQLRPython

There is no maintained CRAN or PyPI client. Query the database directly, or use the web UI and export. The i2b2 core server is the reference implementation.

Sentinel

The FDA's active post-market safety surveillance system.

  • Introduced: Mini-Sentinel in 2008, following the FDA Amendments Act of 2007; the full Sentinel System from 2016
  • Purpose: Detect and evaluate safety signals for drugs and devices in near-real-time, across a very large distributed claims and EHR population
  • Encoding: The Sentinel Common Data Model — relational, claims-oriented, SAS-based
  • Status today: Operational and consequential — FDA regulatory decisions cite it — but it is not a model you adopt. You participate in Sentinel, or you do not
  • Tooling: SAS, distributed as routine querying packages to data partners

Worth knowing exists, and worth knowing you almost certainly will not build on it directly.

CDISC (SDTM / ADaM)

The clinical trials standards. Categorically different from everything above: these are not optional, they are regulatory requirements.

  • Introduced: CDISC founded 1997. SDTM from the mid-2000s; required by the FDA for new drug submissions since December 2016, and by Japan's PMDA
  • Purpose: Standardize how trial data is submitted to regulators so reviewers see the same structure from every sponsor
  • Status today: Universal in industry pharma, essentially absent from academic research. If you work in trials you will live in it; otherwise you may never touch it
  • Competing formats: None. There is no alternative — this is what the regulator accepts

The pipeline is a fixed sequence, and the separation is deliberate: SDTM is what was observed, ADaM is what was analyzed, and a reviewer must be able to trace any analysis result back to a tabulation record.

StandardRole
CDASHHow data is collected on the case report form
SDTMTabulation — the observations as collected, one domain per topic (DM, AE, LB, VS)
ADaMAnalysis-ready datasets, derived from SDTM, one per analysis (ADSL, ADAE, ADLB)
Define-XMLThe machine-readable data dictionary that ships with the submission
ODMXML for exchanging CRF and trial data between systems
Dataset-JSONThe modern replacement for SAS XPT as a transport format
sdtm-ae-domain.csv
STUDYID,DOMAIN,USUBJID,AESEQ,AETERM,AEDECOD,AESTDTC,AESEV,AESER,AEREL
STUDY01,AE,STUDY01-001-0012,1,Headache,Headache,2026-01-15,MILD,N,POSSIBLE
STUDY01,AE,STUDY01-001-0012,2,Nausea,Nausea,2026-01-18,MODERATE,N,PROBABLE
STUDY01,AE,STUDY01-002-0007,1,Rash on forearm,Rash,2026-02-02,MILD,N,UNLIKELY

AETERM is what the investigator wrote; AEDECOD is that term mapped to MedDRA. Keeping both — the verbatim and the coded — is required, and is a good habit well beyond trials.

The required submission transport format is SAS Transport (XPT) Version 5, a format from 1988 with 8-character variable names and 40-character labels. This constraint shapes every CDISC variable name you will ever see. Dataset-JSON exists to replace it and is progressing through regulatory acceptance, but XPT v5 is still what goes in the submission.

RPythonCLIJuliaRust
LanguagePackageNotes
RadmiralThe pharmaverse ADaM toolkit. Now the mainstream open-source alternative to in-house SAS macros
Rsdtm.oakEDC-agnostic SDTM creation. The newer half of the pair
Rmetacore, metatoolsHold Define-XML metadata in R and build datasets from it
Rxportr, havenWrite submission-compliant XPT v5; read SAS files generally
RdatasetjsonRead and write CDISC Dataset-JSON
RlogrxExecution logs for validated environments — a regulatory requirement, not a nicety
Pythonpyreadstat, xportRead and write XPT and SAS7BDAT
PythonodmlibWork with ODM and Define-XML as Python objects
CLIcdisc-rules-engineCDISC's own open-source conformance checker. Run it before you submit
JuliaReadStatTables.jl, SASLib.jlRead SAS files. Nothing CDISC-aware beyond that

Choosing between them

OMOPPCORneti2b2CDISC
Primary useObservational researchPragmatic trials, network studiesCohort discoveryRegulatory submission
OrientationPersonEncounterFact (EAV)Trial subject
VocabulariesMapped to standard conceptsSource codes retainedLocal concept_cd pathsCDISC controlled terminology
GeographyInternationalUS onlyMostly US academicInternational
Best languageRSASJava / SQLR
Adopt it ifYou want portable analyses and a real tool ecosystemYour network already runs itYou need fast feasibility countsYou are submitting to a regulator

For a new observational research project with a free choice, OMOP is the default, and the tooling is the reason. For anything heading to a regulator, CDISC is not a choice at all.