Appearance
Lesson 3 — CDS Expressions, Calculated Elements, and Filtering
In Lesson 2, you created a reusable CDS model:
text
Runner
↓
Reader
↓
ABAP SQL
↓
CDS View Entity
↓
Database TableThe CDS entity mainly exposed existing source fields under business-oriented names.
In this lesson, the CDS model will begin to process and interpret data.
You will create a flight model that exposes:
text
Maximum seats
Occupied seats
Available seats ← calculated
Availability status ← CASE expression
Record type ← typed literalYou will also create a second CDS View Entity that filters the first one.
The architecture becomes:
text
/DMO/FLIGHT
↓
ZI_FLIGHT_###
↓
calculated elements
↓
ZI_AVAILABLE_FLIGHT_###
↓
filtering
↓
Reader
↓
RunnerLesson information
| Duration | About 120 minutes |
| Environment | SAP BTP ABAP Trial |
| Development tool | Eclipse with ABAP Development Tools (ADT) |
| Prerequisites | Lessons 1 and 2 |
| Persistence source | /DMO/FLIGHT |
| Base CDS entity | ZI_FLIGHT_### |
| Filtered CDS entity | ZI_AVAILABLE_FLIGHT_### |
| Reader | ZCL_FLIGHT_READER_### |
| Runner | ZCL_CDS_L03_### |
Learning objectives
After completing this lesson, you should be able to:
- explain what a calculated CDS element is;
- create a calculated element with an arithmetic expression;
- use a
CASEexpression in a CDS View Entity; - use typed literals such as
abap.char'...'; - explain why typed literals are useful in CDS;
- create a CDS View Entity on top of another CDS View Entity;
- add a CDS
WHEREclause; - explain the difference between filtering in CDS and filtering in consuming ABAP SQL;
- use Data Preview to verify calculated elements;
- use SQL Console to query calculated CDS elements;
- use a CDS View Entity as an ABAP global structured type;
- create a Reader that consumes the CDS model;
- explain what logic belongs naturally in the data model and what still belongs in ABAP code.
Before you start
Use your own student/group suffix for every object.
Example:
text
ZI_FLIGHT_### → ZI_FLIGHT_017
ZI_AVAILABLE_FLIGHT_### → ZI_AVAILABLE_FLIGHT_017
ZCL_FLIGHT_READER_### → ZCL_FLIGHT_READER_017
ZCL_CDS_L03_### → ZCL_CDS_L03_017ABAP Cloud and /DMO/*
The /DMO/* artifacts used in this course belong to SAP's ABAP Flight Reference Scenario and are tutorial/demo content.
They are appropriate for these exercises.
For productive ABAP Cloud development, use APIs released for the relevant cloud-development contract rather than assuming arbitrary SAP repository objects are directly consumable.
1. Explore /DMO/FLIGHT
SAP's flight reference scenario defines /DMO/FLIGHT with fields including:
text
carrier_id
connection_id
flight_date
price
currency_code
plane_type_id
seats_max
seats_occupiedThe key is the combination of:
text
carrier_id
connection_id
flight_dateStep 1.1 — Open the table
Press:
text
Ctrl + Shift + ASearch for:
text
/DMO/FLIGHTOpen the database table.
Step 1.2 — Identify the fields
Find:
text
carrier_id
connection_id
flight_date
plane_type_id
seats_max
seats_occupiedConfirm the three key fields.
Step 1.3 — Open Data Preview
Open Data Preview for /DMO/FLIGHT.
Look for records where:
text
seats_max > seats_occupiedand, if present, records where:
text
seats_max = seats_occupiedThe relationship we will model is:
text
Seats available = Maximum seats - Occupied seats2. From exposed fields to calculated elements
In Lesson 2, a CDS element usually came directly from the source:
abap
Flight.seats_max as SeatsMaxA CDS element can also be produced by an expression:
abap
Flight.seats_max - Flight.seats_occupied as SeatsAvailableSeatsAvailable does not exist as a column in /DMO/FLIGHT.
It is calculated by the CDS entity.
text
/DMO/FLIGHT
SeatsMax
SeatsOccupied
↓
CDS
↓
SeatsAvailableMain idea
A CDS data model can expose derived information, not only source fields.
3. Create the base Flight CDS entity
Step 3.1 — Open the Data Definition wizard
Right-click your package:
text
New
→ Other ABAP Repository Object
→ Core Data Services
→ Data DefinitionChoose Next.
Step 3.2 — Enter the details
Use:
text
Name: ZI_FLIGHT_###
Description: Lesson 3 - Flight CDSIf ADT provides Referenced Object, enter:
text
/DMO/FLIGHTChoose the correct transport request if required.
Select a CDS View Entity template.
Confirm the generated source uses:
abap
define view entityFinish the wizard.
4. Start with source fields
Enter:
abap
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Flight'
define view entity ZI_Flight_###
as select from /dmo/flight as Flight
{
key Flight.carrier_id as CarrierId,
key Flight.connection_id as ConnectionId,
key Flight.flight_date as FlightDate,
Flight.plane_type_id as PlaneTypeId,
Flight.seats_max as SeatsMax,
Flight.seats_occupied as SeatsOccupied
}Replace ###.
Run a syntax check:
text
Ctrl + F2Activate:
text
Ctrl + F3Preview:
text
F8At this stage the entity exposes source fields only.
5. Add an arithmetic expression
Add this element after SeatsOccupied:
abap
Flight.seats_max - Flight.seats_occupied as SeatsAvailableThe element list becomes:
abap
{
key Flight.carrier_id as CarrierId,
key Flight.connection_id as ConnectionId,
key Flight.flight_date as FlightDate,
Flight.plane_type_id as PlaneTypeId,
Flight.seats_max as SeatsMax,
Flight.seats_occupied as SeatsOccupied,
Flight.seats_max - Flight.seats_occupied as SeatsAvailable
}Check and activate again.
6. Verify the calculation in Data Preview
Press:
text
F8The result now includes:
text
SeatsAvailablePick a row and verify:
text
SeatsMax - SeatsOccupied = SeatsAvailableThe exact values depend on the demo data in your system.
7. Why calculate this in CDS?
Without the CDS calculation, several consumers might repeat:
text
SeatsMax - SeatsOccupiedWith CDS:
text
/DMO/FLIGHT
↓
ZI_FLIGHT_###
↓
SeatsAvailable
↓
ConsumersThe calculation has one reusable definition.
8. CDS expressions
CDS View Entities support expression families including:
text
arithmetic expressions
CASE expressions
CAST expressions
aggregate expressions
built-in functionsThis lesson focuses on:
text
arithmetic
CASE
typed literals
WHEREThe practice assignment also uses a string function.
9. Add a CASE expression
A number such as:
text
SeatsAvailable = 0is useful.
A descriptive classification can also be useful:
text
Full
AvailableAdd:
abap
case
when Flight.seats_occupied = Flight.seats_max
then abap.char'Full'
else abap.char'Available'
end as AvailabilityStatusThe complete entity becomes:
abap
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Flight'
define view entity ZI_Flight_###
as select from /dmo/flight as Flight
{
key Flight.carrier_id as CarrierId,
key Flight.connection_id as ConnectionId,
key Flight.flight_date as FlightDate,
Flight.plane_type_id as PlaneTypeId,
Flight.seats_max as SeatsMax,
Flight.seats_occupied as SeatsOccupied,
Flight.seats_max - Flight.seats_occupied
as SeatsAvailable,
case
when Flight.seats_occupied = Flight.seats_max
then abap.char'Full'
else abap.char'Available'
end as AvailabilityStatus
}Run:
text
Ctrl + F2then:
text
Ctrl + F3and preview again.
10. How CASE works
The form used here is a searched CASE expression:
abap
case
when <condition>
then <result>
else <result>
endOur condition is:
abap
Flight.seats_occupied = Flight.seats_maxWhen true:
text
AvailabilityStatus = FullOtherwise:
text
AvailabilityStatus = AvailableRemember:
text
CASE → calculates/classifies a value
WHERE → removes rows that do not meet a condition11. Typed literals
The CASE expression uses:
abap
abap.char'Full'This is a typed literal.
The general form is:
text
abap.<type>'value'Examples:
abap
abap.char'Available'
abap.int4'100'
abap.tims'120000'
abap.dats'20261231'Typed literals make the intended type explicit and allow CDS to perform stronger type compatibility checks.
12. Add a constant element
A literal can also become an exposed element.
Add:
abap
abap.char'Flight' as RecordTypePlace it before AvailabilityStatus.
This demonstrates that CDS elements can come from:
text
source fields
calculations
CASE expressions
literals
functions13. Final base CDS source
Your base entity should now look like:
abap
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Flight'
define view entity ZI_Flight_###
as select from /dmo/flight as Flight
{
key Flight.carrier_id as CarrierId,
key Flight.connection_id as ConnectionId,
key Flight.flight_date as FlightDate,
Flight.plane_type_id as PlaneTypeId,
Flight.seats_max as SeatsMax,
Flight.seats_occupied as SeatsOccupied,
Flight.seats_max - Flight.seats_occupied
as SeatsAvailable,
abap.char'Flight' as RecordType,
case
when Flight.seats_occupied = Flight.seats_max
then abap.char'Full'
else abap.char'Available'
end as AvailabilityStatus
}Activate and preview.
14. Query calculated elements in SQL Console
Open SQL Console.
Run:
abap
SELECT FROM zi_flight_###
FIELDS CarrierId,
ConnectionId,
FlightDate,
SeatsMax,
SeatsOccupied,
SeatsAvailable,
AvailabilityStatus
ORDER BY CarrierId,
ConnectionId,
FlightDateThe consumer reads SeatsAvailable and AvailabilityStatus as normal CDS elements.
It does not need to know how they were derived.
15. Filter in SQL Console
Try:
abap
SELECT FROM zi_flight_###
FIELDS CarrierId,
ConnectionId,
FlightDate,
SeatsAvailable,
AvailabilityStatus
WHERE AvailabilityStatus = 'Available'
ORDER BY CarrierId,
ConnectionId,
FlightDateThis filter exists only in this SQL Console query.
Another consumer can still query all rows from ZI_FLIGHT_###.
Next, you will create a CDS entity whose filter is part of the model itself.
16. CDS WHERE
A CDS View Entity can contain a WHERE clause after the element list:
abap
define view entity ...
as select from ...
{
...
}
where <condition>Rows that do not satisfy the condition are not part of that entity's result.
CDS conditions support comparisons such as:
text
=
<>
<
>
<=
>=and Boolean composition with:
text
NOT
AND
OR17. Create a second CDS layer
Create another Data Definition:
text
ZI_AVAILABLE_FLIGHT_###Description:
text
Lesson 3 - Available FlightsThis time the source is your first CDS entity:
text
ZI_FLIGHT_###The dependency becomes:
text
/DMO/FLIGHT
↓
ZI_FLIGHT_###
↓
ZI_AVAILABLE_FLIGHT_###A CDS entity can be a data source for another CDS entity.
18. Create the filtered entity
Use:
abap
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Available Flights'
define view entity ZI_Available_Flight_###
as select from ZI_Flight_### as Flight
{
key Flight.CarrierId,
key Flight.ConnectionId,
key Flight.FlightDate,
Flight.PlaneTypeId,
Flight.SeatsMax,
Flight.SeatsOccupied,
Flight.SeatsAvailable,
Flight.RecordType,
Flight.AvailabilityStatus
}
where Flight.AvailabilityStatus = abap.char'Available'Replace both suffixes.
19. Why filter the calculated status in a second layer?
AvailabilityStatus is calculated in:
text
ZI_FLIGHT_###Once that entity becomes a source, AvailabilityStatus behaves like a normal source element.
So:
text
ZI_FLIGHT_###
calculates AvailabilityStatus
↓
ZI_AVAILABLE_FLIGHT_###
filters AvailabilityStatusThis gives a reusable pattern:
text
Base model
↓
Specialized model20. Activate and preview the filtered entity
Run:
text
Ctrl + F2Activate:
text
Ctrl + F3Preview:
text
F8Every row should have:
text
AvailabilityStatus = AvailableCompare:
text
ZI_FLIGHT_### → all modeled flights
ZI_AVAILABLE_FLIGHT_### → only available flights21. Query both CDS entities in SQL Console
First:
abap
SELECT FROM zi_flight_###
FIELDS CarrierId,
ConnectionId,
FlightDate,
SeatsAvailable,
AvailabilityStatus
ORDER BY CarrierId,
ConnectionId,
FlightDateThen:
abap
SELECT FROM zi_available_flight_###
FIELDS CarrierId,
ConnectionId,
FlightDate,
SeatsAvailable,
AvailabilityStatus
ORDER BY CarrierId,
ConnectionId,
FlightDateCompare the result sets.
22. CDS filtering vs ABAP SQL filtering
Consumer-level filter
abap
SELECT FROM zi_flight_###
FIELDS ...
WHERE AvailabilityStatus = 'Available'The consumer chooses the filter.
Model-level filter
text
ZI_AVAILABLE_FLIGHT_###already means:
text
available flightsEvery consumer starts from the same specialized model.
TIP
Use a model-level filter when the restriction is part of what the entity means.
Use a consumer-level filter when the restriction belongs to a particular query/use case.
23. Create a Flight Reader
Create:
text
ZCL_FLIGHT_READER_###This Reader will consume the CDS models.
An activated CDS View Entity is also available in ABAP as a global structured type.
That means we can define an internal-table type directly from the CDS entity.
24. Define the Reader types
Use:
abap
CLASS zcl_flight_reader_### DEFINITION
PUBLIC
FINAL
CREATE PUBLIC.
PUBLIC SECTION.
TYPES tt_flights TYPE STANDARD TABLE OF zi_flight_###
WITH EMPTY KEY.
TYPES tt_available_flights
TYPE STANDARD TABLE OF zi_available_flight_###
WITH EMPTY KEY.
ENDCLASS.This demonstrates a useful property of CDS entities: the CDS model can also be used as an ABAP type.
25. Add Reader methods
Add:
abap
METHODS get_all
RETURNING VALUE(flights) TYPE tt_flights.
METHODS get_available
RETURNING VALUE(flights) TYPE tt_available_flights.Complete definition:
abap
CLASS zcl_flight_reader_### DEFINITION
PUBLIC
FINAL
CREATE PUBLIC.
PUBLIC SECTION.
TYPES tt_flights TYPE STANDARD TABLE OF zi_flight_###
WITH EMPTY KEY.
TYPES tt_available_flights
TYPE STANDARD TABLE OF zi_available_flight_###
WITH EMPTY KEY.
METHODS get_all
RETURNING VALUE(flights) TYPE tt_flights.
METHODS get_available
RETURNING VALUE(flights) TYPE tt_available_flights.
ENDCLASS.26. Implement GET_ALL
abap
CLASS zcl_flight_reader_### IMPLEMENTATION.
METHOD get_all.
SELECT FROM zi_flight_###
FIELDS *
ORDER BY CarrierId,
ConnectionId,
FlightDate
INTO TABLE @flights.
ENDMETHOD.
ENDCLASS.FIELDS * in ABAP SQL vs CDS
In a CDS View Entity element list, * cannot be used to expose every source element.
In ABAP SQL, FIELDS * is valid.
Here it matches the method contract because the Reader deliberately returns the complete CDS entity type.
27. Implement GET_AVAILABLE
Add:
abap
METHOD get_available.
SELECT FROM zi_available_flight_###
FIELDS *
ORDER BY CarrierId,
ConnectionId,
FlightDate
INTO TABLE @flights.
ENDMETHOD.Activate the Reader.
28. Create the Runner
Create:
text
ZCL_CDS_L03_###Use:
abap
CLASS zcl_cds_l03_### DEFINITION
PUBLIC
FINAL
CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_oo_adt_classrun.
ENDCLASS.
CLASS zcl_cds_l03_### IMPLEMENTATION.
METHOD if_oo_adt_classrun~main.
DATA(reader) = NEW zcl_flight_reader_###( ).
DATA(all_flights) = reader->get_all( ).
out->write( 'All flights' ).
out->write( all_flights ).
DATA(available_flights) = reader->get_available( ).
out->write( 'Available flights' ).
out->write( available_flights ).
ENDMETHOD.
ENDCLASS.Activate and run with:
text
F929. Observe where the logic lives
The Runner does not calculate:
text
SeatsMax - SeatsOccupiedThe Reader does not calculate it either.
The CDS model calculates it.
The Runner also does not determine:
text
Full / AvailableThe CDS model does that.
The specialized CDS model defines:
text
available flights onlyResponsibilities:
text
Persistence
stores source data
CDS
models and derives reusable data
Reader
retrieves model data
Runner
executes and presents the example30. What logic belongs in CDS?
Good candidates include logic that describes reusable data:
text
derived fields
data classification
consistent field naming
model-level restrictions
relationships
semantic metadataNot every business rule belongs in CDS.
Later, RAP behavior will handle transactional/business-object behavior such as:
text
validations
determinations
actions
transactional changes31. Common mistakes
Calculating SeatsAvailable again in the Runner
Once it is part of the CDS model, consumers should use the CDS element.
Confusing CASE with WHERE
text
CASE → returns a value
WHERE → removes rowsForgetting that the filtered CDS has a CDS source
Its source is:
text
ZI_FLIGHT_###not directly:
text
/DMO/FLIGHTUsing * in a CDS View Entity element list
Do not write:
abap
{
*
}in a CDS View Entity.
Assuming a CDS expression changes stored data
SeatsAvailable is a model element, not a new /DMO/FLIGHT column.
Forgetting typed literal syntax
abap
abap.char'Available'Pattern:
text
abap.<dictionary type>'value'32. Knowledge check
1. Does SeatsAvailable exist in /DMO/FLIGHT?
Show answer
No. It is calculated by ZI_FLIGHT_###.
2. What does a CASE expression do?
Show answer
It evaluates conditions and returns a result value. In this lesson it creates AvailabilityStatus.
3. What does a CDS WHERE clause do?
Show answer
It removes rows from that CDS entity's result when they do not satisfy the condition.
4. What is a typed literal?
Show answer
A literal whose ABAP Dictionary type is stated explicitly, for example abap.char'Available'.
5. Can one CDS View Entity use another CDS View Entity as a data source?
Show answer
Yes. ZI_AVAILABLE_FLIGHT_### selects from ZI_FLIGHT_###.
6. Why create a specialized filtered CDS entity?
Show answer
When the restriction is part of what the entity represents, a specialized entity gives all consumers the same reusable filtered model.
7. Can an activated CDS View Entity be used as an ABAP type?
Show answer
Yes. A CDS View Entity is available to ABAP as a global structured type.
8. Why can the Reader use FIELDS * here?
Show answer
The method deliberately returns the complete CDS entity type, and FIELDS * is valid in ABAP SQL. This differs from the CDS View Entity element list, where * cannot expose all source elements.
33. Lesson summary
You extended CDS from simple field exposure to active data modeling.
You used:
text
arithmetic expression
CASE expression
typed literal
constant element
CDS WHERE
CDS-on-CDS layering
Data Preview
SQL Console
CDS entity as ABAP type
Reader consumptionThe resulting architecture is:
text
/DMO/FLIGHT
↓
ZI_FLIGHT_###
├─ SeatsAvailable
└─ AvailabilityStatus
↓
ZI_AVAILABLE_FLIGHT_###
└─ model-level filter
↓
Reader
↓
RunnerThe central idea is:
CDS can derive and specialize reusable data before application code consumes it.
34. What comes next?
The next major modeling step is combining related data.
For example:
text
Connection
needs Carrier
needs Origin Airport
needs Destination AirportThat leads naturally to:
text
joinsand then:
text
associationsBefore moving there, the practice assignment will make you apply expressions, typed literals, functions, and filtering to /DMO/CONNECTION.
After completing the practice, continue to Lesson 4 — CDS Joins to combine related carrier, connection, and airport data in reusable CDS models.