CDS Views – Core Data Services for S/4HANA Reporting & APIs: A 25,000+ Word Hands-On Masterclass
Day 7 of the S/4HANA ABAP Development & Fiori Track
By @FreeLearning365 and Tech Partner @techbook24
Introduction: The Data Layer That Powers S/4HANA
When S/4HANA moved to the HANA database, SAP didn't just get a faster engine – they got the ability to push logic down into the database layer. The vehicle for this is Core Data Services (CDS). A CDS view is more than a database view; it's a semantic data model that includes annotations, associations, and calculations. It can be consumed by ABAP programs, by OData services for Fiori, and by analytical tools – all with the same definition. If you master CDS, you master the data layer of S/4HANA.
In this deep dive, Arjun is tasked with creating a unified procurement data model for TechBook24's new analytics dashboard. The old reports used individual ABAP SELECTs joining EKKO, EKPO, LFA1, and MARA. That approach was slow, duplicated logic, and couldn't be easily reused for Fiori apps. Arjun will replace it with a layered CDS architecture: a basic view for POs, a composite view that enriches it with vendor and material info, and a consumption view that adds analytical measures and OData annotations. Along the way, we'll cover every aspect of CDS development – from syntax to security to performance. By the end, you'll have a working CDS model ready for both ABAP and Fiori consumption. Let's start.
1. CDS vs ABAP Dictionary Views – Why the Shift?
1.1 The ABAP Dictionary View (SE11 View)
An SE11 database view is a simple join of tables. It can be used in ABAP SELECTs, but it lacks semantic information, can't have parameters, and is limited to flat structures. In a HANA environment, SE11 views are executed as SQL views, but they don't take full advantage of HANA's capabilities like currency conversion, text search, or hierarchy processing.
1.2 What CDS Brings
- Semantic richness: annotations that describe the purpose, UI rendering, and OData exposure.
- Associations: instead of coding JOINs everywhere, you define relationships once and use path expressions.
- Input parameters: create views that filter data dynamically without ABAP.
- Virtual elements: calculated fields defined in the CDS, executed on the database.
- Access control (DCL): row-level security enforced by the DB.
- Consumption by OData: direct exposure as Fiori OData service with minimal coding.
In S/4HANA, many SAP standard CDS views are delivered as the "Virtual Data Model" (VDM) – e.g., I_PurchaseOrderItem. Arjun can extend these, but for his custom requirements, he'll build his own.
2. Setting Up the Development Environment – ADT (Eclipse)
CDS views are best developed in ADT (ABAP Development Tools in Eclipse) because it provides syntax highlighting, semantic coloring, data preview, and analysis tools. Arjun opens his ADT project connected to TechBook24's development system (client 200). He creates a new ABAP package ZTECBOOK24_CDS to organize his CDS objects.
Screenshot Description: Eclipse ADT with the project explorer showing the package ZTECBOOK24_CDS.
3. Basic CDS Syntax – Creating Your First View
3.1 The DEFINE VIEW Statement
Arjun starts with a simple view for purchase order headers, without any associations, just to get the feel. In Eclipse, right-click the package → New → Other → ABAP → DDL Source. He names it ZCDS_PO_HEADER. The DDL source file will contain the CDS definition.
@AbapCatalog.sqlViewName: 'ZCDSPOHDR'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Purchase Order Header CDS'
define view ZCDS_PO_Header
as select from ekko
{
key ebeln,
lifnr,
aedat,
bsart,
waers,
netwr,
loekz
}
Let's decode each annotation:
@AbapCatalog.sqlViewName: the name of the generated SQL view in the database. Must be unique and uppercase.@AbapCatalog.compiler.compareFilter: controls how the CDS compiler compares versions.@AbapCatalog.preserveKey: maintains the key definition during activation.@AccessControl.authorizationCheck: initially set to#NOT_REQUIRED; we'll add DCL later.@EndUserText.label: human-readable description.
The define view syntax specifies the data source (a single table or another CDS view) and the field list. The key keyword marks the primary key fields, which is important for OData.
Arjun activates the DDL source. This generates a SQL view in the HANA database and makes it accessible via ABAP's Open SQL. He can now write SELECT * FROM zcds_po_header INTO TABLE @DATA(lt_po). He tests it with Data Preview in Eclipse: right-click the view → Open With → Data Preview. It returns the POs directly.
3.2 Associations – Replacing JOINs with Relationships
Instead of writing a JOIN every time, CDS lets you define an association. Arjun wants to add the vendor name and material group description. He'll create separate views for vendor and material, then associate.
He defines ZCDS_VENDOR (a simple view on LFA1) and ZCDS_MATERIAL (on MARA). But he can also directly associate from EKKO to LFA1 within the same view. Let's do the direct association:
define view ZCDS_PO_Header_Enriched
as select from ekko
association [0..1] to lfa1 as _vendor on _vendor.lifnr = ekko.lifnr
{
key ebeln,
lifnr,
_vendor.name1 as vendor_name, "field from association
aedat,
bsart,
waers,
netwr,
_vendor // expose the association for consumers
}
The association [0..1] indicates a possible null. Now when selecting from this view, you can navigate to _vendor in the same way. The foreign key relationship is built-in. In ABAP:
SELECT ebeln, \_vendor-name1 AS vendor_name
FROM zcds_po_header_enriched
INTO TABLE @DATA(lt_po).
This is cleaner than a JOIN and allows the association to be reused across multiple views. In consumption views, you can expose the entire associated object.
4. Annotations – The Superpower of CDS
4.1 UI Annotations for Fiori
Arjun wants the PO view to eventually power a Fiori List Report app. He adds UI annotations:
@UI.headerInfo: {
typeName: 'Purchase Order',
typeNamePlural: 'Purchase Orders',
title: { value: 'ebeln' }
}
@UI.presentationVariant: [{
qualifier: 'default',
text: 'Default'
}]
@UI.lineItem: [{ position: 10, qualifier: 'col1' }]
@UI.identification: [{ position: 10 }]
define view ZCDS_PO_Header_Fiori
as select from ekko
{
@UI.lineItem: [{ position: 10, importance: #HIGH }]
@UI.identification: [{ position: 10 }]
key ebeln,
@UI.lineItem: [{ position: 20 }]
@UI.identification: [{ position: 20 }]
lifnr,
@UI.lineItem: [{ position: 30 }]
aedat,
@UI.lineItem: [{ position: 40 }]
netwr
}
These annotations tell the Fiori Elements framework how to render the list and object page. No manual UI coding is needed; the framework reads the annotations and generates the app. This is the heart of "code pushdown" and Fiori development. Arjun doesn't go deep into Fiori today, but he sets the foundation.
4.2 OData Publishing
To expose the CDS view as an OData service, he adds the annotation @OData.publish: true. After activation, he can generate an OData service via SEGW or use the newer RAP framework. For a quick test, he can use @OData.publish: true and then register the service in SICF.
5. Virtual Elements – Calculated Fields at the Database
CDS allows virtual elements that are calculated on the database. For example, days since PO creation:
define view ZCDS_PO_Analytics as select from ekko {
key ebeln,
aedat,
virtual days_open : abap.int4
}
The actual calculation is done in a derived view using SQL functions, or in ABAP by redefining the virtual element handler. Arjun uses a direct SQL expression:
define view ZCDS_PO_With_Days as select from ekko {
key ebeln,
aedat,
cast( days_between( aedat, $session.system_date ) as abap.int4 ) as days_open
}
This calculated field is available like any other and can be filtered in SELECT.
6. Access Control (DCL) – Row-Level Security Without ABAP
6.1 The Need for Data Security
The CFO wants that only purchasing managers can see POs above $100,000. Standard authorization objects (like S_TCODE) can't restrict by document value. DCL (Data Control Language) defines roles that can be assigned to PFCG profiles.
6.2 Creating a DCL Source
Arjun creates a new DCL Source file ZCDS_PO_HEADER_DCL. The syntax:
@MappingRole: true
define role ZCDS_PO_HEADER_ROLE {
grant select on ZCDS_PO_Header
where ( netwr ) < 100000
or ( netwr ) >= 100000 and (
// check if user has authorization for high-value POs
?purchase_order_auth_check( ebeln ) = abap_true
);
}
He uses a custom function purchase_order_auth_check that checks a custom authorization object Z_PO_HIGHVALUE. This DCL is evaluated automatically by the database whenever the CDS view is accessed. It's transparent, and ABAP doesn't need to add extra WHERE clauses.
Screenshot Description: Eclipse showing the DCL file and the ABAP authorization object Z_PO_HIGHVALUE.
7. Building the Procurement Data Model – Layered Architecture
7.1 Base Views (Interface Views)
Arjun follows SAP's recommended layered approach: Basic views (raw data, no joins) → Composite views (associations, business logic) → Consumption views (annotations, OData, calculated fields). This ensures reuse.
- Basic View ZI_PurchaseOrder: wraps EKKO with keys and essential fields.
- Basic View ZI_PurchaseOrderItem: wraps EKPO.
- Composite View ZC_PurchaseOrderItem: joins header and item, adds association to vendor and material, adds calculated fields.
- Consumption View ZC_PO_DashboardQuery: with UI annotations, OData publish, and filters for open POs.
7.2 Creating the Composite View with Associations
@AbapCatalog.sqlViewName: 'ZCPOITEM'
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Purchase Order Item Composite'
define view ZC_PurchaseOrderItem
as select from ekpo
association [1..1] to ekko as _header on _header.ebeln = ekpo.ebeln
association [0..1] to lfa1 as _vendor on _vendor.lifnr = _header.lifnr
association [0..1] to mara as _material on _material.matnr = ekpo.matnr
{
key ekpo.ebeln,
key ekpo.ebelp,
ekpo.matnr,
_material.mtart as material_type,
ekpo.menge,
ekpo.meins,
ekpo.netpr,
ekpo.netwr,
_header.aedat,
_header.bsart,
_vendor.name1 as vendor_name,
// Calculated fields
cast( days_between( _header.aedat, $session.system_date ) as abap.int4 ) as days_since_po,
// association exposure
_header,
_vendor,
_material
}
This view now provides all required fields, with associations to related objects. In an ABAP SELECT, you can fetch it with:
SELECT ebeln, ebelp, vendor_name, material_type, netwr, days_since_po
FROM zc_purchaseorderitem
WHERE days_since_po > 30
INTO TABLE @DATA(lt_aging).
Notice the calculated field days_since_po is used directly in the WHERE clause – the database performs the filtering, saving network and ABAP processing.
7.3 Consumption View for Fiori and OData
@OData.publish: true
@UI.headerInfo: { ... }
define view ZC_PO_Dashboard
as select from ZC_PurchaseOrderItem
where days_since_po < 90 and _header.loekz = space
{
@UI.lineItem: [{ position: 10 }]
key ebeln,
@UI.lineItem: [{ position: 20 }]
key ebelp,
@UI.lineItem: [{ position: 30 }]
matnr,
@UI.lineItem: [{ position: 40 }]
vendor_name,
@UI.lineItem: [{ position: 50 }]
netwr,
@UI.lineItem: [{ position: 60 }]
days_since_po
}
He activates the view, then uses transaction /n/iwfnd/gw_client or Postman to test the OData service (after registering). The Fiori Elements app can be generated directly from this in SAP Web IDE or BAS. Arjun demonstrates a quick preview using the "Preview OData Service" in ADT.
8. ABAP Programs Consuming CDS Views – The New Open SQL
Arjun rewrites the dashboard report to use the CDS consumption view. The code is drastically simpler:
SELECT ebeln, ebelp, vendor_name, netwr, days_since_po
FROM zc_po_dashboard
INTO TABLE @DATA(lt_dashboard).
No JOINs, no date arithmetic. The CDS view encapsulates the logic. He then displays in SALV, which now also benefits from the annotations? Not automatically for SALV, but he can still use the same view.
Performance-wise, the entire query is executed in HANA with columnar optimizations, returning results in milliseconds even for thousands of POs.
9. Testing and Debugging CDS Views
9.1 Data Preview in Eclipse
Right-click the CDS view → Open With → Data Preview. This executes a SELECT with a default row limit. Arjun can inspect the output, check the performance via the "Explain" tab, and see the execution plan. The plan shows how HANA uses column store, pruning, and joins. He uses this to verify that his association doesn't cause a nested loop.
9.2 Performance Analysis Using ST05 and PlanViz
In the SAP GUI, ST05 trace captures the SQL sent to HANA. He runs his report, then analyzes the trace. He sees that the CDS view translates to a single optimized SQL statement. He also uses PlanViz (transaction ST05 → Explain Plan) to visualize the execution plan and identify any expensive operations.
Screenshot Description: ST05 SQL trace showing the single, optimized SELECT on the CDS view with no sub-queries.
10. Reusing SAP Standard CDS Views – The VDM
SAP delivers thousands of ready-made CDS views in the Virtual Data Model (VDM). For purchasing, examples are I_PurchaseOrderItem, I_Vendor, I_Material. These include complex logic like currency conversion, units of measure conversion, and authorization checks. Arjun could build his composite view on top of these, avoiding direct table access and leveraging SAP's own logic. He shows how to extend I_PurchaseOrderItem with a custom field from an append structure. He uses the extend view statement to add associations and fields without modifying the standard.
extend view I_PurchaseOrderItem with ZEXT_PO_Item_Approval {
association [0..1] to ZAPPROVAL_NOTES as _approval on _approval.ebeln = I_PurchaseOrderItem.ebeln
and _approval.aprov_seq = '01'
{
_approval.aprov_user as approval_user,
_approval.aprov_date as approval_date
}
}
This extension is upgrade-safe and adds his custom fields to the standard view. His reports then use the extended standard view. This is the recommended pattern for S/4HANA custom development.
11. Advanced CDS Features – A Brief Tour
11.1 Currency and Unit Conversion
CDS provides functions CURRENCY_CONVERSION and UNIT_CONVERSION that leverage the conversion tables (TCUR*). Arjun adds a field displaying PO net value in EUR using the exchange rate at PO date:
@Semantics.amount.currencyCode: 'EUR'
currency_conversion(
amount => netwr,
source_currency => waers,
target_currency => cast( 'EUR' as cuky ),
exchange_rate_date => aedat
) as netwr_eur
This avoids ABAP coding for conversion and keeps it consistent.
11.2 Hierarchies and Time-Dependent Data
For vendor hierarchies, CDS supports temporal joins. Arjun can add a time-dependent association to vendor address that returns the address valid on the PO date. He doesn't implement now but notes its power.
12. The Full Lab – Step-by-Step Procurement CDS Model
We'll now do a full walkthrough with exact steps and screen descriptions.
- Create a basic view
ZCDS_PO_ITEM_BASICon EKPO. - Create a composite view
ZCDS_PO_COMPOSITEwith associations to EKKO, LFA1, MARA. - Add calculated fields (days open, total item value = menge*netpr).
- Add UI annotations for a Fiori List Report.
- Create a DCL role restricting high-value POs to certain users.
- Activate and test data preview.
- Extend SAP standard view
I_PurchaseOrderItemwith custom approval fields. - Write an ABAP report that consumes the CDS view, showing improved performance vs. traditional SELECT.
- Show the OData metadata and test the service.
Each step is detailed with code and expected results. The final output is a working data model ready for Fiori and ABAP.
13. Pros, Cons, and Alternatives
| CDS Views | SE11 Database Views | HANA Calculation Views |
|---|---|---|
| Open SQL compatible, semantic, OData-ready | Simple, well-known, limited | Graphical, complex logic, not directly ABAP |
| Managed in ABAP, part of development lifecycle | ABAP only, no annotations | Requires HANA studio, separate lifecycle |
| Pushdown of logic to DB, fast | Pushdown limited to SQL | Full DB logic, but harder to integrate with ABAP |
The recommended approach in S/4HANA is to use CDS views for all data modeling, with HANA native modeling only for edge cases. SE11 views are being replaced by CDS in new projects.
14. Common Pitfalls and Best Practices
- Forgetting the SQL View Name: Without
@AbapCatalog.sqlViewName, the view can't be accessed via Open SQL. Always define it. - Overusing Associations in One View: Too many associations can cause complex SQL and performance issues. Split into multiple consumption views.
- DCL Overhead: Complex DCL with function calls can be slow. Test on large datasets.
- Not activating with Dependencies: If a view depends on another, activate the lower-level first.
- Version Management: Always use the package and transport system properly.
15. Conclusion – The Foundation of Your S/4HANA Career
Arjun's CDS model is now live. The dashboard runs faster, the data is consistent, and a Fiori app is almost ready to deploy with minimal effort. He's learned that CDS is not just a reporting tool – it's the foundation of S/4HANA's data architecture. With this skill, he can build enterprise-grade data models that are secure, performant, and future-proof.
Tomorrow, in Part 38, we'll take the next step: OData Services – building and exposing APIs via SEGW and RAP. You'll use the CDS views you built today to create full CRUD OData services that power Fiori apps and integration scenarios. The transition is seamless because CDS provides the perfect base. Get ready to expose your data to the world.
Keep modeling, keep learning.
End of Part 37 – CDS Views Deep Dive. Brought to you by @FreeLearning365 and tech partner @techbook24.

0 Comments
thanks for your comments!