Skip to content

CDS associations

An association is the VDM’s answer to the question: how does this view reach data in a related view without hardcoding a join? Instead of writing a SQL JOIN clause, you declare an association with a name, a target view, a cardinality, and an ON condition. The association then behaves like a field you can traverse with dot notation — and crucially, nothing in the database happens until a downstream query actually reads across it.

CDS associations I_SalesOrderItem @VDM.viewType: #COMPOSITE 5 associations declared, 0 eagerly joined I_Product _Product I_Customer _SoldToParty I_Plant _Plant I_SalesOrganization _SalesOrg I_Currency _Currency _Product._ProductGroup.ProductGroupText Dot-paths traverse associations lazily — HANA only joins what the query reads.
Associations — Atlas walks these paths when it expands the plan's dependency graph.

That lazy-evaluation property is the whole reason the VDM scales.

define view I_SalesOrderItem as select from vbap {
key vbeln as SalesOrder,
key posnr as SalesOrderItem,
matnr as Product,
werks as Plant,
_Product,
_Plant,
_SoldToParty
}
association [1..1] to I_Product as _Product on $projection.Product = _Product.Product
association [1..1] to I_Plant as _Plant on $projection.Plant = _Plant.Plant

A downstream view that consumes I_SalesOrderItem can now write something like _Product._ProductGroup.ProductGroupText and get a translated description across three levels of joins. The engine only materializes the joins the consuming query actually reads. If the consumer never references _Plant, that association never touches the database, even though it was declared.

A realistic analytical query — say, purchase-order schedule lines broken down by product group, valuation, controlling area, and fiscal year — can traverse eight or ten associations. Each traversal is a conceptual join, and in a naive engine each would be a round trip. In the VDM, HANA collapses the entire traversal into one vectorized column-store scan. The query returns in milliseconds, and the plan Atlas wrote is small because Atlas only had to list the nodes the query actually walks — not the full transitive set that would exist if every association were eager.

This is why Atlas’s plan graphs do not balloon in the presence of deeply-linked domains. Atlas lists the associations the query reads, and the ones it does not read stay invisible to everyone involved.

Declared vs materialized DECLARED · 5 associations I_SalesOrderItem 5 associations declared _Product _Customer _SalesOrg _Plant _Currency all five are written; none have touched the database MATERIALIZED · 2 actually read I_SalesOrderItem reads only 2 via dot-path _Product _Customer _SalesOrg _Plant _Currency HANA joins only the two that are actually read
Declared vs materialized — associations are cheap to write because they are free when unused.

Associations are convenient. They are also the single easiest place to introduce an invisible dependency. Writing _ProductGroup in a downstream query implicitly commits you to whatever the ProductGroup view underneath is — its fields, its joins, its stability tier. If that view happens to be a P_ (private) view, the consumer now has a P_ dependency it never mentioned.

Atlas walks the association tree when it plans and flags dot-paths that terminate on a view the plan has no business depending on. That flag usually shows up as a gate firing on generate, not on validate, because Atlas prefers to catch it while the code can still be changed cheaply.

Any time a plan is doing something that does not obviously make sense. Open the Studio’s plan canvas, select the consumption node, and expand the Associations panel in the inspector — Atlas renders the full association tree the plan is committing to. Most of the time the tree is exactly what you expected. When it is not, the discrepancy is usually a sign that the task description was reaching farther than intended.