Skip to content
EHI Playground

Other Common Patterns

Purpose: To complete your pattern recognition toolkit with additional conventions found throughout Epic’s database.

You’ve mastered the major patterns, but Epic’s database contains several more conventions that appear repeatedly. Understanding these completes your ability to navigate any table confidently.

Discover the most common column patterns

View SQL Query
WITH pattern_analysis AS (
    SELECT 
        CASE
            WHEN column_name LIKE '%_YN' THEN '_YN (Yes/No)'
            WHEN column_name LIKE '%_ID_%NAME' THEN '_ID/_NAME pairs'
            WHEN column_name LIKE '%_AMT' THEN '_AMT (Amounts)'
            WHEN column_name LIKE '%_DTTM' THEN '_DTTM (DateTime)'
            WHEN column_name LIKE '%_DT' THEN '_DT (Date)'
            WHEN column_name LIKE '%_NUM' THEN '_NUM (Number)'
            WHEN column_name LIKE '%_CSN' THEN '_CSN (Contact Serial)'
            ELSE 'Other'
        END as pattern_type,
        column_name
    FROM _metadata
    WHERE column_name IS NOT NULL
)
SELECT 
    pattern_type,
    COUNT(*) as occurrence_count
FROM pattern_analysis
WHERE pattern_type != 'Other'
GROUP BY pattern_type
ORDER BY occurrence_count DESC
pattern_typeoccurrence_count
_YN (Yes/No)618
_ID/_NAME pairs432
_DT (Date)255
_DTTM (DateTime)246
_AMT (Amounts)226
_NUM (Number)178
_CSN (Contact Serial)40

Epic uses _YN suffix for all boolean fields, with only two valid values:

Understand the _YN boolean pattern

View SQL Query
-- Check valid values
SELECT DISTINCT 
    PRIMARY_DX_YN as yn_value,
    COUNT(*) as occurrences
FROM PAT_ENC_DX
GROUP BY PRIMARY_DX_YN
ORDER BY yn_value;

-- See variety of boolean fields
SELECT 
    column_name,
    REPLACE(column_name, '_YN', '') as what_it_tracks
FROM _metadata
WHERE column_name LIKE '%_YN'
  AND table_name = 'PATIENT'
ORDER BY column_name
LIMIT 10
yn_valueoccurrences
N20
Y12

The rules are simple:

  • ‘Y’ = Yes/True
  • ‘N’ = No/False
  • NULL = Unknown/Not specified

Never expect ‘T’/‘F’, ‘1’/‘0’, or ‘true’/‘false’—Epic exclusively uses Y/N.

Epic frequently pairs technical IDs with human-readable names:

Explore ID/NAME pairs

View SQL Query
-- Find examples of paired columns
SELECT 
    REPLACE(column_name, '_NAME', '') as base_column,
    GROUP_CONCAT(column_name, ' + ') as paired_columns
FROM _metadata
WHERE table_name = 'PROBLEM_LIST'
  AND (column_name LIKE '%USER_ID' OR column_name LIKE '%USER_ID_NAME')
GROUP BY REPLACE(column_name, '_NAME', '')
ORDER BY base_column
base_columnpaired_columns
ENTRY_USER_IDENTRY_USER_ID + ENTRY_USER_ID_NAME
NO_STAGE_USER_IDNO_STAGE_USER_ID + NO_STAGE_USER_ID_NAME

This pattern serves two purposes:

  1. ID: For joining and referential integrity
  2. NAME: For immediate human readability without joins

See ID/NAME pairs in action

View SQL Query
SELECT 
    PROBLEM_LIST_ID,
    ENTRY_USER_ID,
    ENTRY_USER_ID_NAME,
    DESCRIPTION
FROM PROBLEM_LIST
WHERE ENTRY_USER_ID IS NOT NULL
LIMIT 5;
PROBLEM_LIST_IDENTRY_USER_IDENTRY_USER_ID_NAMEDESCRIPTION
90574164RAMMELZLRAMMELKAMP, ZOE L
30694847DHILLOPSDHILLON, PUNEET S

The Continuation Table Pattern (_2, _3, _4)

Section titled “The Continuation Table Pattern (_2, _3, _4)”

Epic splits wide tables across multiple physical tables:

Discover continuation tables

