Custom Analytical Queries dans ABAP Cloud : Evaluations analytiques personnalisees

Catégorie
CDS
Publié
Auteur
Johannes

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

AspectEmbedded AnalyticsSAP BW
EmplacementDirectement dans le systeme ABAPData Warehouse separe
Donnees en temps reelOuiNon (processus ETL)
ComplexiteFaibleElevee
Cas d’utilisationReporting operationnelAnalyse strategique
Volume de donneesMoyenTres 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

AgregationDescriptionCas d’utilisation
#SUMSommeChiffre d’affaires, quantite, nombre
#AVGMoyennePrix, notes
#MINMinimumDate la plus ancienne
#MAXMaximumValeur maximale
#COUNTNombreCompter les enregistrements
#COUNT_DISTINCTNombre de valeurs uniquesClients 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: #AVG
unit_price as AveragePrice,
-- Compteur
@Analytics.measure: true
@Aggregation.default: #SUM
cast(1 as abap.int4) as LineItemCount,
-- Minimum
@Analytics.measure: true
@Aggregation.default: #MIN
order_date as FirstOrderDate,
-- Maximum
@Analytics.measure: true
@Aggregation.default: #MAX
order_date as LastOrderDate

Mesures calculees

-- Dans le Cube : definir les mesures de base
@Analytics.measure: true
@Aggregation.default: #SUM
netwr as Revenue,
@Analytics.measure: true
@Aggregation.default: #SUM
menge 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 RevenueSharePercent

Agregation 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: #LAST
stock_quantity as EndingInventory

Cela 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 Cube
customer_id as CustomerId,
customer_name as CustomerName -- Rend le Cube inutilement volumineux

2. Optimisation des performances

-- Specifier explicitement le comportement d'agregation
@Analytics.measure: true
@Aggregation.default: #SUM
revenue as Revenue,
-- Reference de devise pour une agregation correcte
@Semantics.amount.currencyCode: 'Currency"
revenue as Revenue,
@Semantics.currencyCode: true
waerk as Currency

3. Reutilisabilite

-- Un Cube, plusieurs Queries
ZI_Sales_Cube
├── ZC_Sales_ByCustomer -- Analyse par client
├── ZC_Sales_ByProduct -- Analyse par produit
├── ZC_Sales_ByRegion -- Analyse regionale
└── ZC_Sales_Trending -- Analyse des tendances

4. Prendre en compte le controle d’acces

@EndUserText.label: 'Sales Cube Authorization"
@MappingRole: true
define 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 Dimension
customer_name as CustomerName,
-- SOLUTION : Marquer comme Dimension ou supprimer de la Query
@Analytics.dimension: true
customer_name as CustomerName,

Erreur : “Currency conversion required”

-- PROBLEME : Differentes devises sont additionnees
@Aggregation.default: #SUM
revenue 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 TargetCurrency

Erreur : “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

ConceptAnnotationDescription
Cube@Analytics.dataCategory: #CUBEModele de donnees avec toutes les dimensions/mesures
Query@Analytics.query: trueEvaluation concrete sur le Cube
Dimension@Analytics.dimension: trueCaracteristique de regroupement
Mesure@Analytics.measure: trueIndicateur avec agregation
Axe@AnalyticsDetails.query.axisPlacement 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