Custom Analytical Queries ermöglichen dir, eigene analytische Auswertungen direkt in ABAP Cloud zu erstellen. Mit CDS Views und speziellen Annotations baust du Cubes, Queries und Dashboards für Business Intelligence Szenarien.
Was sind Analytical CDS Views?
Analytical CDS Views sind speziell annotierte Views für OLAP-Szenarien (Online Analytical Processing). Sie unterscheiden sich von transaktionalen Views durch:
- Dimensionen: Gruppierungsmerkmale (Kunde, Produkt, Zeit)
- Measures: Kennzahlen mit Aggregationslogik (Umsatz, Menge)
- Hierarchien: Drill-Down Strukturen (Jahr → Quartal → Monat)
- Aggregationen: Automatische Verdichtung (SUM, AVG, COUNT)
Embedded Analytics vs. BW
| Aspekt | Embedded Analytics | SAP BW |
|---|---|---|
| Ort | Direkt im ABAP System | Separates Data Warehouse |
| Echtzeitdaten | Ja | Nein (ETL-Prozess) |
| Komplexität | Gering | Hoch |
| Anwendungsfall | Operative Reporting | Strategische Analyse |
| Datenvolumen | Mittel | Sehr hoch |
Embedded Analytics ist ideal für operatives Reporting direkt aus den Transaktionsdaten.
Architektur analytischer Views
Die analytische Schichtung folgt dem Virtual Data Model (VDM):
┌─────────────────────────────────────────────────┐│ Analytical Query ││ @Analytics.dataCategory: #DIMENSION ││ @Analytics.query: true ││ (ZC_SalesQuery) │└─────────────────────┬───────────────────────────┘ │┌─────────────────────▼───────────────────────────┐│ Cube View ││ @Analytics.dataCategory: #CUBE ││ (ZI_SalesCube) │└─────────────────────┬───────────────────────────┘ │┌─────────────────────▼───────────────────────────┐│ Dimension Views ││ @Analytics.dataCategory: #DIMENSION ││ (ZI_Customer, ZI_Product, ZI_Time) │└─────────────────────┬───────────────────────────┘ │┌─────────────────────▼───────────────────────────┐│ Basic Views ││ (Transaktionsdaten) │└─────────────────────────────────────────────────┘Cube vs. Query: Der Unterschied
Cube (@Analytics.dataCategory: #CUBE)
Der Cube ist das Datenmodell mit allen Dimensionen und Measures:
@Analytics.dataCategory: #CUBE@ObjectModel.supportedCapabilities: [#ANALYTICAL_QUERY]define view entity ZI_SalesCube as select from vbak as order inner join vbap as item on order.vbeln = item.vbeln inner join kna1 as customer on order.kunnr = customer.kunnr{ -- Dimensionen @Analytics.dimension: true @ObjectModel.foreignKey.association: '_Customer' customer.kunnr as CustomerId,
@Analytics.dimension: true @ObjectModel.foreignKey.association: '_Product' item.matnr as ProductId,
@Analytics.dimension: true @Semantics.calendar.yearMonth: true concat(substring(order.erdat, 1, 4), substring(order.erdat, 5, 2)) as CalendarYearMonth,
@Analytics.dimension: true @Semantics.calendar.year: true substring(order.erdat, 1, 4) as CalendarYear,
@Analytics.dimension: true order.vkorg as SalesOrg,
@Analytics.dimension: true item.werks as Plant,
-- Measures @Analytics.measure: true @Aggregation.default: #SUM @Semantics.amount.currencyCode: 'Currency' item.netwr as Revenue,
@Analytics.measure: true @Aggregation.default: #SUM @Semantics.quantity.unitOfMeasure: 'Unit' item.kwmeng as Quantity,
@Analytics.measure: true @Aggregation.default: #SUM cast(1 as abap.int4) as OrderCount,
@Semantics.currencyCode: true order.waerk as Currency,
@Semantics.unitOfMeasure: true item.vrkme as Unit,
-- Assoziationen fuer Texte _Customer, _Product}Eigenschaften eines Cubes:
- Enthält alle Dimensionen und Measures
- Definiert Aggregationsverhalten
- Wird nicht direkt für UI verwendet
- Grundlage für mehrere Queries
Query (@Analytics.query: true)
Die Query definiert eine konkrete Auswertung auf dem Cube:
@Analytics.query: true@ObjectModel.supportedCapabilities: [#ANALYTICAL_QUERY]@EndUserText.label: 'Umsatzanalyse nach Kunde'define view entity ZC_SalesAnalysis as projection on ZI_SalesCube{ -- Ausgewaehlte Dimensionen @AnalyticsDetails.query.axis: #ROWS @AnalyticsDetails.query.displayHierarchy: #FILTER CustomerId,
@AnalyticsDetails.query.axis: #ROWS CalendarYearMonth,
@AnalyticsDetails.query.axis: #FREE SalesOrg,
-- Aggregierte Measures @AnalyticsDetails.query.axis: #COLUMNS @AnalyticsDetails.query.totals: #SHOW Revenue,
@AnalyticsDetails.query.axis: #COLUMNS Quantity,
@AnalyticsDetails.query.axis: #COLUMNS OrderCount,
-- Berechnete Kennzahlen @AnalyticsDetails.query.axis: #COLUMNS @EndUserText.label: 'Durchschnittlicher Bestellwert' division(Revenue, OrderCount, 2) as AverageOrderValue,
Currency}Eigenschaften einer Query:
- Definiert Achsen (Rows, Columns, Free)
- Bestimmt Aggregationsebene
- Kann berechnete Kennzahlen enthalten
- Wird fuer UI/Reporting verwendet
Dimensionen erstellen
Dimensionen sind die Gruppierungsmerkmale deiner Analyse.
Einfache Dimension
@Analytics.dataCategory: #DIMENSION@ObjectModel.representativeKey: 'CustomerId'@EndUserText.label: 'Kundenstamm'define view entity ZI_CustomerDimension as select from kna1{ @ObjectModel.text.element: ['CustomerName'] key kunnr as CustomerId,
name1 as CustomerName,
@ObjectModel.text.element: ['CountryName'] land1 as Country,
ort01 as City,
brsch as Industry,
_CountryText.landx as CountryName}Zeit-Dimension
@Analytics.dataCategory: #DIMENSION@EndUserText.label: 'Zeitdimension'define view entity ZI_TimeDimension as select from I_CalendarDate{ @Semantics.calendar.date: true key CalendarDate,
@Semantics.calendar.year: true CalendarYear,
@Semantics.calendar.yearQuarter: true CalendarYearQuarter,
@Semantics.calendar.yearMonth: true CalendarYearMonth,
@Semantics.calendar.yearWeek: true CalendarYearWeek,
@Semantics.calendar.month: true CalendarMonth,
@Semantics.calendar.quarter: true CalendarQuarter,
@Semantics.calendar.dayOfMonth: true CalendarDay,
@Semantics.calendar.dayOfWeek: true DayOfWeek,
WeekDay}Dimension mit Hierarchie
@Analytics.dataCategory: #DIMENSION@Hierarchy.parentChild: [{ recurse: { parent: 'ParentOrgUnit', child: 'OrgUnit' }, siblingsOrder: [{ by: 'OrgUnit', direction: #ASC }]}]@EndUserText.label: 'Organisationseinheiten'define view entity ZI_OrgUnitDimension as select from zorg_unit{ @ObjectModel.text.element: ['Description'] key org_unit as OrgUnit,
parent_unit as ParentOrgUnit,
description as Description,
org_type as OrgType}Measures und Aggregationen
Measures sind Kennzahlen mit definiertem Aggregationsverhalten.
Verfuegbare Aggregationstypen
| Aggregation | Beschreibung | Anwendungsfall |
|---|---|---|
#SUM | Summe | Umsatz, Menge, Anzahl |
#AVG | Durchschnitt | Preise, Ratings |
#MIN | Minimum | Fruehestes Datum |
#MAX | Maximum | Hoechstwert |
#COUNT | Anzahl | Datensaetze zaehlen |
#COUNT_DISTINCT | Anzahl eindeutiger Werte | Kunden pro Region |
Measure-Definition
-- Summe@Analytics.measure: true@Aggregation.default: #SUM@Semantics.amount.currencyCode: 'Currency'netwr as Revenue,
-- Durchschnitt@Analytics.measure: true@Aggregation.default: #AVGunit_price as AveragePrice,
-- Zaehler@Analytics.measure: true@Aggregation.default: #SUMcast(1 as abap.int4) as LineItemCount,
-- Minimum@Analytics.measure: true@Aggregation.default: #MINorder_date as FirstOrderDate,
-- Maximum@Analytics.measure: true@Aggregation.default: #MAXorder_date as LastOrderDateBerechnete Measures
-- Im Cube: Basis-Measures definieren@Analytics.measure: true@Aggregation.default: #SUMnetwr as Revenue,
@Analytics.measure: true@Aggregation.default: #SUMmenge as Quantity,
-- In der Query: Berechnungen durchfuehren@EndUserText.label: 'Durchschnittspreis'division(Revenue, Quantity, 2) as AverageUnitPrice,
@EndUserText.label: 'Umsatzanteil %'division(Revenue, sum(Revenue over ()), 4) * 100 as RevenueSharePercentReferenz-Aggregation (Exception Aggregation)
Fuer Bestandswerte wie Lagerbestand braucht man spezielle Aggregation:
@Analytics.measure: true@Aggregation.default: #SUM@Aggregation.referenceElement: 'CalendarDate'@Aggregation.exception: #LASTstock_quantity as EndingInventoryDas bedeutet: Summiere normalerweise, aber bei Aggregation ueber Zeit nimm den letzten Wert.
Vollstaendiges Beispiel: Verkaufsanalyse
1. Dimension Views
-- Kunden-Dimension@Analytics.dataCategory: #DIMENSION@ObjectModel.representativeKey: 'CustomerId'define view entity ZI_Customer_Dim as select from kna1{ @ObjectModel.text.element: ['CustomerName'] key kunnr as CustomerId,
name1 as CustomerName, land1 as Country, ort01 as City, brsch as Industry}-- Produkt-Dimension@Analytics.dataCategory: #DIMENSION@ObjectModel.representativeKey: 'ProductId'define view entity ZI_Product_Dim as select from mara inner join makt on mara.matnr = makt.matnr and makt.spras = $session.system_language{ @ObjectModel.text.element: ['ProductName'] key mara.matnr as ProductId,
makt.maktx as ProductName, mara.mtart as ProductType, mara.matkl as ProductGroup}2. Cube View
@Analytics.dataCategory: #CUBE@ObjectModel.supportedCapabilities: [#ANALYTICAL_QUERY]@AccessControl.authorizationCheck: #CHECK@EndUserText.label: 'Verkaufscube'define view entity ZI_Sales_Cube as select from vbak as header inner join vbap as item on header.vbeln = item.vbeln association [1..1] to ZI_Customer_Dim as _Customer on $projection.CustomerId = _Customer.CustomerId association [1..1] to ZI_Product_Dim as _Product on $projection.ProductId = _Product.ProductId{ -- Dimensionen mit Fremdschluessel-Beziehung @Analytics.dimension: true @ObjectModel.foreignKey.association: '_Customer' header.kunnr as CustomerId,
@Analytics.dimension: true @ObjectModel.foreignKey.association: '_Product' item.matnr as ProductId,
@Analytics.dimension: true @Semantics.calendar.year: true substring(header.erdat, 1, 4) as CalendarYear,
@Analytics.dimension: true @Semantics.calendar.yearMonth: true concat(substring(header.erdat, 1, 4), substring(header.erdat, 5, 2)) as CalendarYearMonth,
@Analytics.dimension: true header.vkorg as SalesOrg,
@Analytics.dimension: true header.vtweg as DistributionChannel,
@Analytics.dimension: true item.werks as Plant,
-- Measures @Analytics.measure: true @Aggregation.default: #SUM @Semantics.amount.currencyCode: 'Currency' item.netwr as Revenue,
@Analytics.measure: true @Aggregation.default: #SUM @Semantics.amount.currencyCode: 'Currency' item.mwsbp as TaxAmount,
@Analytics.measure: true @Aggregation.default: #SUM @Semantics.quantity.unitOfMeasure: 'Unit' item.kwmeng as Quantity,
@Analytics.measure: true @Aggregation.default: #SUM cast(1 as abap.int4) as LineItems,
@Semantics.currencyCode: true header.waerk as Currency,
@Semantics.unitOfMeasure: true item.vrkme as Unit,
-- Assoziationen _Customer, _Product}3. Analytical Query
@Analytics.query: true@ObjectModel.supportedCapabilities: [#ANALYTICAL_QUERY]@AccessControl.authorizationCheck: #CHECK@EndUserText.label: 'Verkaufsanalyse nach Kunde und Produkt'define view entity ZC_Sales_Query as projection on ZI_Sales_Cube{ -- Zeilen-Dimensionen @AnalyticsDetails.query.axis: #ROWS @AnalyticsDetails.query.totals: #SHOW @AnalyticsDetails.query.display: #KEY_TEXT CustomerId,
@AnalyticsDetails.query.axis: #ROWS @AnalyticsDetails.query.display: #KEY_TEXT ProductId,
-- Filter-Dimensionen @AnalyticsDetails.query.axis: #FREE @Consumption.filter: { selectionType: #SINGLE, mandatory: false } CalendarYear,
@AnalyticsDetails.query.axis: #FREE @Consumption.filter: { selectionType: #SINGLE, mandatory: false } SalesOrg,
@AnalyticsDetails.query.axis: #FREE DistributionChannel,
-- Spalten (Kennzahlen) @AnalyticsDetails.query.axis: #COLUMNS @AnalyticsDetails.query.totals: #SHOW Revenue,
@AnalyticsDetails.query.axis: #COLUMNS Quantity,
@AnalyticsDetails.query.axis: #COLUMNS LineItems,
-- Berechnete Kennzahlen @AnalyticsDetails.query.axis: #COLUMNS @EndUserText.label: 'Bruttoumsatz' Revenue + TaxAmount as GrossRevenue,
@AnalyticsDetails.query.axis: #COLUMNS @EndUserText.label: 'Durchschnittspreis' division(Revenue, Quantity, 2) as AveragePrice,
Currency, Unit,
_Customer, _Product}4. Service Binding
-- Service Definition@EndUserText.label: 'Sales Analytics Service'define service ZSB_SALES_ANALYTICS { expose ZC_Sales_Query as SalesAnalysis; expose ZI_Customer_Dim as Customer; expose ZI_Product_Dim as Product;}Integration mit Embedded Analytics
Fiori Analytical List Page (ALP)
Die Analytical List Page kombiniert Chart und Table in einer App:
@Analytics.query: true@UI.chart: [{ chartType: #BAR, dimensions: ['CustomerId'], measures: ['Revenue'], dimensionAttributes: [{ dimension: 'CustomerId', role: #CATEGORY }], measureAttributes: [{ measure: 'Revenue', role: #AXIS1 }]}]@UI.presentationVariant: [{ visualizations: [{ type: #AS_CHART }, { type: #AS_LINEITEM }]}]define view entity ZC_SalesALP as projection on ZI_Sales_Cube{ @UI.lineItem: [{ position: 10 }] @AnalyticsDetails.query.axis: #ROWS CustomerId,
@UI.lineItem: [{ position: 20 }] @AnalyticsDetails.query.axis: #ROWS ProductId,
@UI.lineItem: [{ position: 30 }] @AnalyticsDetails.query.axis: #COLUMNS Revenue,
@UI.lineItem: [{ position: 40 }] @AnalyticsDetails.query.axis: #COLUMNS Quantity,
Currency}KPI Tiles im Fiori Launchpad
Fuer analytische Kacheln im Launchpad:
@Analytics.query: true@UI.headerInfo.title.value: 'Revenue'@UI.kpi: [{ id: 'RevenueKPI', selectionVariantQualifier: 'CurrentYear', detail: { semanticObject: 'SalesAnalysis', action: 'display' }}]define view entity ZC_RevenueKPI as projection on ZI_Sales_Cube{ @AnalyticsDetails.query.axis: #ROWS @Consumption.filter: { selectionType: #SINGLE, defaultValue: '2026' } CalendarYear,
@AnalyticsDetails.query.axis: #COLUMNS @UI.dataPoint: { criticalityCalculation: { improvementDirection: #MAXIMIZE, toleranceRangeLowValue: 1000000, deviationRangeLowValue: 500000 } } Revenue,
Currency}Best Practices
1. Cube-Design
-- RICHTIG: Nur Key-Felder und Measures im Cube@Analytics.dimension: true@ObjectModel.foreignKey.association: '_Customer'customer_id as CustomerId, -- Nur ID, Text kommt via Assoziation
-- FALSCH: Text-Felder direkt im Cubecustomer_id as CustomerId,customer_name as CustomerName -- Macht Cube unnoetig gross2. Performance-Optimierung
-- Aggregations-Verhalten explizit angeben@Analytics.measure: true@Aggregation.default: #SUMrevenue as Revenue,
-- Waehrungsreferenz fuer korrekte Aggregation@Semantics.amount.currencyCode: 'Currency'revenue as Revenue,
@Semantics.currencyCode: truewaerk as Currency3. Wiederverwendbarkeit
-- Ein Cube, mehrere QueriesZI_Sales_Cube ├── ZC_Sales_ByCustomer -- Kundenanalyse ├── ZC_Sales_ByProduct -- Produktanalyse ├── ZC_Sales_ByRegion -- Regionalanalyse └── ZC_Sales_Trending -- Zeitreihenanalyse4. Access Control beachten
@EndUserText.label: 'Sales Cube Authorization'@MappingRole: truedefine role ZI_Sales_Cube_Auth { grant select on ZI_Sales_Cube where (SalesOrg) = aspect pfcg_auth(V_VBAK_VKO, VKORG, ACTVT='03');}Haeufige Fehler und Loesungen
Fehler: “Aggregation not allowed”
-- PROBLEM: Text-Feld ohne Dimension-Annotationcustomer_name as CustomerName,
-- LOESUNG: Als Dimension markieren oder aus Query entfernen@Analytics.dimension: truecustomer_name as CustomerName,Fehler: “Currency conversion required”
-- PROBLEM: Verschiedene Waehrungen werden addiert@Aggregation.default: #SUMrevenue as Revenue, -- EUR, USD, CHF gemischt
-- LOESUNG: Waehrungskonvertierung verwenden@Aggregation.default: #SUM@Semantics.amount.currencyCode: 'TargetCurrency'currency_conversion( amount => revenue, source_currency => currency, target_currency => 'EUR', exchange_rate_date => $session.system_date) as RevenueInEUR,
cast('EUR' as waers) as TargetCurrencyFehler: “Hierarchy not found”
-- PROBLEM: Hierarchie-View nicht korrekt definiert@Hierarchy.parentChild: [{ ... }]
-- LOESUNG: Sicherstellen dass Parent/Child-Beziehung korrekt ist@Hierarchy.parentChild: [{ recurse: { parent: 'ParentId', -- Muss existieren child: 'NodeId' -- Muss Key-Feld sein }}]Zusammenfassung
| Konzept | Annotation | Beschreibung |
|---|---|---|
| Cube | @Analytics.dataCategory: #CUBE | Datenmodell mit allen Dimensionen/Measures |
| Query | @Analytics.query: true | Konkrete Auswertung auf Cube |
| Dimension | @Analytics.dimension: true | Gruppierungsmerkmal |
| Measure | @Analytics.measure: true | Kennzahl mit Aggregation |
| Achse | @AnalyticsDetails.query.axis | Platzierung in Query (#ROWS, #COLUMNS, #FREE) |
Analytical CDS Views sind maechtige Werkzeuge fuer operative Analytik direkt im ABAP System. Mit Cubes definierst du das Datenmodell, mit Queries die konkreten Auswertungen fuer Fiori Apps und Dashboards.
Verwandte Themen
- CDS Views - CDS Grundlagen
- CDS Annotations - Annotations fuer UI und Analytics
- Fiori Overview Page - Analytische Dashboards
- OData Services - Service Binding fuer Analytics