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
| Feature | Klassische Views (SE11) | CDS Views |
|---|---|---|
| Syntax | ABAP Dictionary | SQL-ähnlich (DDL) |
| Performance | Gut | Besser (DB-optimiert) |
| Annotations | Keine | Umfangreich |
| Associations | Nein | Ja |
| Analytics | Begrenzt | Umfangreich |
| Wiederverwendbarkeit | Gering | Hoch |
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üfungdefine view: View-Definitionas 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 ladenSELECT 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_countryVerwendung:
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.dats5. 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: #CHECKdefine 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 kna13. Unions ohne Notwendigkeit:
/* ❌ Langsam wenn vermeidbar */define view Z_Combined as select from tab1 union all select from tab211. 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
| Feature | Syntax | Use Case |
|---|---|---|
| Basic View | define view ... as select from | Einfache Datenabfrage |
| Join | inner/left join ... on | Daten verknüpfen |
| Association | association [0..*] to | Lazy Loading, Performance |
| Parameters | with parameters p_x : type | Dynamische Filter |
| Virtual Elements | @ObjectModel.virtualElement | Berechnete Felder (ABAP) |
| Access Control | @AccessControl + DCL | Berechtigungen |
| Hierarchies | @Hierarchy.parentChild | Org-Strukturen |
| Analytics | @Analytics.dataCategory: #CUBE | Reporting |
📚 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! 🚀