CDS Views Deep Dive: Von Basic bis Analytics (2025)

kategorie
Deep Dive
Veröffentlicht
autor
Johannes

CDS Views (Core Data Services) sind das Herzstück moderner ABAP-Entwicklung. Dieser Deep Dive zeigt dir alles von Basics bis Advanced Analytics.

🎯 Was sind CDS Views?

CDS Views sind semantische Datenmodelle definiert in SQL-ähnlicher Syntax, die direkt auf der Datenbank laufen.

Vorteile vs. klassische Views

FeatureKlassische Views (SE11)CDS Views
SyntaxABAP DictionarySQL-ähnlich (DDL)
PerformanceGutBesser (DB-optimiert)
AnnotationsKeineUmfangreich
AssociationsNeinJa
AnalyticsBegrenztUmfangreich
WiederverwendbarkeitGeringHoch

1. CDS Basics

Einfachste CDS View

@AbapCatalog.sqlViewName: 'ZV_CUSTOMER'
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Customer View'
define view Z_Customer
as select from kna1
{
key kunnr as Customer,
name1 as CustomerName,
land1 as Country,
ort01 as City
}

Komponenten:

  • @AbapCatalog.sqlViewName: Name des generierten DB-Views (max. 16 Zeichen)
  • @AccessControl: Berechtigungsprüfung
  • define view: View-Definition
  • as select from: Datenquelle
  • { ... }: Feldliste

View aktivieren

  • In ADT: Ctrl+F3
  • Generiert automatisch DB-View ZV_CUSTOMER

View nutzen

SELECT Customer, CustomerName, Country
FROM Z_Customer
WHERE Country = 'DE'
INTO TABLE @DATA(lt_customers).

2. Joins & Associations

Inner Join

define view Z_SalesOrderWithCustomer
as select from vbak as SalesOrder
inner join kna1 as Customer
on SalesOrder.kunnr = Customer.kunnr
{
key SalesOrder.vbeln as SalesOrder,
SalesOrder.audat as OrderDate,
Customer.name1 as CustomerName,
Customer.land1 as Country
}

Left Outer Join

define view Z_CustomerWithOrders
as select from kna1 as Customer
left outer join vbak as SalesOrder
on Customer.kunnr = SalesOrder.kunnr
{
key Customer.kunnr as Customer,
Customer.name1 as CustomerName,
SalesOrder.vbeln as SalesOrder,
SalesOrder.netwr as OrderValue
}

Associations (Empfohlen!)

Besser als Joins: Lazy Loading, wiederverwendbar

define view Z_Customer
as select from kna1
association [0..*] to vbak as _SalesOrders
on $projection.Customer = _SalesOrders.kunnr
{
key kunnr as Customer,
name1 as CustomerName,
land1 as Country,
/* Association exposen (ohne Join!) */
_SalesOrders
}

Verwendung:

" Nur Customer-Daten laden
SELECT Customer, CustomerName
FROM Z_Customer
INTO TABLE @DATA(lt_customers).
" Mit Orders (nur wenn gebraucht!)
SELECT Customer, CustomerName,
\_SalesOrders-vbeln as OrderNumber
FROM Z_Customer
WHERE Country = 'DE'
INTO TABLE @DATA(lt_customer_orders).

Vorteil: Performance! Orders nur geladen wenn benötigt.


3. Wichtige Annotations

@Semantics

Gibt Feldern semantische Bedeutung:

define view Z_Product
as select from mara
{
key matnr as Product,
/* Währung */
@Semantics.amount.currencyCode: 'Currency'
price as Price,
@Semantics.currencyCode: true
waers as Currency,
/* Einheit */
@Semantics.quantity.unitOfMeasure: 'Unit'
menge as Quantity,
@Semantics.unitOfMeasure: true
meins as Unit,
/* Datum */
@Semantics.businessDate.from: true
valid_from as ValidFrom,
@Semantics.businessDate.to: true
valid_to as ValidTo,
/* User */
@Semantics.user.createdBy: true
created_by as CreatedBy
}

@ObjectModel

Für RAP & UI:

@ObjectModel.representativeKey: 'Product'
@ObjectModel.usageType.serviceQuality: #A
@ObjectModel.usageType.sizeCategory: #M
@ObjectModel.usageType.dataClass: #MASTER
define view Z_Product as select from mara
{
key matnr as Product,
/* Text-Element */
@ObjectModel.text.element: ['ProductName']
matnr as ProductId,
maktx as ProductName
}

@Analytics

Für analytische Queries:

