How to Track Changes in SAP: CDHDR and CDPOS Explained
How to Track Changes in SAP: CDHDR and CDPOS Explained
Master SAP's change document architecture to build powerful audit trails, track data modifications, and ensure compliance. This comprehensive guide reveals how CDHDR and CDPOS work together to capture every critical change in your SAP system.
Understanding SAP Change Documents: The Foundation of Audit Trails
Every compliance officer, auditor, and SAP developer eventually asks the same question: "Who changed this data, when did they change it, and what was the old value?" SAP's change document framework provides the answer through two powerful tables: CDHDR (Change Document Header) and CDPOS (Change Document Items).
Unlike database-level change tracking mechanisms, SAP's change document architecture operates at the application layer, capturing business-relevant changes with full context. This means you don't just see that field X changed—you see which business object was affected, which transaction was used, who made the change, and the complete before-and-after picture.
The change document framework isn't automatic for every table in SAP. It must be explicitly activated for specific business objects through customizing, which is why understanding the architecture is crucial for developers implementing audit requirements.
CDHDR: The Change Document Header Table
CDHDR serves as the master record for each change document, capturing the contextual metadata around when and how a change occurred. Think of it as the envelope that contains all the details about a change transaction.
Critical Fields in CDHDR
- OBJECTCLAS - The object class identifying what type of business object changed (e.g., 'MATERIAL' for materials, 'VENDORS' for vendor master, 'KRED' for credit management)
- OBJECTID - The unique identifier of the specific object instance (e.g., material number '100000123', vendor number '1000')
- CHANGENR - The change document number, serving as the primary key and linking header to items
- USERNAME - The user ID who executed the change
- UDATE - The date when the change was made
- UTIME - The time when the change was made
- TCODE - The transaction code used to make the change
- CHANGE_IND - The change indicator: 'U' for update, 'I' for insert, 'D' for delete, 'E' for deletion flag
The combination of OBJECTCLAS and OBJECTID allows you to track all changes for a specific business object across time. The CHANGENR field connects each header record to potentially hundreds of detailed line items in CDPOS.
Real-World CDHDR Query Example
Here's how you'd retrieve all changes made to a specific vendor in the last 90 days:
SELECT
h.changenr,
h.objectclas,
h.objectid,
h.username,
h.udate,
h.utime,
h.tcode,
h.change_ind
FROM cdhdr AS h
WHERE h.objectclas = 'KRED' -- Vendor master object class
AND h.objectid = '0000001000' -- Specific vendor number
AND h.udate >= '20250101' -- Changes since January 1, 2025
ORDER BY h.udate DESC, h.utime DESC; This query immediately tells you every transaction that touched vendor 1000, who made the change, and when—essential for vendor master audits and supplier relationship management.
CDPOS: Where the Actual Changes Live
While CDHDR tells you who and when, CDPOS tells you what changed. This is the detail table containing the actual field-level modifications—the old values, the new values, and which fields were affected.
Essential Fields in CDPOS
- CHANGENR - The change document number (foreign key to CDHDR)
- OBJECTCLAS - Object class (denormalized from CDHDR for performance)
- OBJECTID - Object ID (denormalized from CDHDR)
- TABNAME - The actual database table name where the change occurred
- TABKEY - The complete key of the changed table record (concatenated)
- FNAME - The field name that was changed
- CHNGIND - Change indicator: 'U' update, 'I' insert, 'D' delete, 'E' flag
- VALUE_NEW - The new value after the change (CHAR254)
- VALUE_OLD - The previous value before the change (CHAR254)
- TEXT_CASE - Indicates if the change is case-sensitive
Understanding TABKEY: The Critical Link
The TABKEY field deserves special attention. It contains the concatenated
key fields of the changed record, which allows you to precisely identify
which record in which table was modified. For example, if a material
description changes in table MAKT, the TABKEY might look like: 100000123EN (material number + language key).
Parsing TABKEY correctly requires knowledge of the table structure. You need to know the field lengths and sequence to extract meaningful key values. This is where domain knowledge of SAP table structures becomes invaluable.
Practical CDPOS Query: Field-Level Change History
Let's build a comprehensive query that shows exactly what changed for a material master record:
SELECT
h.udate AS change_date,
h.utime AS change_time,
h.username,
h.tcode,
p.tabname AS table_name,
p.fname AS field_name,
p.value_old AS old_value,
p.value_new AS new_value,
p.chngind AS change_type
FROM cdhdr AS h
INNER JOIN cdpos AS p
ON h.changenr = p.changenr
WHERE h.objectclas = 'MATERIAL'
AND h.objectid = '000000000000100000' -- Material number (18 chars, zero-padded)
AND h.udate BETWEEN '20250101' AND '20251231'
ORDER BY h.udate DESC, h.utime DESC, p.tabname, p.fname; This query provides a complete audit trail showing every field that changed, what the values were before and after, and the complete context of who made the change and when.
Common Object Classes and Their Use Cases
Understanding which object class to query is essential for effective change tracking. Here are the most commonly audited object classes in SAP systems:
| Object Class | Business Object | Typical Use Case |
|---|---|---|
MATERIAL | Material Master | Product data changes, pricing updates |
KRED | Vendor Master | Supplier audits, payment term changes |
DEBI | Customer Master | Credit limit tracking, address updates |
BELEG | Accounting Documents | Financial audit trails, posting changes |
EINKBELEG | Purchase Orders | PO modifications, price change tracking |
VERKBELEG | Sales Orders | Sales audit, delivery date changes |
KONDITION | Pricing Conditions | Price change audits, discount tracking |
Each object class has its own set of related tables in CDPOS. For example, MATERIAL changes might affect MARA (general material data), MARC (plant data), MAKT (descriptions), and dozens of other tables depending on the material type and views being maintained.
Advanced Query Patterns for Compliance and Audit
Pattern 1: Tracking Specific Field Changes Across All Objects
Sometimes you need to track changes to a specific field across all instances. For example, tracking all credit limit changes in the customer master:
SELECT
h.objectid AS customer_number,
h.udate AS change_date,
h.username,
p.value_old AS old_credit_limit,
p.value_new AS new_credit_limit,
CAST(p.value_new AS DECIMAL(15,2)) - CAST(p.value_old AS DECIMAL(15,2)) AS increase
FROM cdhdr AS h
INNER JOIN cdpos AS p
ON h.changenr = p.changenr
WHERE h.objectclas = 'DEBI'
AND p.fname = 'KLIMK' -- Credit limit field
AND p.tabname = 'KNKK' -- Customer master credit management
AND h.udate >= '20250101'
AND p.value_old <> p.value_new -- Exclude non-changes
ORDER BY CAST(p.value_new AS DECIMAL(15,2)) - CAST(p.value_old AS DECIMAL(15,2)) DESC; This query identifies the largest credit limit increases, which is crucial for financial risk management and fraud detection.
Pattern 2: User Activity Analysis
For compliance audits, you often need to analyze what a specific user changed during a particular period:
SELECT
h.objectclas,
h.tcode,
COUNT(DISTINCT h.changenr) AS change_documents,
COUNT(*) AS field_changes,
MIN(h.udate) AS first_change,
MAX(h.udate) AS last_change
FROM cdhdr AS h
INNER JOIN cdpos AS p
ON h.changenr = p.changenr
WHERE h.username = 'JSMITH'
AND h.udate BETWEEN '20250201' AND '20250228'
GROUP BY h.objectclas, h.tcode
ORDER BY field_changes DESC; This summary view shows you what a user has been working on, which transactions they used, and the volume of changes—perfect for detecting unusual activity patterns.
Pattern 3: Mass Change Detection
Mass changes can indicate either legitimate batch updates or potential data quality issues. Here's how to detect them:
SELECT
h.changenr,
h.objectclas,
h.username,
h.udate,
h.utime,
h.tcode,
COUNT(*) AS affected_fields,
COUNT(DISTINCT p.tabname) AS affected_tables
FROM cdhdr AS h
INNER JOIN cdpos AS p
ON h.changenr = p.changenr
WHERE h.udate = '20250215'
GROUP BY h.changenr, h.objectclas, h.username, h.udate, h.utime, h.tcode
HAVING COUNT(*) > 50 -- More than 50 field changes
ORDER BY affected_fields DESC; High field counts in a single change document often indicate mass maintenance activities that warrant additional scrutiny.
Performance Considerations and Best Practices
CDHDR and CDPOS are among the largest tables in any SAP system, often containing hundreds of millions of records. Query performance requires careful consideration:
Critical Performance Tips:
- Always use OBJECTCLAS in your WHERE clause - This is the primary partitioning key in most systems
- Include date restrictions - UDATE is heavily indexed and dramatically reduces result sets
- Avoid SELECT * - Specify only the fields you need, especially with CDPOS
- Use OBJECTID when possible - Searching for specific objects is much faster than broad scans
- Consider archiving - SAP provides archiving objects for change documents (CHANGEDOCU)
- Leverage secondary indexes - Many systems add custom indexes on USERNAME or TCODE
- Use EXISTS for presence checks - Rather than COUNT(*) when you only need to know if changes exist
Optimized Query Example
-- Instead of this slow query:
SELECT * FROM cdhdr WHERE udate >= '20240101';
-- Use this optimized version:
SELECT
objectclas,
objectid,
changenr,
username,
udate,
tcode
FROM cdhdr
WHERE objectclas IN ('MATERIAL', 'KRED', 'DEBI') -- Specific object classes
AND udate BETWEEN '20250201' AND '20250228' -- Narrow date range
AND username IN ('JSMITH', 'MJONES') -- Specific users
WITH HINT (INDEX(cdhdr~0));Activating Change Documents for Custom Objects
If you're developing custom Z-tables and need change tracking, you must explicitly activate the change document framework. This involves several steps:
- Define the change document object using transaction SCDO (Change Document Object)
- Specify which tables and fields should be tracked
- Generate the function modules that write to CDHDR and CDPOS
- Call the generated write function in your update logic
- Test thoroughly to ensure changes are captured correctly
The generated function modules follow naming convention <OBJECT>_WRITE_DOCUMENT and handle all the complexity of writing to both header and item tables.
Sample ABAP Code for Writing Change Documents
DATA: objectid TYPE cdhdr-objectid,
tcode TYPE cdhdr-tcode,
utime TYPE cdhdr-utime,
udate TYPE cdhdr-udate,
username TYPE cdhdr-username,
upd_imseg TYPE TABLE OF imseg,
imseg_old TYPE imseg,
imseg_new TYPE imseg.
objectid = '4500012345'. " Material document number
tcode = 'MIGO'.
udate = sy-datum.
utime = sy-uzeit.
username = sy-uname.
" Call the generated change document write function
CALL FUNCTION 'MB_CREATE_GOODS_MVT_WRITE_DOC'
EXPORTING
objectid = objectid
tcode = tcode
utime = utime
udate = udate
username = username
upd_imseg = upd_imseg
TABLES
imseg_old = imseg_old
imseg_new = imseg_new.Integration with Standard SAP Reports
While direct SQL queries offer maximum flexibility, SAP provides standard transactions for accessing change documents:
- RSSCD100 - Display Change Documents (generic across all object classes)
- S_ALR_87012326 - Material Master Changes
- S_ALR_87012328 - Vendor Master Changes
- S_ALR_87012329 - Customer Master Changes
- FB03 - Display Document with change history (for accounting documents)
- ME23N - Display Purchase Order with change history
These transactions provide user-friendly interfaces but limited flexibility. For complex audit requirements, custom reports querying CDHDR and CDPOS directly are often necessary.
Troubleshooting Common Issues
Issue 1: Missing Change Documents
If expected changes aren't appearing in CDHDR/CDPOS, check:
- Is change document creation activated for this object class? (Check table TCDOB)
- Are the specific fields flagged for change tracking? (Check table TCDOBV)
- Was the change made through a supported transaction or API?
- Are there any filter criteria excluding certain changes?
Issue 2: TABKEY Parsing Errors
TABKEY is a fixed-length concatenated string. If you're getting wrong values, verify the field lengths and positions match the actual table structure. Use transaction SE11 to examine the table definition and calculate the correct offsets.
Issue 3: Performance Degradation
If queries are running slowly, consider:
- Running database statistics to update the optimizer
- Implementing archiving to reduce table size
- Creating secondary indexes on frequently queried fields
- Using SAP BW or S/4HANA embedded analytics for historical analysis
Real-World Use Case: Comprehensive Vendor Master Audit
Let's put everything together with a complete audit scenario. Your compliance team needs a report showing all vendor master changes in the last quarter, focusing on critical fields like bank accounts, payment terms, and contact information:
SELECT
h.objectid AS vendor_number,
lfa1.name1 AS vendor_name,
h.udate AS change_date,
h.utime AS change_time,
h.username AS changed_by,
h.tcode AS transaction_code,
p.tabname AS table_name,
p.fname AS field_name,
CASE p.fname
WHEN 'BANKN' THEN 'Bank Account Number'
WHEN 'BANKL' THEN 'Bank Key'
WHEN 'ZTERM' THEN 'Payment Terms'
WHEN 'KTOKK' THEN 'Account Group'
WHEN 'SPERR' THEN 'Central Block'
WHEN 'SPERM' THEN 'Purchasing Block'
ELSE p.fname
END AS field_description,
p.value_old AS old_value,
p.value_new AS new_value
FROM cdhdr AS h
INNER JOIN cdpos AS p
ON h.changenr = p.changenr
LEFT OUTER JOIN lfa1
ON lfa1.lifnr = h.objectid
WHERE h.objectclas = 'KRED'
AND h.udate >= '20250101'
AND p.fname IN ('BANKN', 'BANKL', 'ZTERM', 'KTOKK', 'SPERR', 'SPERM')
ORDER BY h.objectid, h.udate DESC, h.utime DESC; This query provides exactly what auditors need: who changed critical vendor data, what the changes were, and when they occurred. The field descriptions make the report immediately understandable to non-technical stakeholders.
Conclusion: Mastering SAP Change Documents
Understanding CDHDR and CDPOS is essential for anyone responsible for SAP compliance, auditing, or data governance. These tables provide an unparalleled audit trail when you know how to query them effectively.
Key Takeaways:
- CDHDR captures the context - who, when, what transaction, what object
- CDPOS captures the details - which fields changed, old values, new values
- Always filter by OBJECTCLAS and date range for optimal performance
- Join to master data tables to make reports business-user friendly
- Understand TABKEY structure to correctly parse composite keys
- Use specific field name filters to focus on compliance-critical changes
- Consider archiving strategies for long-term data retention
Whether you're building custom audit reports, investigating data quality issues, or responding to compliance inquiries, these tables are your foundation. Master them, and you'll have unprecedented visibility into your SAP system's data history.
The queries and patterns in this guide provide a solid starting point, but every SAP system is unique. Experiment with these examples, adapt them to your specific object classes and tables, and build a comprehensive change tracking library that meets your organization's needs.
Next Steps
Ready to implement comprehensive change tracking in your SAP system? Start with these actions:
- Identify your most critical business objects and verify change documents are active
- Create a library of standard audit queries for your common use cases
- Build user-friendly reports that join change document data with master data
- Establish archiving policies to manage long-term growth
- Document your object classes and critical fields for audit teams
- Consider integrating change document queries into your governance dashboards
With the knowledge from this guide, you're equipped to build powerful audit trails, ensure compliance, and provide unprecedented transparency into your SAP data changes.