Skip to content

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:

  1. Lesson 3 — CDS Expressions, Calculated Elements, and Filtering
  2. Lesson 3 Practice — Connection Modeling

1. Why Lesson 3 uses /DMO/FLIGHT ​

/DMO/FLIGHT provides a simple reusable calculation:

text
SeatsAvailable = seats_max - seats_occupied

This 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_occupied

The key fields are:

text
carrier_id
connection_id
flight_date

2. 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 functions

View 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 SeatsAvailable

This is evaluated per result row.

The expression does not alter persistence.

text
Persisted:
  seats_max
  seats_occupied

Calculated CDS element:
  SeatsAvailable

4. Why not occupancy percentage yet? ​

A tempting calculation is:

text
SeatsOccupied / SeatsMax * 100

The lesson avoids it because division introduces:

text
result-type decisions
division-by-zero handling
integer vs decimal behavior
rounding
CAST / DIVISION considerations

Those 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 AvailabilityStatus

Current CDS View Entity syntax supports both simple and searched CASE forms.

The important distinction is:

text
CASE  → creates a value
WHERE → restricts rows

6. 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:00

Do not write:

text
12:00
12:00:00

inside an abap.tims literal.


8. Model layering ​

The lesson creates:

text
/DMO/FLIGHT
      ↓
ZI_FLIGHT_###
      ↓
ZI_AVAILABLE_FLIGHT_###

The base entity calculates:

text
SeatsAvailable
AvailabilityStatus

The specialized entity reuses those elements and filters:

text
AvailabilityStatus = Available

This 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 CASE

Prefer:

text
Base CDS calculates once
      ↓
Specialized CDS reuses
      ↓
Reader consumes

That 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
OR

Other 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 > 1

assuming 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 contract

Lesson 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_unit

The practice uses this source for:

text
CONCAT_WITH_SPACE
CASE
typed time literals
filtered CDS layering

without 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
RIGHT

The 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 JFK

23. 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_id

FIELDS * causes confusion ​

Remember:

text
CDS element list → no *
ABAP SQL         → FIELDS * is valid

27. Suggested pacing ​

SectionApprox. time
Inspect /DMO/FLIGHT10 min
Arithmetic calculated element15 min
CASE expression15 min
Typed literals10 min
Data Preview + SQL Console15 min
Filtered CDS layer20 min
CDS entity as ABAP type10 min
Reader + Runner20 min
Review5 min

28. Current SAP references ​