@Analytics.query: true
@Analytics.dataCategory: #CUBE
define view Z_SalesAnalytics
as select from vbak
{
/* Dimensionen */
@Analytics.dimension: true
kunnr as Customer,
@Analytics.dimension: true
vkorg as SalesOrg,
/* Measures (Kennzahlen) */
@Aggregation.default: #SUM
@Semantics.amount.currencyCode: 'Currency'
netwr as Revenue,
@Aggregation.default: #COUNT
vbeln as OrderCount,
waerk as Currency
}

4. Parameters

Input-Parameter für dynamische Views:

define view Z_CustomerByCountry
with parameters
p_country : land1
as select from kna1
{
key kunnr as Customer,
name1 as CustomerName,
land1 as Country
}
where
land1 = :p_country

Verwendung:

SELECT Customer, CustomerName
FROM Z_CustomerByCountry( p_country = 'DE' )
INTO TABLE @DATA(lt_customers).

Mit Defaultwert:

with parameters
@Environment.systemField: #SYSTEM_DATE
p_date : abap.dats

5. Virtual Elements

Berechnete Felder (nicht in DB):

define view Z_Product
as select from mara
{
key matnr as Product,
/* Virtuelle Felder */
@ObjectModel.virtualElement: true
@ObjectModel.virtualElementCalculatedBy: 'ABAP:ZCL_CALC_STOCK'
cast( 0 as abap.int4 ) as AvailableStock,
@ObjectModel.virtualElement: true
@ObjectModel.virtualElementCalculatedBy: 'ABAP:ZCL_CALC_PRICE'
cast( 0.0 as abap.curr(16,2) ) as CurrentPrice
}

Implementierung:

CLASS zcl_calc_stock DEFINITION PUBLIC.
PUBLIC SECTION.
INTERFACES if_sadl_exit_calc_element_read.
ENDCLASS.
CLASS zcl_calc_stock IMPLEMENTATION.
METHOD if_sadl_exit_calc_element_read~calculate.
" Daten lesen
DATA lt_products TYPE STANDARD TABLE OF z_product.
lt_products = CORRESPONDING #( it_original_data ).
" Bestandsabfrage
LOOP AT lt_products ASSIGNING FIELD-SYMBOL(<product>).
SELECT SINGLE labst FROM mard
WHERE matnr = <product>-product
INTO @<product>-availablestock.
ENDLOOP.
" Zurückgeben
ct_calculated_data = CORRESPONDING #( lt_products ).
ENDMETHOD.
ENDCLASS.

6. Access Control (DCL)

Daten-Berechtigungen auf View-Ebene:

CDS View:

@AccessControl.authorizationCheck: #CHECK
define view Z_SalesOrder
as select from vbak
{
key vbeln as SalesOrder,
kunnr as Customer,
vkorg as SalesOrg,
netwr as NetValue
}

DCL (Z_SalesOrder.dcls):

@EndUserText.label: 'Access Control for Sales Orders'
@MappingRole: true
define role Z_SalesOrder {
grant select on Z_SalesOrder
where
/* Nur eigene Sales Org */
(SalesOrg) = aspect pfcg_auth(
V_VBAK_VKO,
VKORG,
ACTVT = '03'
);
}

Ergebnis: User sehen nur SalesOrders ihrer SalesOrg!


7. Hierarchies

Für Parent-Child Strukturen:

@Hierarchy.parentChild: [{ name: 'OrgHierarchy',
recurse: {
parent: 'ParentOrg',
child: 'Organization',
depth: 'Level',
orphans: #ERROR
}
}]
define view Z_OrganizationHierarchy
as select from t001
{
key bukrs as Organization,
parent_bukrs as ParentOrg,
butxt as OrganizationName,
/* Wird automatisch berechnet */
cast( 0 as abap.int1 ) as Level
}

8. Extending CDS Views

Andere Views als Basis nutzen:

/* Basis View */
define view Z_Customer_Basic
as select from kna1
{
key kunnr as Customer,
name1 as CustomerName,
land1 as Country
}
/* Erweitert Basis View */
define view Z_Customer_Extended
as select from Z_Customer_Basic
association [0..*] to vbak as _Orders
on $projection.Customer = _Orders.kunnr
{
Customer,
CustomerName,
Country,
/* Zusätzliche Felder */
_Orders.vbeln as OrderNumber,
_Orders.netwr as OrderValue,
_Orders
}

9. CDS Views für Analytics

Cube View

