Skip to Content

In this post, I will discuss the architecture of building a virtual data model (VDM) in S/4 HANA, using CDS Views (Core Data Services).

With the availability of the SAP HANA platform there has been a paradigm shift in the way business applications are developed at SAP. The rule-of-thumb is: Do as much as you can in the database to get the best performance.

CDS Views

To take advantage of SAP HANA for application development, SAP introduced a new data modeling infrastructure known as core data services. With CDS, data models are defined and consumed on the database rather than on the application server. CDS also offers capabilities beyond the traditional data modeling tools, including support for conceptual modeling and relationship definitions, built-in functions, and extensions


Technically, CDS is an enhancement of SQL which provides a Data Definition Language (DDL) for defining semantically rich database tables/views (CDS entities) and user-defined types in the database. Some of the enhancements are:

  • Expressions used for calculations and queries in the data model
  • Associations on a conceptual level, replacing joins with simple path expressions in queries
  • Annotations to enrich the data models with additional (domain specific) metadata


Supported natively in both ABAP and SAP HANA, the data models are expressed in data definition language (DDL) and are defined as CDS views, which can be used in ABAP programs via Open SQL statements to enable access to the database. CDS provides a range of advantages for businesses and developers, including:

  • Semantically rich data models
    CDS builds on the well-known entity relationship model and is declarative in nature, very close to conceptual thinking.
  • Compatibility across any database platform
    CDS is generated into managed Open SQL views and is natively integrated into the SAP HANA layer. These views based on Open SQL are supported by all major database vendors
  • Efficiency
    CDS offers a variety of highly efficient built-in functions — such as SQL operators, aggregations, and expressions — for creating views.
  • Support for annotations
    The CDS syntax supports domain-specific annotations that can be easily evaluated by other components, such as the UI, analytics, and OData services.
  • Support for conceptual associations
    CDS helps you define associations that serve as relationships between different views. Path expressions can be used to navigate along relations. Introducing an abstraction of foreign key relationships and joins, associations make navigation between entities consumable
  • Extensibility.
    Customers can extend SAP-defined CDS views with fields that will be automatically added to the CDS view along with its usage hierarchy.


CDS Views for Embedded Analytics

Before HANA, querying large datasets in an ERP system could be time consuming, and degrade overall performance. Data Warehouses were used to create persisted data models using advanced modelling techniques to improve query performance. SAP HANA removes the performance issue in ERP out of the equation, allowing us to create Virtual Data Models (VDM) directly in ERP with incredible performance.

What is a VDM? A combination of semantically enriched CDS views that logically combine data from source ERP tables to create meaningful datasets that can be readily consumed in frontend tools


As the slide suggests, at a high level, the VDM consists of CDS Views reading data from tables in the DB, which are then read by other CDS views, without any persistency, happening in real time.


Annotations help qualify the CDS Views, and provides semantics and meaning to fields within a CDS View

  • They can be applied to the entire CDS View entity;
  • They can be used to specify semantics to fields in the SELECT list;
  • Are always preceded by the @ symbol.

Below is SAP’s list of annotations:

It can also be reached by following the path: ABAP – Keyword Documentation > ABAP – Dictionary > ABAP CDS in ABAP Dictionary > ABAP CDS – Syntax > ABAP CDS – Annotations

Below are a few of the key annotations that define entire CDS Views specific to VDMs:

