Appearance
Lesson 3 Companion
This companion expands Lesson 3 — CDS Expressions, Calculated Elements, and Filtering.
It contains deeper explanations, complete reference implementations, practice solutions, troubleshooting guidance, and current SAP references.
How to use this companion
Use the main lesson to build the artifacts first. Use this companion to review syntax, compare implementations, or investigate why the data model is structured in a particular way.
Related Lesson 3 files:
- Lesson 3 — CDS Expressions, Calculated Elements, and Filtering
- Lesson 3 Practice — Connection Modeling
1. Why Lesson 3 uses /DMO/FLIGHT
/DMO/FLIGHT provides a simple reusable calculation:
text
SeatsAvailable = seats_max - seats_occupiedThis demonstrates arithmetic CDS expressions without requiring joins, associations, aggregates, currency conversions, or transactional behavior.
SAP's ABAP Flight Reference Scenario documents /DMO/FLIGHT with fields including:
text
carrier_id
connection_id
flight_date
price
currency_code
plane_type_id
seats_max
seats_occupiedThe key fields are:
text
carrier_id
connection_id
flight_date2. Current CDS View Entity expression scope
Current SAP documentation lists expression families for CDS View Entities including:
text
arithmetic expressions
aggregate expressions
CASE expressions
CAST expressions
built-in functionsView Entities also support clauses such as WHERE, GROUP BY, and HAVING, plus relationship modeling with joins and associations.
Lesson 3 uses a focused subset.
3. Arithmetic calculated elements
The lesson uses:
abap
Flight.seats_max - Flight.seats_occupied as SeatsAvailableThis is evaluated per result row.
The expression does not alter persistence.
text
Persisted:
seats_max
seats_occupied
Calculated CDS element:
SeatsAvailable4. Why not occupancy percentage yet?
A tempting calculation is:
text
SeatsOccupied / SeatsMax * 100The lesson avoids it because division introduces:
text
result-type decisions
division-by-zero handling
integer vs decimal behavior
rounding
CAST / DIVISION considerationsThose are useful topics, but they would distract from the first expression lesson.
5. CASE expression
The lesson uses a searched CASE:
abap
case
when Flight.seats_occupied = Flight.seats_max
then abap.char'Full'
else abap.char'Available'
end as AvailabilityStatusCurrent CDS View Entity syntax supports both simple and searched CASE forms.
The important distinction is:
text
CASE → creates a value
WHERE → restricts rows6. Typed literals
Current SAP keyword documentation recommends typed literals where applicable.
Syntax:
text
abap.dtype'value'Examples:
abap
abap.char'Available'
abap.int4'100'
abap.tims'120000'
abap.dats'20261231'Typed literals make the intended Dictionary type explicit and improve compatibility checking.
7. Typed time literals
The practice assignment uses:
abap
abap.tims'120000'
abap.tims'180000'abap.tims uses the HHMMSS representation.
Examples:
text
120000 = 12:00:00
180000 = 18:00:00Do not write:
text
12:00
12:00:00inside an abap.tims literal.
8. Model layering
The lesson creates:
text
/DMO/FLIGHT
↓
ZI_FLIGHT_###
↓
ZI_AVAILABLE_FLIGHT_###The base entity calculates:
text
SeatsAvailable
AvailabilityStatusThe specialized entity reuses those elements and filters:
text
AvailabilityStatus = AvailableThis avoids repeating the CASE expression.
9. Why a second entity instead of repeating the expression?
Avoid:
text
Base CDS calculates status
Specialized CDS repeats CASE
Reader repeats CASEPrefer:
text
Base CDS calculates once
↓
Specialized CDS reuses
↓
Reader consumesThat is a core benefit of a reusable data model.
10. Model-level vs consumer-level filtering
Consumer filter:
abap
SELECT FROM zi_flight_###
...
WHERE AvailabilityStatus = 'Available'This restriction belongs to one request.
Specialized model:
text
ZI_AVAILABLE_FLIGHT_###This entity itself represents available flights.
Choose the layer based on the meaning of the requirement.
11. CDS condition capabilities
Current CDS conditions support comparison operators including:
text
=
<>
<
>
<=
>=and Boolean composition with:
text
NOT
AND
OROther supported condition forms include BETWEEN, LIKE, IS NULL, and IS INITIAL in appropriate CDS conditions.
12. SQL evaluation-order caution
SAP documents that SQL does not guarantee short-circuit evaluation order for conditions combined with AND.
Therefore, do not rely on logic such as:
text
x <> 0 AND y / x > 1assuming the second expression can never be evaluated when x = 0.
For potentially unsafe expressions, a CASE expression can be a safer design.
This detail is background knowledge rather than a required Lesson 3 exercise.
13. CDS entities as ABAP types
An activated CDS View Entity is available in ABAP as a global structured type.
Therefore:
abap
TYPES tt_flights TYPE STANDARD TABLE OF zi_flight_###
WITH EMPTY KEY.is valid conceptually for a Reader that deliberately exposes the CDS model as its contract.
This differs from the earlier Carrier Reader, which preserved a manually defined Reader structure.
Both designs are useful:
text
Independent Reader contract
or
CDS model as Reader contractLesson 3 demonstrates the second.
14. Why FIELDS * is acceptable here
The method declares that it returns the complete CDS entity type.
Therefore:
abap
SELECT FROM zi_flight_###
FIELDS *matches the method contract.
This is different from casually using SELECT * against a large persistence table.
15. * in CDS vs ABAP SQL
CDS View Entity element list:
abap
{
*
}is not supported for exposing all source elements.
ABAP SQL:
abap
FIELDS *is valid.
Do not treat CDS DDL and ABAP SQL syntax as identical.
16. Base Flight CDS reference
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
}17. Filtered Flight CDS reference
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'18. Flight Reader reference
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.
CLASS zcl_flight_reader_### IMPLEMENTATION.
METHOD get_all.
SELECT FROM zi_flight_###
FIELDS *
ORDER BY CarrierId,
ConnectionId,
FlightDate
INTO TABLE @flights.
ENDMETHOD.
METHOD get_available.
SELECT FROM zi_available_flight_###
FIELDS *
ORDER BY CarrierId,
ConnectionId,
FlightDate
INTO TABLE @flights.
ENDMETHOD.
ENDCLASS.19. Runner reference
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 ).
DATA(available_flights) = reader->get_available( ).
out->write( available_flights ).
ENDMETHOD.
ENDCLASS.20. Practice source: /DMO/CONNECTION
SAP documents /DMO/CONNECTION with:
text
carrier_id key
connection_id key
airport_from_id
airport_to_id
departure_time
arrival_time
distance
distance_unitThe practice uses this source for:
text
CONCAT_WITH_SPACE
CASE
typed time literals
filtered CDS layeringwithout introducing joins.
21. Practice base entity reference
abap
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Connection'
define view entity ZI_Connection_###
as select from /dmo/connection as Connection
{
key Connection.carrier_id as CarrierId,
key Connection.connection_id as ConnectionId,
Connection.airport_from_id as AirportFromId,
Connection.airport_to_id as AirportToId,
Connection.departure_time as DepartureTime,
Connection.arrival_time as ArrivalTime,
Connection.distance as Distance,
Connection.distance_unit as DistanceUnit,
concat_with_space(
Connection.airport_from_id,
Connection.airport_to_id,
1
) as RouteCode,
case
when Connection.departure_time < abap.tims'120000'
then abap.char'Morning'
when Connection.departure_time < abap.tims'180000'
then abap.char'Afternoon'
else abap.char'Evening'
end as DeparturePeriod
}22. Why CONCAT_WITH_SPACE
Current View Entity function scope includes string functions such as:
text
CONCAT
CONCAT_WITH_SPACE
LENGTH
LOWER
UPPER
LEFT
RIGHTThe practice uses:
abap
concat_with_space(
Connection.airport_from_id,
Connection.airport_to_id,
1
)to create a simple route representation such as:
text
FRA JFK23. Practice filtered entity reference
abap
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Morning Connections'
define view entity ZI_Morning_Connection_###
as select from ZI_Connection_### as Connection
{
key Connection.CarrierId,
key Connection.ConnectionId,
Connection.AirportFromId,
Connection.AirportToId,
Connection.DepartureTime,
Connection.ArrivalTime,
Connection.Distance,
Connection.DistanceUnit,
Connection.RouteCode,
Connection.DeparturePeriod
}
where Connection.DeparturePeriod = abap.char'Morning'24. Practice Reader reference
abap
CLASS zcl_connection_reader_### DEFINITION
PUBLIC
FINAL
CREATE PUBLIC.
PUBLIC SECTION.
TYPES tt_connections
TYPE STANDARD TABLE OF zi_connection_###
WITH EMPTY KEY.
TYPES tt_morning_connections
TYPE STANDARD TABLE OF zi_morning_connection_###
WITH EMPTY KEY.
METHODS get_all
RETURNING VALUE(connections) TYPE tt_connections.
METHODS get_morning
RETURNING VALUE(connections) TYPE tt_morning_connections.
ENDCLASS.
CLASS zcl_connection_reader_### IMPLEMENTATION.
METHOD get_all.
SELECT FROM zi_connection_###
FIELDS *
ORDER BY CarrierId,
ConnectionId
INTO TABLE @connections.
ENDMETHOD.
METHOD get_morning.
SELECT FROM zi_morning_connection_###
FIELDS *
ORDER BY CarrierId,
ConnectionId
INTO TABLE @connections.
ENDMETHOD.
ENDCLASS.25. Practice Runner reference
abap
CLASS zcl_cds_l03_practice_### DEFINITION
PUBLIC
FINAL
CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_oo_adt_classrun.
ENDCLASS.
CLASS zcl_cds_l03_practice_### IMPLEMENTATION.
METHOD if_oo_adt_classrun~main.
DATA(reader) = NEW zcl_connection_reader_###( ).
DATA(connections) = reader->get_all( ).
LOOP AT connections INTO DATA(connection).
out->write(
|{ connection-RouteCode } - { connection-DeparturePeriod }|
).
ENDLOOP.
DATA(morning_connections) = reader->get_morning( ).
out->write( morning_connections ).
ENDMETHOD.
ENDCLASS.26. Troubleshooting
SeatsAvailable is unknown in the filtered entity
Confirm ZI_FLIGHT_### is active and exposes SeatsAvailable.
AvailabilityStatus comparison fails
Confirm the spelling and the typed literal:
abap
abap.char'Available'abap.tims is rejected
Check the format:
abap
abap.tims'120000'CONCAT_WITH_SPACE cannot resolve fields
Use source-qualified fields:
abap
Connection.airport_from_id
Connection.airport_to_idFIELDS * causes confusion
Remember:
text
CDS element list → no *
ABAP SQL → FIELDS * is valid27. Suggested pacing
| Section | Approx. time |
|---|---|
Inspect /DMO/FLIGHT | 10 min |
| Arithmetic calculated element | 15 min |
CASE expression | 15 min |
| Typed literals | 10 min |
| Data Preview + SQL Console | 15 min |
| Filtered CDS layer | 20 min |
| CDS entity as ABAP type | 10 min |
| Reader + Runner | 20 min |
| Review | 5 min |