@Analytics.dataCategory: #CUBE
define view Z_SalesCube
as select from vbak
{
@Analytics.dimension: true
vkorg as SalesOrg,
@Analytics.dimension: true
vtweg as DistributionChannel,
@Analytics.dimension: true
audat as OrderDate,
@Aggregation.default: #SUM
netwr as Revenue,
@Aggregation.default: #AVG
netwr as AvgOrderValue,
@Aggregation.default: #COUNT
vbeln as OrderCount
}

Query View (konsumiert Cube)

@Analytics.query: true
define view Z_SalesQuery
as select from Z_SalesCube
{
@AnalyticsDetails.query.axis: #ROWS
SalesOrg,
@AnalyticsDetails.query.axis: #ROWS
DistributionChannel,
@AnalyticsDetails.query.axis: #COLUMNS
OrderDate,
Revenue,
AvgOrderValue,
OrderCount
}

10. Performance Best Practices

✅ DO

1. Pushdown statt ABAP-Logic:

/* ✅ Gut: In CDS berechnen */
define view Z_Customer
as select from kna1
{
key kunnr,
/* Berechnung in DB */
case land1
when 'DE' then 'Deutschland'
when 'AT' then 'Österreich'
else 'Anderes Land'
end as CountryText
}

2. Indizes nutzen:

/* Wo möglich auf Key-Felder filtern */
where vbeln = :p_sales_order /* ✅ Key, sehr schnell */

3. Buffering aktivieren:

@AbapCatalog.buffering.status: #ACTIVE
@AbapCatalog.buffering.type: #FULL
@AbapCatalog.buffering.numberOfKeyFields: 1
define view Z_Country
as select from t005
{
key land1 as Country,
landx as CountryName
}

❌ DON’T

1. Zu viele Joins:

/* ❌ Schlecht: 10 Joins */
define view Z_ComplexView
as select from t1
join t2 on ...
join t3 on ...
... /* 10 weitere */

Besser: Mehrere Views mit Associations

2. SELECT DISTINCT ohne Grund:

/* ❌ Langsam */
define view Z_Customer
as select distinct from kna1

3. Unions ohne Notwendigkeit:

/* ❌ Langsam wenn vermeidbar */
define view Z_Combined
as select from tab1
union all
select from tab2

11. Testing CDS Views

CLASS ltc_cds_test DEFINITION FOR TESTING
DURATION SHORT
RISK LEVEL HARMLESS.
PRIVATE SECTION.
CLASS-DATA environment TYPE REF TO if_cds_test_environment.
CLASS-METHODS class_setup.
CLASS-METHODS class_teardown.
METHODS test_customer_view FOR TESTING.
ENDCLASS.
CLASS ltc_cds_test IMPLEMENTATION.
METHOD class_setup.
environment = cl_cds_test_environment=>create(
i_for_entity = 'Z_CUSTOMER'
).
ENDMETHOD.
METHOD class_teardown.
environment->destroy( ).
ENDMETHOD.
METHOD test_customer_view.
" Testdaten einfügen
environment->insert_test_data(
i_data = VALUE #(
( customer = '0001' customername = 'Test AG' country = 'DE' )
)
).
" CDS View abfragen
SELECT customer, customername
FROM z_customer
WHERE country = 'DE'
INTO TABLE @DATA(lt_result).
" Assertions
cl_abap_unit_assert=>assert_equals(
act = lines( lt_result )
exp = 1
).
cl_abap_unit_assert=>assert_equals(
act = lt_result[ 1 ]-customername
exp = 'Test AG'
).
ENDMETHOD.
ENDCLASS.

🎯 Zusammenfassung: CDS View Cheat Sheet

FeatureSyntaxUse Case
Basic Viewdefine view ... as select fromEinfache Datenabfrage
Joininner/left join ... onDaten verknüpfen
Associationassociation [0..*] toLazy Loading, Performance
Parameterswith parameters p_x : typeDynamische Filter
Virtual Elements@ObjectModel.virtualElementBerechnete Felder (ABAP)
Access Control@AccessControl + DCLBerechtigungen
Hierarchies@Hierarchy.parentChildOrg-Strukturen
Analytics@Analytics.dataCategory: #CUBEReporting

📚 Weiterführende Ressourcen

Auf abapcloud.com:

SAP Dokumentation:

Best Practices:

  • Nutze Associations statt Joins wo möglich
  • Pushdown Logik in DB (nicht ABAP)
  • Buffering für Master-Daten
  • Access Control für Berechtigungen
  • Test deine CDS Views!

Viel Erfolg mit CDS Views! 🚀