Annotation Description
VDM.viewType Defines the type of a VDM view
Analytics.dataCategory Analytic queries can be defined on top of CDS views.  By specifying the data category, the developer can provide directives and hints, telling the analytic manager how to interpret individual entities for example
Analytics.dataExtraction.enabled Application developers can use this annotation to mark views that are suitable for data replication (for example, delta capabilities must be provided for mass data)
Analytics.query By tagging the CDS view, the developer can specify which views will be exposed to the analytic manager. This type of view will be interpreted as an analytic query by the analytic manager.
ObjectModel.dataCategory Defines the category of data (#TEXT or #HIERARCHY)
ObjectModel.representativekey Most specific element (field or managed association) of the primary key (indicated by the keyword KEY) that represents the entity which the view is based on
AccessControl.authorizationCheck Enables row level authorization on a specific CDS view


Because I’m also a BW developer, I’ve taken some of the most important annotations for VDMs and compared them to BW objects:

Annotation: @VDM.viewType

Value Description BW Equivalent
#BASIC Views that form the core data basis without data redundancies. This is your basic SELECT from a physical table in the database This would be equivalent to an ADSO, where your raw data is present, with some ETL
#COMPOSITE Views that provide data derived and/or composed from the BASIC views. This would be equivalent to a Composite Provider, which is the virtual layer which allows for joins and unions
#CONSUMPTION Views that serve for specific application purposes and may be defined based upon public interface (for example, BASIC and COMPOSITE) views. This would be equivalent to a BW Query, where we specify a particular layout, variables, RKFs and CKFs, totals, etc.

Annotation: @Analytics.dataCategory

Value Description BW Equivalent
#DIMENSION Views representing master data should be annotated with ObjectModel.dataCategory: #DIMENSION Equivalent to Infoobject Attributes
#FACT This value indicates that the entity represents transactional data (center of star schema). Usually it contains the measures. Typically, these views are necessary for replication, therefore, they should not be joined with master data views. Equivalent to an ADSO loading from a single datasource, without any master data links
#CUBE The #CUBE value (like #FACT) also represents factual data, but #CUBE does not have to be without redundancy. This means joins with master data are possible. Queries are usually built on top of #CUBE, where data is replicated from facts Equivalent to an ADSO loading from a single/multiple datasources, with master data linkage to attributes/texts/hiererachies

Annotation: @Analytics.dataExtraction.enabled

Value Description BW Equivalent
#TRUE Application developers can use this annotation to mark views that are suitable for data replication (for example, delta capabilities must be provided for mass data). N/A
#FALSE This view is not suitable for data replication N/A

Annotation: @Analytics.query

Value Description BW Equivalent

By tagging the CDS view, the developer can specify which views will be exposed to the analytic manager. This type of view will be interpreted as an analytic query by the analytic manager.

This will display the consumption view in the frontend tools such as BOBJ, when searching for it in the application folder

BW or BEx Query. This view will be enabled to be run in RSRT in the backend
#FALSE The query view will not be exposed to the analytic manager. N/A

Annotation: @ObjectModel.dataCategory

Value Description BW Equivalent

Indicates that the annotated entity represents texts. Usually one key element is of type language.

NOTE: Within the VDM a text view is always language-dependent.

Equivalent to infoobject Texts/descriptions
#HIERARCHY Indicates that the entity represents the hierarchy-related data. This could be a header information or structure information. Equivalent to infoobject Hierarchy

Annotation: @ObjectModel.representativekey

  • Most specific element (field or managed association) of the primary key (indicated by the keyword KEY) that represents the entity which the view is based on. This element shall be used as the anchor for defining foreign key relationships (except for text views): The foreign key field corresponding to the representative key represents the entity. As such it can be called representative foreign key element. The foreign key association is defined on the representative foreign key element. The name of the representative key typically equals the name of the entity represented by the view.
  • For non-text views it is the key element for which the view serves as a value list/check table. For text views (@ObjectModel.dataCategory: #TEXT) it identifies the key element to which the text fields relate to.
  • The representative key element has to be modelled explicitly even if there is only one primary key field (no implicit derivation).
  • A view may only become a target of a foreign key association if it has a representative key element (exception: language dependent text views may not be used as targets of foreign key relationships

Annotation: @AccessControl.authorizationCheck

Value Description BW Equivalent
#NOT_REQUIRED A Data Control Language (row level security object for CDS Views) does NOT exist for the CDS View therefore no security is enforced for the CDS View, and all data is displayed N/A
#CHECK A Data Control Language (row level security object for CDS Views) EXISTS for the CDS View and row level security is to be enforced for the CDS View Somewhat equivalent to BW authorizations
#NOT_ALLOWED A Data Control Language (row level security object for CDS Views) exists for the CDS View and row level security is NOT to be enforced for the CDS View N/A


Now that we’ve seen the main CDS View annotations, we’ll look at a more detailed architecture of the VDM:

Notice how the concepts are similar to that of a BW environment. We have texts and dimension CDS Views. We can build these once and re-utilize them across any number of transactional CDS views.

Think of this as building a material dimension and text views. You only need to build this once, as the base tables won’t change (MARA and MAKT). But for every transactional model built (Sales, Deliveries, COPA, Inventory, etc.) where you require material description or attributes, you can re-utilize that dimension and text view.

Below are more details regarding each of the CDS Views mentioned above:



@AbapCatalog.sqlViewName: 'ZBTMATERIAL'
@ObjectModel.dataCategory: #TEXT
@Analytics: { dataCategory: #TEXT, dataExtraction.enabled: true }
@AccessControl.authorizationCheck: #NOT_REQUIRED
@VDM.viewType: #BASIC
@EndUserText.label: 'Material Text'
@ObjectModel.representativeKey: 'Material'

define view Zbt_Material as 
  select from makt 
          @ObjectModel.text.element:  [ 'MaterialName' ] 
          key makt.matnr as Material, 
          @Semantics.language: true
          key makt.spras as Language,
          @Semantics.text: true 
              makt.maktx as MaterialName}

where makt.spras = $session.system_language




@AbapCatalog.sqlViewName: 'ZBDMATERIAL'
@Analytics: { dataCategory: #DIMENSION, dataExtraction.enabled: true }
@VDM.viewType: #BASIC
@AbapCatalog.compiler.compareFilter: true
@EndUserText.label: 'Material Attributes'
@ObjectModel.representativeKey: 'Material'
@AccessControl.authorizationCheck: #NOT_REQUIRED

define view Zbd_Material as select from mara 

association [0..1] to Zbt_Material           as _Text           on $projection.Material      = _Text.Material
association [0..1] to Zbd_MaterialType       as _MaterialType   on $projection.MaterialType  = _MaterialType.MaterialType
association [0..1] to Zbd_MaterialGroup      as _MaterialGroup  on $projection.MaterialGroup = _MaterialGroup.MaterialGroup
association [0..1] to I_UnitOfMeasure        as _BaseUnit       on $projection.MaterialBaseUnit = _BaseUnit.UnitOfMeasure
association [0..1] to I_UnitOfMeasure        as _WeightUnit     on $projection.MaterialWeightUnit = _WeightUnit.UnitOfMeasure
association [0..1] to Zbt_Storage_Conditions as _StorCond       on $projection.StorageCondition = _StorCond.StorageCond

     { @EndUserText.label: 'Material'
       @ObjectModel.text.association: '_Text'
          key mara.matnr as Material, _Text,
       @ObjectModel.foreignKey.association: '_MaterialType'
       @EndUserText.label: 'Material Type'  
              mara.mtart as MaterialType, _MaterialType,
       @ObjectModel.foreignKey.association: '_MaterialGroup'
       @EndUserText.label: 'Material Group'        
              mara.matkl as MaterialGroup, _MaterialGroup,
       @EndUserText.label: 'Base Unit of Measure'  
       @Semantics.unitOfMeasure: true
       @ObjectModel.foreignKey.association: '_BaseUnit'
              mara.meins as MaterialBaseUnit, _BaseUnit,                
       @EndUserText.label: 'Gross Weight'  
       @Semantics.quantity.unitOfMeasure: 'MaterialWeightUnit'              
       @DefaultAggregation: #NONE
              mara.brgew as MaterialGrossWeight,
       @EndUserText.label: 'Net Weight'  
       @Semantics.quantity.unitOfMeasure: 'MaterialWeightUnit'
       @DefaultAggregation: #NONE
                     mara.ntgew as MaterialNetWeight,              
       @EndUserText.label: 'Weight Unit'
       @Semantics.unitOfMeasure: true
       @ObjectModel.foreignKey.association: '_WeightUnit'
              mara.gewei as MaterialWeightUnit, _WeightUnit,
              mara.mfrnr as MaterialManufacturerNumber,
              mara.mfrpn as MaterialManufacturerPartNumber,
       @EndUserText.label: 'Storage Condition'
       @ObjectModel.text.association: '_StorCond'              
              mara.raube as StorageCondition, _StorCond,
       @EndUserText.label: 'Product Hierarchy'       
              mara.prdha as ProductHierarchy


Notice how we haven’t applied any semantics (Annotations) to the fields in the select list, since this is the BASIC view, and we’re creating the FACT of our model. Semantic annotations will be applied at the composite view.

Also note that a virtual data model can be comprised of multiple BASIC/FACT views depending on requirements.

@AbapCatalog.sqlViewName: 'ZBFACDOCAXX'
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Universal Journal Entry, Basic'
@VDM.viewType: #BASIC
@Analytics.dataCategory: #FACT 
@Analytics.dataExtraction.enabled: true
define view ZBF_ACDOCA_XX as select from acdoca 

       rbukrs as CompCode,
       gjahr  as FiscalYear,
       poper  as Period,
       racct  as GLAccount,
       matnr  as Material,
       werks  as Plant,
// UOMs - Currencies
       runit  as UOM,
       rhcur  as CCCurr,
// Measures
       msl    as Quantity,
       hsl    as AmtCC              




@AbapCatalog.sqlViewName: 'ZCCACDOCAXX'
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Universal Journal Entry, Composite'
@Analytics.dataCategory: #CUBE
@Analytics.dataExtraction.enabled: true

define view ZCC_ACDOCA_XX as select from ZBF_ACDOCA_XX 

association [0..1] to I_Material    as _Mat      on $projection.Material = _Mat.Material
association [0..1] to I_Plant       as _Plant    on $projection.Plant    = _Plant.Plant
association [0..1] to I_CompanyCode as _CompCode on $projection.CompCode = _CompCode.CompanyCode

      @ObjectModel.foreignKey.association: '_CompCode'
      CompCode, _CompCode,
      @ObjectModel.foreignKey.association: '_Mat'
      Material, _Mat,
      @ObjectModel.foreignKey.association: '_Plant'
      Plant, _Plant,
      //UOMs - Currencies
      @Semantics.unitOfMeasure: true
      @EndUserText.label: 'Base UOM'
      @Semantics.currencyCode: true
      @EndUserText.label: 'Comp. Code Curr.'
      @DefaultAggregation: #SUM
      @EndUserText.label: 'Quantity'
      @Semantics.quantity.unitOfMeasure: 'UOM'
      @DefaultAggregation: #SUM
      @EndUserText.label: 'Amount CC'
      @Semantics.amount.currencyCode: 'CCCurr'



@AbapCatalog.sqlViewName: 'ZCCACDOCAXXQ001'
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Universal Journal Entry, Query'
@Analytics.query: true 
define view ZCC_ACDOCA_XX_Q001 as select from ZCC_ACDOCA_XX 

    @Consumption.filter: {mandatory: false, selectionType: #SINGLE, multipleSelections: true}
    @AnalyticsDetails.query.display: #KEY_TEXT
    @AnalyticsDetails.query.axis: #ROWS  
//UOMs - Currencies  



One of the main advantages of the VDM is that you build it once, and it can be consumed in a multitude of front end tools:

  • BOBJ
    • WEBI
    • Analysis for Office
    • Crystal
    • OLAP
  • Lumira Discovery (even though it’s being discontinued in favor of Analytics Cloud)
  • Analytics Cloud
  • ALV Grid
  • ODATA services
  • Fiori

In addition to that, because the CDS View exists on the application layer, we can leverage the existing ABAP security model through PFCG. For CDS View security we use an artifact called the Data Control Language (DCL). For more information on DCL, please refer to my blog below:

Once you learn how to model using CDS Views, the time to develop a VDM can be very fast, translating to delivering quick and efficient reporting solutions to customers.


Regarding performance, the VDM is amazing for high volume high aggregation scenarios. I built a model at a client on Purchase Orders, going from the header down to the schedule line, with many master data joins. The schedule line table had about 250 Million records, across 8 years of data at the time.

When running a summarized report with no filters, with 3 measures on the columns and just the year on the drill down (8 rows, 3 columns, 24 data points), the report returned in 1-2 seconds!!!

Now, using the same data model above, when I ran a very detailed report, going down to the schedule line item, bringing many different attributes, with about 30 rows in the drilldown, for an entire month, that report took 10 minutes to run. Why? Because it was doing all the joins and calculations I had defined in the model in real time, and was very process intensive.

The scenario above would be a great candidate for a persisted solution in BW, where the ETL has taken care of any calculations and data joins. The data is persisted in an ADSO and when a report is run, it is simply selecting the data based on the criteria, without the need of any additional processing, with the expectation of performance in the sub second region.


So with this I hope I was able to provide information on the Virtual Data Model using CDS Views. I hope I was able to provide context regarding performance expectations, and that this is a great solution for real time operational reporting, but each reporting scenario must be addressed individually to assess the suitability of the solution.

To report this post you need to login first.


You must be Logged on to comment or reply to a post.

Leave a Reply