Skip to content

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 Table

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

You 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
     ↓
Runner

Lesson information ​

DurationAbout 120 minutes
EnvironmentSAP BTP ABAP Trial
Development toolEclipse with ABAP Development Tools (ADT)
PrerequisitesLessons 1 and 2
Persistence source/DMO/FLIGHT
Base CDS entityZI_FLIGHT_###
Filtered CDS entityZI_AVAILABLE_FLIGHT_###
ReaderZCL_FLIGHT_READER_###
RunnerZCL_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 CASE expression 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 WHERE clause;
  • 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_017

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

The key is the combination of:

text
carrier_id
connection_id
flight_date

Step 1.1 — Open the table ​

Press:

text
Ctrl + Shift + A

Search for:

text
/DMO/FLIGHT

Open the database table.

Step 1.2 — Identify the fields ​

Find:

text
carrier_id
connection_id
flight_date
plane_type_id
seats_max
seats_occupied

Confirm the three key fields.

Step 1.3 — Open Data Preview ​

Open Data Preview for /DMO/FLIGHT.

Look for records where:

text
seats_max > seats_occupied

and, if present, records where:

text
seats_max = seats_occupied

The relationship we will model is:

text
Seats available = Maximum seats - Occupied seats

2. From exposed fields to calculated elements ​

In Lesson 2, a CDS element usually came directly from the source:

abap
Flight.seats_max as SeatsMax

A CDS element can also be produced by an expression:

abap
Flight.seats_max - Flight.seats_occupied as SeatsAvailable

SeatsAvailable does not exist as a column in /DMO/FLIGHT.

It is calculated by the CDS entity.

text
/DMO/FLIGHT
  SeatsMax
  SeatsOccupied
       ↓
     CDS
       ↓
SeatsAvailable

Main 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 Definition

Choose Next.

Step 3.2 — Enter the details ​

Use:

text
Name:        ZI_FLIGHT_###
Description: Lesson 3 - Flight CDS

If ADT provides Referenced Object, enter:

text
/DMO/FLIGHT

Choose the correct transport request if required.

Select a CDS View Entity template.

Confirm the generated source uses:

abap
define view entity

Finish 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 + F2

Activate:

text
Ctrl + F3

Preview:

text
F8

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

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

The result now includes:

text
SeatsAvailable

Pick a row and verify:

text
SeatsMax - SeatsOccupied = SeatsAvailable

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

With CDS:

text
/DMO/FLIGHT
      ↓
ZI_FLIGHT_###
      ↓
SeatsAvailable
      ↓
Consumers

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

This lesson focuses on:

text
arithmetic
CASE
typed literals
WHERE

The practice assignment also uses a string function.


9. Add a CASE expression ​

A number such as:

text
SeatsAvailable = 0

is useful.

A descriptive classification can also be useful:

text
Full
Available

Add:

abap
case
  when Flight.seats_occupied = Flight.seats_max
    then abap.char'Full'
  else abap.char'Available'
end as AvailabilityStatus

The 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 + F2

then:

text
Ctrl + F3

and preview again.


10. How CASE works ​

The form used here is a searched CASE expression:

abap
case
  when <condition>
    then <result>
  else <result>
end

Our condition is:

abap
Flight.seats_occupied = Flight.seats_max

When true:

text
AvailabilityStatus = Full

Otherwise:

text
AvailabilityStatus = Available

Remember:

text
CASE   → calculates/classifies a value
WHERE  → removes rows that do not meet a condition

11. 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 RecordType

Place it before AvailabilityStatus.

This demonstrates that CDS elements can come from:

text
source fields
calculations
CASE expressions
literals
functions

13. 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,
           FlightDate

The 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,
           FlightDate

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

17. Create a second CDS layer ​

Create another Data Definition:

text
ZI_AVAILABLE_FLIGHT_###

Description:

text
Lesson 3 - Available Flights

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

This gives a reusable pattern:

text
Base model
   ↓
Specialized model

20. Activate and preview the filtered entity ​

Run:

text
Ctrl + F2

Activate:

text
Ctrl + F3

Preview:

text
F8

Every row should have:

text
AvailabilityStatus = Available

Compare:

text
ZI_FLIGHT_###             → all modeled flights
ZI_AVAILABLE_FLIGHT_###   → only available flights

21. Query both CDS entities in SQL Console ​

First:

abap
SELECT FROM zi_flight_###
  FIELDS CarrierId,
         ConnectionId,
         FlightDate,
         SeatsAvailable,
         AvailabilityStatus
  ORDER BY CarrierId,
           ConnectionId,
           FlightDate

Then:

abap
SELECT FROM zi_available_flight_###
  FIELDS CarrierId,
         ConnectionId,
         FlightDate,
         SeatsAvailable,
         AvailabilityStatus
  ORDER BY CarrierId,
           ConnectionId,
           FlightDate

Compare 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 flights

Every 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
F9

29. Observe where the logic lives ​

The Runner does not calculate:

text
SeatsMax - SeatsOccupied

The Reader does not calculate it either.

The CDS model calculates it.

The Runner also does not determine:

text
Full / Available

The CDS model does that.

The specialized CDS model defines:

text
available flights only

Responsibilities:

text
Persistence
  stores source data

CDS
  models and derives reusable data

Reader
  retrieves model data

Runner
  executes and presents the example

30. 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 metadata

Not every business rule belongs in CDS.

Later, RAP behavior will handle transactional/business-object behavior such as:

text
validations
determinations
actions
transactional changes

31. 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 rows

Forgetting that the filtered CDS has a CDS source ​

Its source is:

text
ZI_FLIGHT_###

not directly:

text
/DMO/FLIGHT

Using * 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 consumption

The resulting architecture is:

text
/DMO/FLIGHT
      ↓
ZI_FLIGHT_###
  ├─ SeatsAvailable
  └─ AvailabilityStatus
      ↓
ZI_AVAILABLE_FLIGHT_###
  └─ model-level filter
      ↓
Reader
      ↓
Runner

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

That leads naturally to:

text
joins

and then:

text
associations

Before 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.