CDS - Associações e Joins
Diferença entre Associações e Joins
Joins (Clássicos)
- Executados imediatamente
- Todos os dados são carregados
- Definidos na cláusula
FROM
Associações (On-Demand)
- Executados apenas quando necessário
- Dados carregados sob demanda
- Reutilizáveis em outras views
- Recomendado para CDS moderno
Associações em CDS
Sintaxe Básica
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Sales Order with Customer'
define view entity ZI_SalesOrder
as select from vbak as SalesOrder
// Definição da associação
association [0..1] to ZI_Customer as _Customer
on $projection.CustomerId = _Customer.CustomerId
{
key SalesOrder.vbeln as SalesOrderId,
SalesOrder.erdat as CreationDate,
SalesOrder.kunnr as CustomerId,
SalesOrder.netwr as NetValue,
// Exposição da associação
_Customer
}
Cardinalidades
// [min..max] ou [0..*]
association [1] to ZI_Customer as _Customer // Exatamente 1
association [0..1] to ZI_Address as _Address // Zero ou 1
association [1..*] to ZI_Item as _Items // Um ou mais
association [0..*] to ZI_Document as _Documents // Zero ou mais
Consumindo Associações
Em Consulta ABAP
SELECT FROM ZI_SalesOrder
FIELDS SalesOrderId,
CreationDate,
CustomerId,
\_Customer-CustomerName, " Acesso via associação
\_Customer-Country,
\_Customer-City
WHERE CreationDate >= @sy-datum
INTO TABLE @DATA(lt_orders).
LOOP AT lt_orders INTO DATA(ls_order).
WRITE: / ls_order-SalesOrderId,
ls_order-CustomerName,
ls_order-Country.
ENDLOOP.
Em Outra CDS View
define view entity ZC_SalesOrderDisplay
as select from ZI_SalesOrder
{
key SalesOrderId,
CreationDate,
CustomerId,
NetValue,
// Projeção de campos da associação
_Customer.CustomerName,
_Customer.Country,
_Customer.City,
// Manter associação para uso posterior
_Customer
}
Path Expressions (Navegação em Cadeia)
Múltiplos Níveis de Associação
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Sales Order Item with Details'
define view entity ZI_SalesOrderItem
as select from vbap as Item
association [1] to ZI_SalesOrder as _SalesOrder
on $projection.SalesOrderId = _SalesOrder.SalesOrderId
{
key Item.vbeln as SalesOrderId,
key Item.posnr as ItemNumber,
Item.matnr as ProductId,
Item.kwmeng as Quantity,
// Navegação em cadeia: Item → Order → Customer
_SalesOrder._Customer.CustomerName as CustomerName,
_SalesOrder._Customer.Country as CustomerCountry,
// Exposição das associações
_SalesOrder
}
Exemplo Completo de Path Expression
define view entity ZI_InvoiceItem
as select from bseg as InvoiceItem
association [1] to ZI_Invoice as _Invoice
on $projection.DocumentNumber = _Invoice.DocumentNumber
{
key InvoiceItem.belnr as DocumentNumber,
key InvoiceItem.buzei as LineItem,
InvoiceItem.wrbtr as Amount,
// Caminho: Item → Invoice → Customer → Country
_Invoice._Customer._Country.CountryName as CountryName,
_Invoice._Customer._Country.Region as Region,
_Invoice
}
Joins Explícitos
Inner Join
define view entity ZI_CustomerOrders
as select from kna1 as Customer
inner join vbak as SalesOrder
on Customer.kunnr = SalesOrder.kunnr
{
key Customer.kunnr as CustomerId,
Customer.name1 as CustomerName,
SalesOrder.vbeln as SalesOrderId,
SalesOrder.erdat as OrderDate,
SalesOrder.netwr as NetValue
}
Left Outer Join
define view entity ZI_CustomerWithOrders
as select from kna1 as Customer
left outer join vbak as SalesOrder
on Customer.kunnr = SalesOrder.kunnr
{
key Customer.kunnr as CustomerId,
Customer.name1 as CustomerName,
SalesOrder.vbeln as SalesOrderId,
SalesOrder.erdat as OrderDate,
SalesOrder.netwr as NetValue
}
Performance
Joins são executados imediatamente. Use associações sempre que possível para melhor performance, especialmente com HANA.
Comparação: Join vs Associação
Exemplo com Join
define view entity ZI_Order_WithJoin
as select from vbak as SalesOrder
inner join kna1 as Customer
on SalesOrder.kunnr = Customer.kunnr
inner join vbap as Item
on SalesOrder.vbeln = Item.vbeln
{
key SalesOrder.vbeln as SalesOrderId,
key Item.posnr as ItemNumber,
Customer.name1 as CustomerName,
Item.matnr as ProductId
}
Mesmo Exemplo com Associação (Recomendado)
define view entity ZI_Order_WithAssoc
as select from vbak as SalesOrder
association [0..1] to ZI_Customer as _Customer
on $projection.CustomerId = _Customer.CustomerId
association [0..*] to ZI_Item as _Items
on $projection.SalesOrderId = _Items.SalesOrderId
{
key SalesOrder.vbeln as SalesOrderId,
SalesOrder.kunnr as CustomerId,
_Customer,
_Items
}
// View de consumo que projeta os dados necessários
define view entity ZC_OrderDisplay
as select from ZI_Order_WithAssoc
{
key SalesOrderId,
CustomerId,
_Customer.CustomerName,
_Items.ItemNumber,
_Items.ProductId
}
Associações com Filtros
Filtro na Associação
define view entity ZI_SalesOrder
as select from vbak as SalesOrder
association [0..*] to vbap as _Items
on $projection.SalesOrderId = _Items.vbeln
and _Items.abgru = '' // Apenas itens não rejeitados
{
key SalesOrder.vbeln as SalesOrderId,
SalesOrder.kunnr as CustomerId,
_Items
}
Associação com Parâmetros
define view entity ZI_SalesOrderFiltered
as select from vbak as SalesOrder
association [0..*] to ZI_Item as _Items
on $projection.SalesOrderId = _Items.SalesOrderId
and _Items.Quantity > 0
{
key SalesOrder.vbeln as SalesOrderId,
SalesOrder.kunnr as CustomerId,
SalesOrder.netwr as NetValue,
_Items
}
Associações para Textos
View de Texto
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Product Text'
define view entity ZI_ProductText
as select from makt
{
key matnr as ProductId,
key spras as Language,
maktx as ProductDescription
}
View Principal com Associação de Texto
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Product with Text'
define view entity ZI_Product
as select from mara as Product
association [0..*] to ZI_ProductText as _Text
on $projection.ProductId = _Text.ProductId
{
key Product.matnr as ProductId,
Product.meins as BaseUnit,
Product.ntgew as NetWeight,
// Texto na língua de logon
_Text[1: Language = $session.system_language].ProductDescription,
_Text
}
Exemplo Completo: Hierarquia de Views
1. View de Interface (Customer)
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Customer Interface'
define view entity ZI_Customer
as select from kna1
association [0..1] to ZI_Country as _Country
on $projection.Country = _Country.CountryCode
{
key kunnr as CustomerId,
name1 as CustomerName,
land1 as Country,
ort01 as City,
_Country
}
2. View de Interface (Sales Order)
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Sales Order Interface'
define view entity ZI_SalesOrder
as select from vbak
association [0..1] to ZI_Customer as _Customer
on $projection.CustomerId = _Customer.CustomerId
association [0..*] to ZI_SalesOrderItem as _Items
on $projection.SalesOrderId = _Items.SalesOrderId
{
key vbeln as SalesOrderId,
erdat as CreationDate,
kunnr as CustomerId,
@Semantics.amount.currencyCode: 'Currency'
netwr as NetValue,
waers as Currency,
_Customer,
_Items
}
3. View de Consumo
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Sales Order Consumption'
@Metadata.allowExtensions: true
define view entity ZC_SalesOrder
as select from ZI_SalesOrder
{
key SalesOrderId,
CreationDate,
CustomerId,
_Customer.CustomerName,
_Customer.Country,
_Customer._Country.CountryName,
NetValue,
Currency,
_Customer,
_Items
}
Exercícios Práticos
Exercício 1: Associação Simples
Crie uma view ZI_Material com associação para ZI_MaterialText.
Exercício 2: Path Expression
Crie uma view que mostre itens de pedido com o nome do país do cliente usando path expression.
Exercício 3: Comparação
Implemente a mesma lógica usando: 1. Join explícito 2. Associação Compare a performance com Data Preview.
Próximos Passos
Na próxima página, exploraremos Anotações e Metadata Extensions para enriquecer as CDS Views com informações semânticas.