Les Custom Analytical Queries vous permettent de creer vos propres evaluations analytiques directement dans ABAP Cloud. Avec les CDS Views et des annotations specifiques, vous construisez des Cubes, des Queries et des tableaux de bord pour les scenarios de Business Intelligence.
Que sont les Analytical CDS Views ?
Les Analytical CDS Views sont des vues specialement annotees pour les scenarios OLAP (Online Analytical Processing). Elles se distinguent des vues transactionnelles par :
- Dimensions : caracteristiques de regroupement (client, produit, temps)
- Mesures : indicateurs avec logique d’agregation (chiffre d’affaires, quantite)
- Hierarchies : structures de drill-down (annee → trimestre → mois)
- Agregations : consolidation automatique (SUM, AVG, COUNT)
Embedded Analytics vs BW
| Aspect | Embedded Analytics | SAP BW |
|---|---|---|
| Emplacement | Directement dans le systeme ABAP | Data Warehouse separe |
| Donnees en temps reel | Oui | Non (processus ETL) |
| Complexite | Faible | Elevee |
| Cas d’utilisation | Reporting operationnel | Analyse strategique |
| Volume de donnees | Moyen | Tres eleve |
Embedded Analytics est ideal pour le reporting operationnel directement a partir des donnees transactionnelles.
Architecture des vues analytiques
La stratification analytique suit le 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 ││ (Donnees transactionnelles) │└─────────────────────────────────────────────────┘Cube vs Query : la difference
Cube (@Analytics.dataCategory: #CUBE)
Le Cube est le modele de donnees avec toutes les dimensions et mesures :
@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{ -- Dimensions @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,
-- Mesures @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,
-- Associations pour les textes _Customer, _Product}Caracteristiques d’un Cube :
- Contient toutes les dimensions et mesures
- Definit le comportement d’agregation
- N’est pas utilise directement pour l’UI
- Base pour plusieurs Queries
Query (@Analytics.query: true)
La Query definit une evaluation concrete sur le Cube :
@Analytics.query: true@ObjectModel.supportedCapabilities: [#ANALYTICAL_QUERY]@EndUserText.label: 'Analyse du chiffre d affaires par client"define view entity ZC_SalesAnalysis as projection on ZI_SalesCube{ -- Dimensions selectionnees @AnalyticsDetails.query.axis: #ROWS @AnalyticsDetails.query.displayHierarchy: #FILTER CustomerId,
@AnalyticsDetails.query.axis: #ROWS CalendarYearMonth,
@AnalyticsDetails.query.axis: #FREE SalesOrg,
-- Mesures agregees @AnalyticsDetails.query.axis: #COLUMNS @AnalyticsDetails.query.totals: #SHOW Revenue,
@AnalyticsDetails.query.axis: #COLUMNS Quantity,
@AnalyticsDetails.query.axis: #COLUMNS OrderCount,
-- Indicateurs calcules @AnalyticsDetails.query.axis: #COLUMNS @EndUserText.label: 'Valeur moyenne de commande" division(Revenue, OrderCount, 2) as AverageOrderValue,
Currency}Caracteristiques d’une Query :
- Definit les axes (Rows, Columns, Free)
- Determine le niveau d’agregation
- Peut contenir des indicateurs calcules
- Utilisee pour l’UI/Reporting
Creer des dimensions
Les dimensions sont les caracteristiques de regroupement de votre analyse.
Dimension simple
@Analytics.dataCategory: #DIMENSION@ObjectModel.representativeKey: 'CustomerId"@EndUserText.label: 'Donnees client"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}Dimension temporelle
@Analytics.dataCategory: #DIMENSION@EndUserText.label: 'Dimension temporelle"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 avec hierarchie
@Analytics.dataCategory: #DIMENSION@Hierarchy.parentChild: [{ recurse: { parent: 'ParentOrgUnit', child: 'OrgUnit' }, siblingsOrder: [{ by: 'OrgUnit', direction: #ASC }]}]@EndUserText.label: 'Unites organisationnelles"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}Mesures et agregations
Les mesures sont des indicateurs avec un comportement d’agregation defini.
Types d’agregation disponibles
| Agregation | Description | Cas d’utilisation |
|---|---|---|
#SUM | Somme | Chiffre d’affaires, quantite, nombre |
#AVG | Moyenne | Prix, notes |
#MIN | Minimum | Date la plus ancienne |
#MAX | Maximum | Valeur maximale |
#COUNT | Nombre | Compter les enregistrements |
#COUNT_DISTINCT | Nombre de valeurs uniques | Clients par region |
Definition de mesures
-- Somme@Analytics.measure: true@Aggregation.default: #SUM@Semantics.amount.currencyCode: 'Currency"netwr as Revenue,
-- Moyenne@Analytics.measure: true@Aggregation.default: #AVGunit_price as AveragePrice,
-- Compteur@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 LastOrderDateMesures calculees
-- Dans le Cube : definir les mesures de base@Analytics.measure: true@Aggregation.default: #SUMnetwr as Revenue,
@Analytics.measure: true@Aggregation.default: #SUMmenge as Quantity,
-- Dans la Query : effectuer les calculs@EndUserText.label: 'Prix moyen"division(Revenue, Quantity, 2) as AverageUnitPrice,
@EndUserText.label: 'Part du CA %"division(Revenue, sum(Revenue over ()), 4) * 100 as RevenueSharePercentAgregation de reference (Exception Aggregation)
Pour les valeurs de stock comme l’inventaire, une agregation speciale est necessaire :
@Analytics.measure: true@Aggregation.default: #SUM@Aggregation.referenceElement: 'CalendarDate"@Aggregation.exception: #LASTstock_quantity as EndingInventoryCela signifie : faire la somme normalement, mais lors de l’agregation dans le temps, prendre la derniere valeur.
Exemple complet : analyse des ventes
1. Dimension Views
-- Dimension Client@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}-- Dimension Produit@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: 'Cube des ventes"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{ -- Dimensions avec relation de cle etrangere @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,
-- Mesures @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,
-- Associations _Customer, _Product}3. Analytical Query
@Analytics.query: true@ObjectModel.supportedCapabilities: [#ANALYTICAL_QUERY]@AccessControl.authorizationCheck: #CHECK@EndUserText.label: 'Analyse des ventes par client et produit"define view entity ZC_Sales_Query as projection on ZI_Sales_Cube{ -- Dimensions en lignes @AnalyticsDetails.query.axis: #ROWS @AnalyticsDetails.query.totals: #SHOW @AnalyticsDetails.query.display: #KEY_TEXT CustomerId,
@AnalyticsDetails.query.axis: #ROWS @AnalyticsDetails.query.display: #KEY_TEXT ProductId,
-- Dimensions de filtre @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,
-- Colonnes (indicateurs) @AnalyticsDetails.query.axis: #COLUMNS @AnalyticsDetails.query.totals: #SHOW Revenue,
@AnalyticsDetails.query.axis: #COLUMNS Quantity,
@AnalyticsDetails.query.axis: #COLUMNS LineItems,
-- Indicateurs calcules @AnalyticsDetails.query.axis: #COLUMNS @EndUserText.label: 'CA brut" Revenue + TaxAmount as GrossRevenue,
@AnalyticsDetails.query.axis: #COLUMNS @EndUserText.label: 'Prix moyen" 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 avec Embedded Analytics
Fiori Analytical List Page (ALP)
L’Analytical List Page combine graphique et tableau dans une application :
@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}Tuiles KPI dans le Fiori Launchpad
Pour les tuiles analytiques dans le 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}Bonnes pratiques
1. Conception du Cube
-- CORRECT : Seuls les champs cles et les mesures dans le Cube@Analytics.dimension: true@ObjectModel.foreignKey.association: '_Customer"customer_id as CustomerId, -- Seulement l'ID, le texte vient via l'association
-- INCORRECT : Champs texte directement dans le Cubecustomer_id as CustomerId,customer_name as CustomerName -- Rend le Cube inutilement volumineux2. Optimisation des performances
-- Specifier explicitement le comportement d'agregation@Analytics.measure: true@Aggregation.default: #SUMrevenue as Revenue,
-- Reference de devise pour une agregation correcte@Semantics.amount.currencyCode: 'Currency"revenue as Revenue,
@Semantics.currencyCode: truewaerk as Currency3. Reutilisabilite
-- Un Cube, plusieurs QueriesZI_Sales_Cube ├── ZC_Sales_ByCustomer -- Analyse par client ├── ZC_Sales_ByProduct -- Analyse par produit ├── ZC_Sales_ByRegion -- Analyse regionale └── ZC_Sales_Trending -- Analyse des tendances4. Prendre en compte le controle d’acces
@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');}Erreurs courantes et solutions
Erreur : “Aggregation not allowed”
-- PROBLEME : Champ texte sans annotation Dimensioncustomer_name as CustomerName,
-- SOLUTION : Marquer comme Dimension ou supprimer de la Query@Analytics.dimension: truecustomer_name as CustomerName,Erreur : “Currency conversion required”
-- PROBLEME : Differentes devises sont additionnees@Aggregation.default: #SUMrevenue as Revenue, -- EUR, USD, CHF melanges
-- SOLUTION : Utiliser la conversion de devise@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 TargetCurrencyErreur : “Hierarchy not found”
-- PROBLEME : Vue hierarchique mal definie@Hierarchy.parentChild: [{ ... }]
-- SOLUTION : S'assurer que la relation Parent/Child est correcte@Hierarchy.parentChild: [{ recurse: { parent: 'ParentId', -- Doit exister child: 'NodeId' -- Doit etre un champ cle }}]Resume
| Concept | Annotation | Description |
|---|---|---|
| Cube | @Analytics.dataCategory: #CUBE | Modele de donnees avec toutes les dimensions/mesures |
| Query | @Analytics.query: true | Evaluation concrete sur le Cube |
| Dimension | @Analytics.dimension: true | Caracteristique de regroupement |
| Mesure | @Analytics.measure: true | Indicateur avec agregation |
| Axe | @AnalyticsDetails.query.axis | Placement dans la Query (#ROWS, #COLUMNS, #FREE) |
Les Analytical CDS Views sont des outils puissants pour l’analytique operationnelle directement dans le systeme ABAP. Avec les Cubes, vous definissez le modele de donnees, avec les Queries, les evaluations concretes pour les applications Fiori et les tableaux de bord.
Sujets connexes
- CDS Views - Fondamentaux CDS
- CDS Annotations - Annotations pour UI et Analytics
- Fiori Overview Page - Tableaux de bord analytiques
- OData Services - Service Binding pour Analytics