CDS Views Deep Dive: De básico a Analytics (2025)

Kategorie
Deep Dive
Veröffentlicht
Autor
Johannes

CDS Views (Core Data Services) son el corazón del desarrollo ABAP moderno. Este deep dive te muestra todo, desde lo básico hasta Analytics avanzado.

¿Qué son los CDS Views?

Los CDS Views son modelos de datos semánticos definidos en sintaxis similar a SQL que se ejecutan directamente en la base de datos.

Ventajas vs. views clásicas

CaracterísticaViews clásicas (SE11)CDS Views
SintaxisABAP DictionarySimilar a SQL (DDL)
RendimientoBuenoMejor (optimizado en BD)
AnotacionesNingunaExtensas
AsociacionesNo
AnalyticsLimitadoExtenso
ReutilizaciónBajaAlta

1. Básicos de CDS

CDS View más simple

@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
}

Componentes:

  • @AbapCatalog.sqlViewName: Nombre del DB-View generado (máx. 16 caracteres)
  • @AccessControl: Verificación de permisos
  • define view: Definición del view
  • as select from: Fuente de datos
  • { ... }: Lista de campos

Activar view

  • En ADT: Ctrl+F3
  • Genera automáticamente DB-View ZV_CUSTOMER

Usar view

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 (¡Recomendado!)

Mejor que Joins: Carga diferida (lazy loading), reutilizable

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,
/* Exponer asociación (¡sin Join!) */
_SalesOrders
}

Uso:

" Solo cargar datos de Customer
SELECT Customer, CustomerName
FROM Z_Customer
INTO TABLE @DATA(lt_customers).
" Con Orders (¡solo cuando se necesita!)
SELECT Customer, CustomerName,
\_SalesOrders-vbeln as OrderNumber
FROM Z_Customer
WHERE Country = 'DE'
INTO TABLE @DATA(lt_customer_orders).

Ventaja: ¡Rendimiento! Los pedidos solo se cargan cuando se necesitan.


3. Anotaciones importantes

@Semantics

Da significado semántico a los campos:

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

@ObjectModel

Para 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,
/* Elemento de texto */
@ObjectModel.text.element: ['ProductName']
matnr as ProductId,
maktx as ProductName
}

@Analytics

Para consultas analíticas:

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

4. Parámetros

Parámetros de entrada para views dinámicos:

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

Uso:

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

Con valor por defecto:

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

5. Virtual Elements

Campos calculados (no en BD):

define view Z_Product
as select from mara
{
key matnr as Product,
/* Campos virtuales */
@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
}

Implementación:

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.
" Leer datos
DATA lt_products TYPE STANDARD TABLE OF z_product.
lt_products = CORRESPONDING #( it_original_data ).
" Consulta de stock
LOOP AT lt_products ASSIGNING FIELD-SYMBOL(<product>).
SELECT SINGLE labst FROM mard
WHERE matnr = <product>-product
INTO @<product>-availablestock.
ENDLOOP.
" Devolver
ct_calculated_data = CORRESPONDING #( lt_products ).
ENDMETHOD.
ENDCLASS.

6. Access Control (DCL)

Permisos de datos a nivel de View:

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
/* Solo propia Sales Org */
(SalesOrg) = aspect pfcg_auth(
V_VBAK_VKO,
VKORG,
ACTVT = '03'
);
}

Resultado: ¡Los usuarios solo ven SalesOrders de su SalesOrg!


7. Hierarchies

Para estructuras padre-hijo:

@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,
/* Se calcula automáticamente */
cast( 0 as abap.int1 ) as Level
}

8. Extender CDS Views

Usar otros views como base:

/* View base */
define view Z_Customer_Basic
as select from kna1
{
key kunnr as Customer,
name1 as CustomerName,
land1 as Country
}
/* Extiende view base */
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,
/* Campos adicionales */
_Orders.vbeln as OrderNumber,
_Orders.netwr as OrderValue,
_Orders
}

9. CDS Views para 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 (consume 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. Mejores prácticas de rendimiento

HACER

1. Pushdown en lugar de lógica ABAP:

/* Bueno: Calcular en CDS */
define view Z_Customer
as select from kna1
{
key kunnr,
/* Cálculo en BD */
case land1
when 'DE' then 'Alemania'
when 'AT' then 'Austria'
else 'Otro país'
end as CountryText
}

2. Usar índices:

/* Donde sea posible filtrar por campos clave */
where vbeln = :p_sales_order /* Clave, muy rápido */

3. Activar buffering:

@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
}

NO HACER

1. Demasiados Joins:

/* Malo: 10 Joins */
define view Z_ComplexView
as select from t1
join t2 on ...
join t3 on ...
... /* 10 más */

Mejor: Múltiples views con Associations

2. SELECT DISTINCT sin razón:

/* Lento */
define view Z_Customer
as select distinct from kna1

3. Unions sin necesidad:

/* Lento si evitable */
define view Z_Combined
as select from tab1
union all
select from tab2

11. Testing de 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.
" Insertar datos de prueba
environment->insert_test_data(
i_data = VALUE #(
( customer = '0001' customername = 'Test AG' country = 'DE' )
)
).
" Consultar CDS View
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.

Resumen: CDS View Cheat Sheet

CaracterísticaSintaxisCaso de uso
View básicodefine view ... as select fromConsulta de datos simple
Joininner/left join ... onVincular datos
Associationassociation [0..*] toCarga diferida, rendimiento
Parámetroswith parameters p_x : typeFiltros dinámicos
Virtual Elements@ObjectModel.virtualElementCampos calculados (ABAP)
Access Control@AccessControl + DCLPermisos
Hierarchies@Hierarchy.parentChildEstructuras organizativas
Analytics@Analytics.dataCategory: #CUBEReporting

Recursos adicionales

En abapcloud.com:

Documentación SAP:

Mejores prácticas:

  • Usa Associations en lugar de Joins donde sea posible
  • Pushdown de lógica a BD (no ABAP)
  • Buffering para datos maestros
  • Access Control para permisos
  • ¡Testea tus CDS Views!

¡Mucho éxito con CDS Views!