- by Handson
- August 9, 2026
How to Apply CDISC Standards: A Hands-On Clinical SAS Tutorial with Data
The Clinical Data Interchange Standards Consortium (CDISC) has become a fundamental part of modern clinical research and regulatory data submission. For Clinical SAS programmers, understanding how to apply CDISC standards is much more valuable than simply memorizing SDTM variable names.
In this hands-on tutorial, we will take a small set of fictional clinical trial data and transform it into standardized CDISC SDTM datasets using SAS programming concepts.
The objective is to understand the complete workflow:
Raw Clinical Data → Mapping Specification → SDTM Domain → SAS Program → Validation
The examples use fictional data for educational purposes and should not be treated as a substitute for the applicable CDISC standards, implementation guides, controlled terminology, sponsor specifications, or regulatory requirements.
What Is CDISC?
The Clinical Data Interchange Standards Consortium (CDISC) develops standards that help organizations represent and exchange clinical research data consistently.
CDISC standards cover different stages of the clinical research lifecycle. Important standards for a Clinical SAS programmer include:
-
CDASH
-
SDTM
-
ADaM
-
Define-XML
-
Controlled Terminology
CDISC describes SDTM as a model for organizing and formatting clinical study data, while SDTMIG provides implementation guidance for standard clinical trial tabulation datasets.
A simplified clinical data standards flow is:
Clinical Trial / CRF
↓
CDASH
↓
Raw Data
↓
SDTM
↓
ADaM
↓
Statistical Analysis
The focus of this tutorial is the SDTM portion of this process.
Step 1: Understand the Clinical Trial
Before writing SAS code, a programmer needs to understand the study.
Consider a fictional study:
Study ID: ABC001
Study type: Randomized clinical trial
Treatment groups:
-
Drug A
-
Placebo
Suppose we have three subjects:
| Subject | Site | Treatment | Sex | Age |
|---|---|---|---|---|
| 001 | 101 | Drug A | F | 45 |
| 002 | 101 | Placebo | M | 52 |
| 003 | 102 | Drug A | F | 38 |
The study also collects:
-
Adverse events
-
Vital signs
-
Laboratory results
-
Treatment exposure
Our objective is to convert these raw observations into SDTM domains.
Step 2: Identify the SDTM Domains
The first question a Clinical SAS programmer should ask is:
Where does each piece of clinical information belong?
For this example:
| Raw Information | SDTM Domain |
|---|---|
| Subject demographics | DM |
| Treatment administration | EX |
| Adverse events | AE |
| Vital signs | VS |
| Laboratory results | LB |
Therefore, our project will create:
DM
EX
AE
VS
LB
This is the beginning of the mapping process.
Step 3: Create the Raw Demographic Data
Let's start with a fictional raw demographic dataset.
SUBJECT SITE TREATMENT SEX AGE
001 101 Drug A F 45
002 101 Placebo M 52
003 102 Drug A F 38
In SAS, we could create this data as:
data raw.dm;
input subject $ site $ treatment $ sex $ age;
datalines;
001 101 DrugA F 45
002 101 Placebo M 52
003 102 DrugA F 38
;
run;
This is our starting point.
Step 4: Map Raw Variables to SDTM Variables
Now create a mapping specification.
| Raw Variable | SDTM Variable | Transformation |
|---|---|---|
| SUBJECT | SUBJID | Direct mapping |
| SITE | SITEID | Direct mapping |
| TREATMENT | ARM | Map treatment |
| SEX | SEX | Controlled terminology |
| AGE | AGE | Direct mapping |
| Study identifier | STUDYID | Assigned |
| Domain | DOMAIN | Assigned |
| Subject identifier | USUBJID | Derived |
The important lesson is that SDTM programming is not simply renaming variables.
Some variables are:
-
Directly mapped
-
Derived
-
Assigned
-
Standardized
-
Coded
-
Converted to ISO 8601
-
Controlled using CDISC terminology
Step 5: Create STUDYID
Every study needs a study identifier.
For our example:
STUDYID = ABC001
In SAS:
studyid = "ABC001";
Step 6: Create DOMAIN
For the Demographics domain:
domain = "DM";
The resulting record begins to look like:
STUDYID DOMAIN
ABC001 DM
Step 7: Create USUBJID
One of the most important identifiers in SDTM is USUBJID, the Unique Subject Identifier.
Suppose the sponsor's study specification defines USUBJID as:
STUDYID-SITEID-SUBJID
Then:
ABC001-101-001
ABC001-101-002
ABC001-102-003
In SAS:
usubjid = catx("-", studyid, siteid, subject);
The resulting data becomes:
| STUDYID | DOMAIN | USUBJID |
|---|---|---|
| ABC001 | DM | ABC001-101-001 |
| ABC001 | DM | ABC001-101-002 |
| ABC001 | DM | ABC001-102-003 |
The exact USUBJID construction should always follow the study-specific specification.
Step 8: Create the DM Dataset
Now we can build a simplified DM dataset.
data sdtm.dm;
set raw.dm;
length studyid $20
domain $2
usubjid $40
arm $20;
studyid = "ABC001";
domain = "DM";
siteid = site;
subjid = subject;
usubjid = catx("-", studyid, siteid, subjid);
if treatment = "DrugA" then arm = "Drug A";
else if treatment = "Placebo" then arm = "Placebo";
keep studyid domain usubjid subjid siteid sex age arm;
run;
This illustrates an important Clinical SAS programming principle:
The raw dataset is transformed into a standardized dataset rather than being used directly for analysis.
Step 9: Add Treatment Exposure Data
Now suppose the raw exposure data is:
| Subject | Start Date | End Date | Dose | Unit |
|---|---|---|---|---|
| 001 | 2026-01-10 | 2026-02-09 | 100 | mg |
| 002 | 2026-01-10 | 2026-02-09 | 0 | mg |
| 003 | 2026-01-12 | 2026-02-11 | 100 | mg |
This information belongs to the EX domain.
The mapping might be:
| Raw Variable | SDTM Variable |
|---|---|
| Subject | USUBJID |
| Start Date | EXSTDTC |
| End Date | EXENDTC |
| Dose | EXDOSE |
| Unit | EXDOSU |
| Treatment | EXTRT |
Step 10: Convert Dates to ISO 8601
SDTM date/time variables commonly use ISO 8601 representations.
For example:
2026-01-10
A SAS date can be converted using:
exstdtc = put(start_date, yymmdd10.);
Similarly:
exendtc = put(end_date, yymmdd10.);
CDISC guidance also discusses ISO 8601 date/time and interval representations in SDTMIG v3.4 and SDTM v2.0.
Step 11: Create the EX Dataset
A simplified SAS program could be:
data sdtm.ex;
set raw.exposure;
length studyid $20
domain $2
usubjid $40
extrt $40
exdosu $20
exstdtc exendtc $20;
studyid = "ABC001";
domain = "EX";
usubjid = catx("-", studyid, siteid, subject);
extrt = treatment;
exdose = dose;
exdosu = unit;
exstdtc = put(start_date, yymmdd10.);
exendtc = put(end_date, yymmdd10.);
keep studyid domain usubjid extrt exdose exdosu
exstdtc exendtc;
run;
Step 12: Add Adverse Event Data
Suppose the raw adverse event data is:
| Subject | Event | Start Date | End Date | Severity |
|---|---|---|---|---|
| 001 | Headache | 2026-01-15 | 2026-01-17 | Mild |
| 002 | Nausea | 2026-01-20 | 2026-01-21 | Moderate |
| 003 | Headache | 2026-01-18 | 2026-01-20 | Mild |
The information belongs to the AE domain.
The mapping could be:
| Raw Variable | SDTM Variable |
|---|---|
| Event | AETERM |
| Start Date | AESTDTC |
| End Date | AEENDTC |
| Severity | AESEV |
| Subject | USUBJID |
Step 13: Understand AETERM and AEDECOD
This is an important concept for beginners.
Suppose the investigator reports:
Head pain
The reported term can be represented as:
AETERM = Head pain
The coded medical terminology may result in:
AEDECOD = HEADACHE
Therefore:
AETERM represents the reported term, while AEDECOD represents the standardized coded term.
The coding process depends on the study's medical coding conventions and applicable dictionaries.
A programmer should not simply assume that AETERM and AEDECOD are always identical.
Step 14: Create AESEQ
Many SDTM domains require a sequence number to distinguish multiple records for the same subject.
For example:
Subject 001
AESEQ = 1
AESEQ = 2
AESEQ = 3
A simple SAS approach is:
by usubjid;
if first.usubjid then aeseq = 0;
aeseq + 1;
The exact sequence-generation approach should follow the study's programming specifications.
Step 15: Create the AE Dataset
A simplified example:
proc sort data=raw.ae out=ae_sort;
by subject start_date;
run;
data sdtm.ae;
set ae_sort;
by subject;
length studyid $20
domain $2
usubjid $40
aeterm $200
aedecod $200
aestdtc aeendtc $20
aesev $20;
studyid = "ABC001";
domain = "AE";
usubjid = catx("-", studyid, siteid, subject);
if first.subject then aeseq = 0;
aeseq + 1;
aeterm = event;
if upcase(event) = "HEADACHE" then
aedecod = "HEADACHE";
else if upcase(event) = "NAUSEA" then
aedecod = "NAUSEA";
aestdtc = put(start_date, yymmdd10.);
aeendtc = put(end_date, yymmdd10.);
aesev = propcase(severity);
keep studyid domain usubjid aeseq
aeterm aedecod aestdtc aeendtc aesev;
run;
This is an educational example. Production medical coding should follow the study's coding process and applicable dictionary.
Step 16: Create Vital Signs Data
Suppose the raw vital-sign data is:
| Subject | Visit | Test | Result | Unit |
|---|---|---|---|---|
| 001 | Baseline | Systolic BP | 120 | mmHg |
| 001 | Baseline | Diastolic BP | 78 | mmHg |
| 002 | Baseline | Systolic BP | 130 | mmHg |
| 002 | Baseline | Diastolic BP | 82 | mmHg |
| 003 | Baseline | Systolic BP | 118 | mmHg |
| 003 | Baseline | Diastolic BP | 76 | mmHg |
This information belongs to the VS domain.
Step 17: Understand TEST and TESTCD
The Findings domains commonly use a pair of variables such as:
VSTESTCD
VSTEST
For example:
VSTESTCD = SYSBP
VSTEST = Systolic Blood Pressure
and:
VSTESTCD = DIABP
VSTEST = Diastolic Blood Pressure
The TESTCD is a short standardized code, while TEST provides the descriptive test name.
Controlled terminology and applicable domain guidance should be used rather than inventing values arbitrarily.
Step 18: Create VS
A simplified SAS program might be:
data sdtm.vs;
set raw.vitals;
length studyid $20
domain $2
usubjid $40
vstestcd $8
vstest $40
vsorresu $20
vsstresu $20;
studyid = "ABC001";
domain = "VS";
usubjid = catx("-", studyid, siteid, subject);
if upcase(test) = "SYSTOLIC BP" then do;
vstestcd = "SYSBP";
vstest = "Systolic Blood Pressure";
end;
else if upcase(test) = "DIASTOLIC BP" then do;
vstestcd = "DIABP";
vstest = "Diastolic Blood Pressure";
end;
vsorres = strip(result);
vsorresu = unit;
vsstresn = input(result, best.);
vsstresc = strip(result);
vsstresu = unit;
keep studyid domain usubjid vstestcd vstest
vsorres vsorresu vsstresc vsstresn vsstresu;
run;
This demonstrates the distinction between the original result and the standardized result.
Step 19: Understanding ORRES and STRESC/STRESN
Findings domains often distinguish between:
ORRES
The result as originally collected.
STRESC
The standardized character result.
STRESN
The standardized numeric result, where appropriate.
For example:
VSORRES = 120
VSSTRESC = 120
VSSTRESN = 120
For a numeric finding, VSSTRESN can be particularly useful for standardized numerical representation.
Step 20: Create Laboratory Data
Suppose we have:
| Subject | Test | Result | Unit |
|---|---|---|---|
| 001 | Hemoglobin | 13.5 | g/dL |
| 002 | Hemoglobin | 14.1 | g/dL |
| 003 | Hemoglobin | 12.8 | g/dL |
| 001 | ALT | 25 | U/L |
| 002 | ALT | 31 | U/L |
| 003 | ALT | 22 | U/L |
These records belong to the LB domain.
A simplified mapping is:
| Raw | SDTM |
|---|---|
| Test | LBTEST |
| Test Code | LBTESTCD |
| Result | LBORRES |
| Unit | LBORRESU |
| Numeric Result | LBSTRESN |
| Subject | USUBJID |
Step 21: Apply Controlled Terminology
Controlled Terminology is one of the most important parts of CDISC implementation.
CDISC defines Controlled Terminology as standardized expressions and valid values used with data items in CDISC-defined datasets. CDISC maintains this terminology with the National Cancer Institute's Enterprise Vocabulary Services.
For example, instead of allowing multiple inconsistent representations:
Male
male
M
Man
the applicable controlled terminology should determine the appropriate submission representation.
The same principle applies to:
-
Sex
-
Race
-
Ethnicity
-
Severity
-
Relationship
-
Outcome
-
Units
-
Findings tests
-
Other controlled values
Controlled terminology should be checked against the applicable current release rather than copied from an old project.
CDISC's March 27, 2026 terminology release updated SDTM and other terminology files, illustrating why programmers need to verify the terminology version used by the study.
Step 22: Build a Complete SDTM Mapping Table
A practical mapping specification might look like this:
| Raw Domain | Raw Variable | SDTM Domain | SDTM Variable | Transformation |
|---|---|---|---|---|
| Demographics | SUBJECT | DM | SUBJID | Direct |
| Demographics | SITE | DM | SITEID | Direct |
| Demographics | SEX | DM | SEX | Controlled terminology |
| Demographics | AGE | DM | AGE | Direct |
| Exposure | TREATMENT | EX | EXTRT | Standardize |
| Exposure | DOSE | EX | EXDOSE | Direct |
| Exposure | START_DATE | EX | EXSTDTC | ISO 8601 |
| Exposure | END_DATE | EX | EXENDTC | ISO 8601 |
| AE | EVENT | AE | AETERM | Direct |
| AE | EVENT | AE | AEDECOD | Medical coding |
| AE | START_DATE | AE | AESTDTC | ISO 8601 |
| AE | SEVERITY | AE | AESEV | Controlled terminology |
| Vital Signs | TEST | VS | VSTEST | Standardize |
| Vital Signs | RESULT | VS | VSORRES | Direct |
| Laboratory | TEST | LB | LBTEST | Standardize |
| Laboratory | RESULT | LB | LBORRES | Direct |
This mapping specification becomes the blueprint for SAS programming.
Step 23: Understand the Complete SDTM Programming Architecture
A real-world Clinical SAS project is generally more complex than the examples above.
A simplified architecture is:
Raw / EDC Data
|
v
Data Review
|
v
Mapping Specification
|
v
SDTM Programming
|
v
SDTM Domains
|
+---- DM
+---- AE
+---- EX
+---- VS
+---- LB
|
v
Quality Control
|
v
Validation
|
v
Define-XML / Metadata
|
v
Regulatory Submission
Step 24: Validate the SDTM Dataset
Programming is not complete when the SAS dataset is created.
The programmer should check:
Dataset-level checks
-
Correct domain name
-
Correct dataset structure
-
Correct variable names
-
Correct variable labels
-
Correct variable types
-
Correct lengths
-
Appropriate sort order
-
Appropriate keys
Record-level checks
-
Duplicate records
-
Missing identifiers
-
Incorrect dates
-
Invalid controlled terminology
-
Unexpected values
-
Incorrect sequence numbers
-
Inconsistent subject identifiers
Cross-domain checks
For example:
DM.USUBJID
↓
AE.USUBJID
↓
EX.USUBJID
↓
VS.USUBJID
↓
LB.USUBJID
A subject appearing in AE, EX, VS or LB should generally be traceable to the appropriate study subject information.
Step 25: Example Validation Checks in SAS
Check for duplicate subjects in DM:
proc sort data=sdtm.dm out=dm_check nodupkey;
by usubjid;
run;
Check missing USUBJID:
proc sql;
select *
from sdtm.ae
where missing(usubjid);
quit;
Check unexpected AE severity values:
proc freq data=sdtm.ae;
tables aesev;
run;
Check domain values:
proc freq data=sdtm.ae;
tables domain;
run;
These simple checks are useful during development, although production validation normally includes substantially more extensive checks.
Step 26: CDISC Is More Than SDTM
One common misconception among beginners is:
CDISC = SDTM
That is not correct.
CDISC is a broader standards ecosystem.
For a Clinical SAS programmer, the important components include:
CDISC
|
+-- CDASH
|
+-- SDTM
|
+-- ADaM
|
+-- Define-XML
|
+-- Controlled Terminology
|
+-- Therapeutic Area Standards
CDISC states that its foundational standards form the basis of an end-to-end standards suite, while therapeutic-area standards extend the foundational standards for disease-specific data.
Step 27: SDTM vs ADaM
It is important to understand the difference.
SDTM
The primary objective is to organize clinical study data into standardized tabulation structures.
ADaM
The primary objective is to create analysis-ready datasets with traceability to SDTM and the underlying data.
A simplified relationship is:
RAW DATA
↓
SDTM
↓
ADaM
↓
Tables, Listings & Figures
For example:
Raw Laboratory Data
↓
LB
↓
ADLB
↓
Lab Tables / Analysis
Step 28: Hands-On Exercise for Clinical SAS Students
If you are learning Clinical SAS, do not stop after reading the examples.
Create your own mini clinical trial.
Create 20 subjects
Include:
SUBJECT
SITE
SEX
AGE
TREATMENT
Create adverse events
Include:
SUBJECT
EVENT
START_DATE
END_DATE
SEVERITY
Create vital signs
Include:
SUBJECT
VISIT
TEST
RESULT
UNIT
Create laboratory data
Include:
SUBJECT
TEST
RESULT
UNIT
Then create:
DM
AE
EX
VS
LB
using SAS.
Step 29: Build Your Own Mapping Specification
Before writing code, create a spreadsheet with columns such as:
Raw Dataset
Raw Variable
SDTM Domain
SDTM Variable
Source/Derivation
Controlled Terminology
Algorithm
Comments
For example:
RAW_AE
EVENT
AE
AETERM
Direct Mapping
-
-
Reported event term
and:
RAW_AE
SEVERITY
AE
AESEV
Mapping
CDISC CT
-
Standardize severity
This exercise teaches an extremely important real-world skill:
SDTM programming begins with understanding and documenting the mapping, not with writing SAS code.
Step 30: What a Clinical SAS Programmer Should Learn
A strong CDISC-focused Clinical SAS programmer should understand:
SAS
-
DATA step
-
PROC SQL
-
PROC SORT
-
PROC TRANSPOSE
-
MERGE
-
SET
-
BY-group processing
-
Arrays
-
Functions
-
Formats
-
Macro programming
Clinical Research
-
Clinical trial terminology
-
Visits
-
Treatment
-
Randomization
-
Adverse events
-
Laboratory assessments
-
Vital signs
-
Medical history
-
Concomitant medications
-
Exposure
CDISC
-
CDASH
-
SDTM
-
SDTMIG
-
ADaM
-
Controlled Terminology
-
Define-XML
-
Therapeutic Area User Guides
Programming
-
Raw-to-SDTM mapping
-
Derivations
-
ISO 8601 dates
-
Sequence generation
-
Controlled terminology
-
Cross-domain validation
-
Traceability
-
Quality control
Common Mistakes Beginners Make
Mistake 1: Learning Variable Names Without Understanding the Data
Knowing that AETERM exists is not enough.
You need to understand what the clinical event represents.
Mistake 2: Treating SDTM as a Rename Exercise
SDTM programming involves:
Mapping
+
Derivation
+
Standardization
+
Coding
+
Controlled Terminology
+
Validation
Mistake 3: Ignoring the SDTMIG
The SDTM model and implementation guide should be used together. CDISC describes SDTMIG as the guide for the organization, structure and format of standard clinical trial tabulation datasets.
Mistake 4: Using Old Terminology
Controlled terminology changes over time.
Always verify the applicable release.
Mistake 5: Creating Dates That Were Never Collected
If a date is incomplete or missing, the programmer should follow the applicable standard and study specification rather than inventing information.
Mistake 6: Using SUPPQUAL for Everything
Supplemental qualifiers should be used only when appropriate. First determine whether the information belongs in an existing standard variable, domain, or another supported structure.
A Complete Learning Workflow
A useful way to learn CDISC through Clinical SAS is:
1. Learn Clinical Trial Concepts
↓
2. Learn Base SAS
↓
3. Learn CDISC Fundamentals
↓
4. Study SDTMIG
↓
5. Learn SDTM Domains
↓
6. Practice Mapping
↓
7. Write SAS Programs
↓
8. Create SDTM Datasets
↓
9. Validate
↓
10. Learn ADaM
↓
11. Learn Define-XML
↓
12. Practice Complete Projects
Applying the Clinical Data Interchange Standards Consortium (CDISC) standards is a practical skill that combines clinical knowledge, data standards and SAS programming.
The most effective way to learn is not to memorize hundreds of SDTM variables. Instead, start with real-world-style raw data, understand what each observation means, determine the correct SDTM domain, prepare a mapping specification, write the SAS transformation, apply controlled terminology and finally validate the resulting dataset.
A beginner should start with the core domains:
DM → AE → EX → VS → LB → CM → MH → DS
and gradually move toward more complex domains and standards.
CDISC's current standards ecosystem continues to evolve, so Clinical SAS programmers should always verify the applicable SDTM model, implementation guide, controlled terminology release, therapeutic-area guidance and regulatory requirements for the project. CDISC currently lists SDTM v2.1 as a released model, while SDTMIG v3.4 is the published human clinical-trial implementation guide and is documented for use with SDTM v2.0.
For anyone preparing for a career as a Clinical SAS Programmer, SDTM Programmer or CDISC Programmer, hands-on raw-to-SDTM programming practice is one of the best ways to turn theoretical CDISC knowledge into job-ready skills.


