Appearance
Lesson 5 Companion — CDS Associations & Path Expressions
This companion expands Lesson 5 — CDS Associations & Path Expressions.
It includes:
- precise association terminology;
- cardinality guidance;
$projectionexplanation;- path-expression behavior;
- exposed-association behavior;
- Data Preview navigation;
- complete Route reference code;
- complete Booking practice reference code;
- a to-many challenge reference;
- troubleshooting;
- current SAP documentation links.
How to use this companion
Use it after attempting the main lesson and practice, or whenever you want to compare your model with a complete reference implementation.
Related Lesson 5 files:
1. Why associations follow joins
Current SAP CDS supports both:
text
join expressions
associationsAssociations are reusable relationship definitions. When a path target field is used, CDS can instantiate the relationship as a database join.
This makes the course sequence intentional:
text
Lesson 4
explicit joins
↓
understand ON, join type, and row multiplication
Lesson 5
associations
↓
name and reuse those relationshipsAssociations are easier to reason about once learners already understand the relational mechanics underneath.
2. Associations are directional
A normal association is defined in its source entity and points to a target.
Example:
text
Connection ──► Carrierdoes not automatically create:
text
Carrier ──► ConnectionIf reverse navigation is required, another relationship must be modeled from the reverse source.
This prepares students for later RAP relationships where both directions can be modeled explicitly.
3. Why this lesson targets CDS entities
The guided lesson associates to:
text
/DMO/I_Carrier
/DMO/I_Airportrather than directly to:
text
/DMO/CARRIER
/DMO/AIRPORTThe intention is to reinforce:
text
consumer model
↓
reusable CDS model
↓
persistenceThis is closer to current SAP RAP examples, where reference/master data is typically consumed through CDS entities.
4. Verified target fields
Current SAP Flight Reference Scenario documentation defines:
/DMO/I_Carrier
text
AirlineID key
Name
CurrencyCode/DMO/I_Airport
text
AirportID key
Name
City
CountryCode/DMO/I_Customer
text
CustomerID key
FirstName
LastName
City
CountryCode
.../DMO/I_Connection
text
AirlineID key
ConnectionID key
DepartureAirport
DestinationAirport
DepartureTime
ArrivalTime
Distance
DistanceUnitThese names are the target-side element names used in the lesson and practice.
5. Numeric cardinality in this course
The lesson uses numeric cardinality notation:
text
[0..1]
[1..1]
[0..*]
[1..*]Current SAP RAP documentation continues to use these forms extensively.
Current ABAP releases also support newer source/target cardinality syntax, for example forms such as:
text
association of exact one to many ...The numeric notation remains clear and compatible for the course's current level.
6. Cardinality is a model contract
Cardinality should reflect the real relationship.
Examples:
text
Booking → Customer
to one
Connection → Carrier
to one
Carrier → Connections
to manyDo not choose cardinality based on what is easiest to type.
Ask:
text
Can no target exist?
Can several targets exist?
Does the ON condition identify the complete target key?7. To-one relationships and complete target keys
SAP guidance emphasizes that a real to-one relationship should normally map the complete target key.
Example target:
text
/DMO/I_Connection
key AirlineID
key ConnectionIDA Booking association must therefore use:
abap
on $projection.CarrierId = _Connection.AirlineID
and $projection.ConnectionId = _Connection.ConnectionIDUsing only:
text
ConnectionIDdoes not identify the full target record.
This is the Lesson 4 complete-key rule expressed in association form.
8. $projection
Suppose the source field is:
abap
Booking.customer_id as CustomerIdAn association can refer to the current CDS element:
abap
$projection.CustomerIdFor example:
abap
association [1..1] to /DMO/I_Customer as _Customer
on $projection.CustomerId = _Customer.CustomerIDThis lets the association use the current model's field name rather than the persistence source name.
9. Source fields needed by exposed associations
If an association is exposed publicly, the source fields used in its ON condition must remain available in the exposing entity.
For example:
text
_Carrierdepends on:
text
CarrierIdTherefore CarrierId needs to remain part of the current entity's element list.
10. Path target fields
This:
abap
_Carrier.Name as CarrierNamemeans:
text
navigate _Carrier
read Name from the target
expose it as CarrierNameCarrierName is then an ordinary scalar element for consumers.
The relationship logic remains in CDS.
11. Default join semantics of a CDS path target field
Current SAP documentation states that when a path target field is used in the element list of a CDS entity, a left outer join is generated by default.
Therefore:
abap
_Carrier.Namehas default left-outer semantics even if Lesson 4 explicitly used:
abap
inner joinThis distinction is useful.
12. Cardinality vs join type
These concepts are related but not identical.
text
cardinality
=
what relationship is expected
join type
=
how rows are combined for a particular path useSo:
text
[1..1]does not mean:
text
the path must be an INNER JOINThe default path-target access is still left outer.
13. Path join attributes
Current CDS path syntax can override the default join type.
Example:
abap
_Carrier[inner].NameThis explicitly requests inner-join behavior for that path usage.
The main lesson does not use it because the goal is to teach association modeling first.
14. Exposing an association
Compare:
abap
_Carrier.Name as CarrierNamewith:
abap
_CarrierThe first exposes:
text
a scalar valueThe second exposes:
text
the navigation relationshipThis distinction matters for:
text
CDS consumers
projection layers
service navigation
RAP models15. Data Preview navigation
Current ADT provides Follow Association in CDS Data Preview.
The intended workflow is:
text
open Data Preview
↓
Follow Association
↓
choose _Carrier / _OriginAirport / ...
↓
inspect target result
↓
navigate back with breadcrumbThis is one of the strongest practical demonstrations of why an exposed association is different from a copied scalar field.
16. Complete Route association reference
abap
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Route with Associations'
define view entity ZI_Route_Assoc_###
as select from /dmo/connection as Connection
association [1..1] to /DMO/I_Carrier as _Carrier
on $projection.CarrierId = _Carrier.AirlineID
association [1..1] to /DMO/I_Airport as _OriginAirport
on $projection.OriginAirportId = _OriginAirport.AirportID
association [1..1] to /DMO/I_Airport as _DestinationAirport
on $projection.DestinationAirportId = _DestinationAirport.AirportID
{
key Connection.carrier_id as CarrierId,
key Connection.connection_id as ConnectionId,
_Carrier.Name as CarrierName,
Connection.airport_from_id as OriginAirportId,
_OriginAirport.Name as OriginAirportName,
_OriginAirport.City as OriginCity,
_OriginAirport.CountryCode as OriginCountryCode,
Connection.airport_to_id as DestinationAirportId,
_DestinationAirport.Name as DestinationAirportName,
_DestinationAirport.City as DestinationCity,
_DestinationAirport.CountryCode as DestinationCountryCode,
Connection.departure_time as DepartureTime,
Connection.arrival_time as ArrivalTime,
Connection.distance as Distance,
Connection.distance_unit as DistanceUnit,
_Carrier,
_OriginAirport,
_DestinationAirport
}17. Why the source is still /DMO/CONNECTION
The lesson is focused on association mechanics.
Using:
text
/DMO/CONNECTIONas the main source keeps the new material concentrated on:
text
association declaration
cardinality
$projection
path expressions
exposure
navigationA later course refactor could introduce a dedicated Connection interface entity first, but it is not required here.
18. Route Reader reference
abap
CLASS zcl_route_assoc_reader_### DEFINITION
PUBLIC
FINAL
CREATE PUBLIC.
PUBLIC SECTION.
TYPES tt_routes
TYPE STANDARD TABLE OF zi_route_assoc_###
WITH EMPTY KEY.
METHODS get_all
RETURNING VALUE(routes) TYPE tt_routes.
METHODS get_from_country
IMPORTING
country_code TYPE /dmo/airport-country
RETURNING
VALUE(routes) TYPE tt_routes.
ENDCLASS.
CLASS zcl_route_assoc_reader_### IMPLEMENTATION.
METHOD get_all.
SELECT FROM zi_route_assoc_###
FIELDS *
ORDER BY CarrierId,
ConnectionId
INTO TABLE @routes.
ENDMETHOD.
METHOD get_from_country.
SELECT FROM zi_route_assoc_###
FIELDS *
WHERE OriginCountryCode = @country_code
ORDER BY CarrierId,
ConnectionId
INTO TABLE @routes.
ENDMETHOD.
ENDCLASS.19. Route Runner reference
abap
CLASS zcl_cds_l05_### DEFINITION
PUBLIC
FINAL
CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_oo_adt_classrun.
ENDCLASS.
CLASS zcl_cds_l05_### IMPLEMENTATION.
METHOD if_oo_adt_classrun~main.
DATA(reader) = NEW zcl_route_assoc_reader_###( ).
DATA(routes) = reader->get_all( ).
LOOP AT routes INTO DATA(route).
out->write(
|{ route-CarrierName }: | &&
|{ route-OriginCity } -> { route-DestinationCity }|
).
ENDLOOP.
ENDMETHOD.
ENDCLASS.20. Practice architecture
The practice uses:
text
/DMO/BOOKING
↓
ZI_BOOKING_ASSOC_###
├── _Customer ───► /DMO/I_Customer
├── _Carrier ────► /DMO/I_Carrier
└── _Connection ─► /DMO/I_ConnectionThis is an excellent association exercise because _Connection requires a composite target key.
21. Practice association reference
abap
association [1..1] to /DMO/I_Customer as _Customer
on $projection.CustomerId = _Customer.CustomerID
association [1..1] to /DMO/I_Carrier as _Carrier
on $projection.CarrierId = _Carrier.AirlineID
association [1..1] to /DMO/I_Connection as _Connection
on $projection.CarrierId = _Connection.AirlineID
and $projection.ConnectionId = _Connection.ConnectionID22. Practice complete CDS reference
abap
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Booking with Associations'
define view entity ZI_Booking_Assoc_###
as select from /dmo/booking as Booking
association [1..1] to /DMO/I_Customer as _Customer
on $projection.CustomerId = _Customer.CustomerID
association [1..1] to /DMO/I_Carrier as _Carrier
on $projection.CarrierId = _Carrier.AirlineID
association [1..1] to /DMO/I_Connection as _Connection
on $projection.CarrierId = _Connection.AirlineID
and $projection.ConnectionId = _Connection.ConnectionID
{
key Booking.travel_id as TravelId,
key Booking.booking_id as BookingId,
Booking.booking_date as BookingDate,
Booking.customer_id as CustomerId,
_Customer.FirstName as CustomerFirstName,
_Customer.LastName as CustomerLastName,
Booking.carrier_id as CarrierId,
_Carrier.Name as CarrierName,
Booking.connection_id as ConnectionId,
_Connection.DepartureAirport as DepartureAirportId,
_Connection.DestinationAirport as DestinationAirportId,
_Connection.DepartureTime as DepartureTime,
_Connection.ArrivalTime as ArrivalTime,
Booking.flight_date as FlightDate,
Booking.flight_price as FlightPrice,
Booking.currency_code as CurrencyCode,
_Customer,
_Carrier,
_Connection
}23. Why the practice keeps first/last name separate
Lesson 4 already practiced:
text
CONCAT_WITH_SPACELesson 5 should concentrate on association navigation.
The practice therefore exposes:
text
CustomerFirstName
CustomerLastNamethrough _Customer.
Students can optionally derive a display name, but it is not required to demonstrate the new concept.
24. Practice Reader reference
abap
CLASS zcl_booking_assoc_reader_### DEFINITION
PUBLIC
FINAL
CREATE PUBLIC.
PUBLIC SECTION.
TYPES tt_bookings
TYPE STANDARD TABLE OF zi_booking_assoc_###
WITH EMPTY KEY.
METHODS get_all
RETURNING VALUE(bookings) TYPE tt_bookings.
METHODS get_by_carrier
IMPORTING
carrier_id TYPE /dmo/carrier-carrier_id
RETURNING
VALUE(bookings) TYPE tt_bookings.
ENDCLASS.
CLASS zcl_booking_assoc_reader_### IMPLEMENTATION.
METHOD get_all.
SELECT FROM zi_booking_assoc_###
FIELDS *
ORDER BY TravelId,
BookingId
INTO TABLE @bookings.
ENDMETHOD.
METHOD get_by_carrier.
SELECT FROM zi_booking_assoc_###
FIELDS *
WHERE CarrierId = @carrier_id
ORDER BY TravelId,
BookingId
INTO TABLE @bookings.
ENDMETHOD.
ENDCLASS.25. Practice Runner reference
abap
CLASS zcl_cds_l05_practice_### DEFINITION
PUBLIC
FINAL
CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_oo_adt_classrun.
ENDCLASS.
CLASS zcl_cds_l05_practice_### IMPLEMENTATION.
METHOD if_oo_adt_classrun~main.
DATA(reader) = NEW zcl_booking_assoc_reader_###( ).
DATA(bookings) = reader->get_all( ).
LOOP AT bookings INTO DATA(booking).
out->write(
|{ booking-TravelId }/{ booking-BookingId }: | &&
|{ booking-CustomerFirstName } { booking-CustomerLastName } - | &&
|{ booking-CarrierName }|
).
ENDLOOP.
ENDMETHOD.
ENDCLASS.26. To-many challenge reference
One carrier can have many connections.
Reference:
abap
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Carrier Connections'
define view entity ZI_Carrier_Connections_###
as select from /DMO/I_Carrier as Carrier
association [0..*] to /DMO/I_Connection as _Connections
on $projection.CarrierId = _Connections.AirlineID
{
key Carrier.AirlineID as CarrierId,
Carrier.Name as CarrierName,
_Connections
}This is intentionally a navigation model.
Students should use Data Preview's Follow Association rather than flattening all Connection fields into the Carrier row.
27. Why [0..*] for the challenge
The reverse direction is:
text
Carrier → ConnectionsA carrier can have:
text
zero connections
one connection
many connectionsTherefore:
text
[0..*]is appropriate for the general relationship.
This contrasts cleanly with:
text
Booking → Carrier [1..1]28. To-many row behavior
A to-many association can lead to more than one target row for one source.
If a model flattens a target field from a to-many path, row multiplication can occur.
That is the same relational effect students saw with joins in Lesson 4.
Associations do not remove the need to understand cardinality.
29. ABAP SQL path expressions
ABAP SQL can navigate exposed CDS associations as well.
Its syntax differs from CDS DDL path syntax; ABAP SQL path navigation in data-source positions uses backslash association notation.
The main lesson does not teach full ABAP SQL association-path syntax yet.
That is deliberate:
text
Lesson 5 goal
=
master CDS association modeling and navigation
not
=
master every possible consumer syntaxStudents already have SQL Console and Reader access to the exposed scalar model.
30. Filtered path expressions
Current CDS supports path filters such as:
text
_Assoc[condition].Fieldand attributes that can affect path cardinality and join behavior.
These are valuable for text models and restricted to-many relationships.
They are deferred until a real use case requires them rather than being added as syntax inventory.
31. Troubleshooting
Target object not found
Search with:
text
Ctrl + Shift + Afor:
text
/DMO/I_CARRIER
/DMO/I_AIRPORT
/DMO/I_CUSTOMER
/DMO/I_CONNECTIONConfirm the Flight Reference Scenario content is available in the training tenant.
AirlineID unknown
When target is:
text
/DMO/I_CARRIERuse its CDS key:
text
AirlineIDnot the persistence field:
text
carrier_idCountry unknown on Airport
The target CDS field is:
text
CountryCodeUse:
abap
_OriginAirport.CountryCode$projection.CarrierId unknown
Make sure the current entity exposes:
abap
... as CarrierIdwith exactly the same spelling.
Follow Association does not list _Carrier
Make sure:
abap
_Carrieritself is in the element list and the entity has been activated.
A scalar path field alone is not the same as exposed navigation.
_Connection practice relationship behaves incorrectly
Check the full target key:
text
AirlineID
ConnectionIDBoth must participate in the ON condition.
32. Self-review checklist
A strong Lesson 5 implementation demonstrates:
- target CDS entities understood before association coding;
- meaningful association names;
- correct cardinality;
- complete target-key conditions;
- deliberate use of
$projection; - correct path target fields;
- exposed associations;
- successful Follow Association navigation;
- correct distinction between scalar field and relationship;
- no duplicated relationship logic in Reader/Runner;
- readiness for semantic/text associations in Lesson 6.