View SQL Query
SELECT 
    SUBSTR(name, 1, LENGTH(name) - 2) as base_table,
    GROUP_CONCAT(name, ', ') as all_parts,
    COUNT(*) as table_count
FROM sqlite_master
WHERE type = 'table'
  AND (name LIKE '%\_2' ESCAPE '\' 
       OR name LIKE '%\_3' ESCAPE '\' 
       OR name LIKE '%\_4' ESCAPE '\')
GROUP BY base_table
HAVING table_count >= 1
ORDER BY table_count DESC
LIMIT 10;
base_tableall_partstable_count
REFERRALREFERRAL_2, REFERRAL_3, REFERRAL_43
PAT_ENCPAT_ENC_2, PAT_ENC_3, PAT_ENC_43
PATIENTPATIENT_2, PATIENT_3, PATIENT_43
ORDER_PROCORDER_PROC_2, ORDER_PROC_3, ORDER_PROC_43
ORDER_MEDORDER_MED_2, ORDER_MED_3, ORDER_MED_43
HSP_ACCOUNTHSP_ACCOUNT_2, HSP_ACCOUNT_3, HSP_ACCOUNT_43
CLM_VALUESCLM_VALUES_2, CLM_VALUES_3, CLM_VALUES_43
SVC_LN_INFOSVC_LN_INFO_2, SVC_LN_INFO_32
ORDER_DISP_INFOORDER_DISP_INFO_2, ORDER_DISP_INFO_32
HSP_TRANSACTIONSHSP_TRANSACTIONS_2, HSP_TRANSACTIONS_32

These aren’t separate entities—they’re chunks of the same logical record, split because:

  • SQL Server has column limits
  • Chronicles records can be enormous
  • Historical reasons from older database versions

Always query them together:

SELECT p.*, p2.*, p3.*
FROM PATIENT p
LEFT JOIN PATIENT_2 p2 ON p.PAT_ID = p2.PAT_ID
LEFT JOIN PATIENT_3 p3 ON p.PAT_ID = p3.PAT_ID;

Epic uses multiple patterns for temporal data:

Analyze temporal column patterns

View SQL Query
WITH date_patterns AS (
    SELECT 
        CASE
            WHEN column_name LIKE '%_DTTM' THEN 'DateTime (_DTTM)'
            WHEN column_name LIKE '%_DT' AND column_name NOT LIKE '%_DTTM' THEN 'Date only (_DT)'
            WHEN column_name LIKE '%_DATE' THEN 'Date (spelled out)'
            WHEN column_name LIKE '%_TIME' THEN 'Time only (_TIME)'
            WHEN column_name LIKE '%_TM' AND column_name NOT LIKE '%_DTTM' THEN 'Time (_TM)'
        END as pattern,
        column_name,
        table_name
    FROM _metadata
    WHERE column_name LIKE '%_DT%' 
       OR column_name LIKE '%_DATE%'
       OR column_name LIKE '%_TIME%'
       OR column_name LIKE '%_TM'
)
SELECT 
    pattern,
    COUNT(*) as column_count,
    SUBSTR(GROUP_CONCAT(DISTINCT column_name), 1, 100) as examples
FROM date_patterns
WHERE pattern IS NOT NULL
GROUP BY pattern
ORDER BY column_count DESC
LIMIT 5
patterncolumn_countexamples
Date (spelled out)445CONTACT_DATE,BIRTHDATE,CONTRACT_EXP_DATE,HB_INS_AGING_DATE,HB_LAST_DEMAND_DATE,HB_LAST_STMT_DATE,HB_
Date only (_DT)255EB_LAST_D_INFO_DT,EB_LAST_D_STMT_DT,EB_LAST_INFO_ST_DT,EB_LAST_ND_STMT_DT,HB_BILL_NOTE_EXP_DT,HB_LAS
DateTime (_DTTM)246ABN_FOLLOW_UP_INST_DTTM,ADDR_CHG_INSTANT_DTTM,HBMYC_LST_DB_V_DTTM,HBMYC_LST_LT_V_DTTM,HBMYC_LST_ST_V
Time only (_TIME)101ACTION_DATETIME,CR_HX_TIME,SERVICE_TIME,START_TIME,STOP_TIME,ADMIT_DATETIME,INJURY_DATETIME,SPARCS_E
Time (_TM)49FIRST_TREAT_HOUR_TM,DISCHARGE_HOUR_TM,ADMSN_TM,DISCHG_TM,ADJUD_TM,DAILY_DIGEST_TM,ACQUISITION_TM,FRA

Each serves a specific purpose:

  • _DTTM: Full datetime stamp
  • _DT: Date only (often abbreviated)
  • _DATE: Date only (spelled out)
  • _TIME: Time portion only
  • _REAL: Precise chronological ordering (as you learned)

All monetary values use the _AMT suffix:

Explore financial amount columns

View SQL Query
SELECT 
    table_name,
    column_name,
    SUBSTR(documentation, 1, 100) || '...' as description
FROM _metadata
WHERE column_name LIKE '%_AMT'
  AND documentation LIKE '%dollar%' OR documentation LIKE '%amount%'
ORDER BY RANDOM()
LIMIT 5
table_namecolumn_namedescription
SVC_LN_INFO_2LN_POST_CLM_AMTStores Line Postage Claimed Amount...
HSP_CLP_CMS_LINEADJUSTMENT_AMTStores the adjustment amount for the claim line....
PMT_EOB_INFO_ICOB_AMTThe Coordination of�Benefits amount for that transaction....
CL_RMT_INP_ADJ_INFCLM_DISP_SHAR_AMTMonetary amount for the claim disproportionate share for inpatient adjudication information....
ORDER_MEDDOSAGEThe dispensation amount for the prescription entered by the user in the orders activity. This amount...

These are always:

  • REAL data type
  • Stored in dollars (not cents)
  • Can be negative (credits, adjustments)
  • NULL when not applicable

CSN (Contact Serial Number) is Epic’s unique identifier for encounters:

Understand CSN usage

View SQL Query
SELECT 
    column_name,
    COUNT(DISTINCT table_name) as used_in_tables
FROM _metadata
WHERE column_name LIKE '%_CSN%'
GROUP BY column_name
ORDER BY used_in_tables DESC
LIMIT 10
column_nameused_in_tables
PAT_ENC_CSN_ID85
NOTE_CSN_ID5
HX_LNK_ENC_CSN3
TREATMENT_PLAN_CSN2
PRIM_ENC_CSN_ID2
PAT_CSN2
EPT_CSN2
CARE_PLAN_CSN_ID2
UAC_LME_CSN1
TR_TEAM_SRC_CSN_ID1

CSNs are:

  • Globally unique across the entire Epic system
  • Never reused
  • The primary way to link encounter-related data

Let’s see multiple patterns in one query:

See all patterns working together

View SQL Query
SELECT 
    -- ID/NAME pair
    ENTRY_USER_ID,
    ENTRY_USER_ID_NAME,
    
    -- _YN boolean
    CHRONIC_YN,
    
    -- _C_NAME category
    PROBLEM_STATUS_C_NAME,
    
    -- Standard date
    DATE_OF_ENTRY,
    
    -- The actual problem
    DESCRIPTION
    
FROM PROBLEM_LIST
WHERE PROBLEM_LIST_ID IS NOT NULL
LIMIT 5;
ENTRY_USER_IDENTRY_USER_ID_NAMECHRONIC_YNPROBLEM_STATUS_C_NAMEDATE_OF_ENTRYDESCRIPTION
RAMMELZLRAMMELKAMP, ZOE LNActive8/29/2022 12:00:00 AM
DHILLOPSDHILLON, PUNEET SNActive8/9/2018 12:00:00 AM

When encountering a new table:

  1. Check for LINE columns → Indicates one-to-many relationships
  2. Look for _HX suffix → Has historical tracking
  3. Scan for _C_NAME → Contains denormalized categories
  4. Find _REAL columns → Use these for chronological sorting
  5. Identify _YN fields → Boolean flags with Y/N values
  6. Spot ID/NAME pairs → Denormalized for convenience
  7. Check for _2, _3 tables → Continuation of the base table

  • _YN columns are Epic’s boolean pattern (Y/N/NULL only)
  • ID/NAME pairs provide both referential integrity and human readability
  • Continuation tables (_2, _3, _4) are parts of the same logical record
  • Date patterns vary: _DTTM (datetime), _DT (date), _TIME (time only)
  • _AMT indicates financial amounts in dollars
  • _CSN columns contain Contact Serial Numbers for encounter linking
  • These patterns combine with the major five to form Epic’s complete data grammar