Como usar AMDP no S/4HANA: Table Functions, Scalar Functions e Virtual Elements
O cálculo está no lugar errado?
Boa parte dos problemas de performance em S/4HANA nasce do mesmo erro: processar dados no servidor de aplicação quando o HANA já daria a resposta pronta. É o oposto do code pushdown — o princípio de empurrar o trabalho para perto dos dados.
AMDP (ABAP Managed Database Procedures) é a ferramenta para isso: você escreve procedures e functions nativas do HANA em SQLScript, mas gerenciadas pelo ciclo de vida do ABAP — definidas em métodos de classe, transportadas e versionadas como qualquer objeto. O código mora no ABAP; a execução acontece no banco. A pergunta que este guia responde não é "como escrever AMDP", e sim "qual sabor usar em cada situação" — porque escolher errado entre Table Function, Scalar Function e Virtual Element é o que separa um relatório instantâneo de um que trava com 10 mil linhas.
Neste guia você vai ver, na prática, como usar Table Functions, como usar Scalar Functions e como usar Virtual Elements — com exemplos AMDP prontos para copiar, uma matriz de decisão para escolher o sabor certo e os anti-padrões que matam a performance.
O essencial em 4 linhas
Table Function: retorna uma tabela, roda no HANA. Para lógica SQL que o CDS puro não expressa.
Scalar Function: retorna um valor, roda no HANA. Para fórmulas reutilizáveis com pushdown completo.
Virtual Element (ABAP exit): calcula no servidor de aplicação, linha a linha. Último recurso — só quando precisa de FM/BAPI/contexto da sessão e o resultset é pequeno.
Regra de ouro: só vá para AMDP quando o CDS puro não der conta. E nunca use loops dentro de AMDP.
O que é AMDP e quais são os três objetos que usam?
AMDP deixa você executar SQLScript nativo do HANA diretamente do código ABAP, evitando o transporte massivo de dados entre o servidor de aplicação e o banco. Três variações se integram a CDS, e cada uma resolve um problema diferente:
Componente | Retorno | Onde executa | Caso de uso primário |
|---|---|---|---|
Table Function | Tabela (multi-row) | HANA (SQLScript) | Lógica SQL complexa que o CDS puro não consegue expressar |
Scalar Function | Valor único | HANA (SQLScript) | Encapsular fórmulas reutilizáveis em SELECT-list / WHERE |
Virtual Element | Coluna calculada | Application Server (ABAP) | Cálculos dinâmicos pós-query, com contexto de usuário/sessão |
O detalhe que pega todo mundo: Virtual Elements padrão rodam em ABAP no Application Server, não no HANA. Para fazer pushdown real do cálculo de uma coluna, prefira Scalar Functions ou expressões em CDS.
Qual a hierarquia de code pushdown?
A SAP recomenda uma ordem de preferência clara. Você só desce um degrau quando o degrau de cima não atende. Comece sempre pelo mais simples e mais próximo do banco:
1. CDS Views (puro) ← Sempre tente primeiro
2. CDS Table Functions + AMDP ← Quando o CDS puro não atende
3. AMDP Procedures ← Lógica imperativa pesada / DML
4. ABAP SQL (Open SQL) ← Acesso pontual na aplicação
5. Virtual Elements (ABAP exit) ← Último recurso para cálculos"Só transicione para AMDP quando não conseguir cumprir os requisitos usando CDS. Evite loops em AMDP — eles destroem a performance."
Como usar CDS Table Functions (e quando valem a pena)?
Uma CDS Table Function é uma entidade CDS cuja implementação é delegada a um método AMDP que retorna uma tabela. Por fora, você a consome como qualquer view CDS — com SELECT, joins e associações. Por dentro, ela roda SQLScript no HANA. É o degrau certo quando o CDS puro trava.
Quando USAR
Lógica SQL que o CDS puro não consegue expressar: window functions com particionamento dinâmico, hierarquias recursivas, manipulação avançada de string/data com lógica condicional, ou consumo de funções HANA nativas (geoespaciais, predictive).
Quando você precisa de parâmetros de entrada que alteram a query.
Quando o resultado precisa ser reutilizável em outras CDS.
Quando NÃO usar
Quando a lógica cabe em CDS puro — use CDS.
Para operações DML (INSERT/UPDATE/DELETE): Table Function é read-only.
Quando precisa de loops imperativos pesados — considere uma AMDP Procedure.
Para uma chamada única de relatório pontual — use Open SQL.
Limitações que você precisa conhecer
O
WHEREadicional não tem a mesma flexibilidade do CDS puro — o filtro vem do consumidor.O cliente (
mandt) é tratado manualmente, viaSESSION_CONTEXT( 'CLIENT' )ou a keywordCLIENT.Não pode chamar outras Table Functions diretamente — use a tabela base.
O método AMDP deve ser estático e a classe precisa implementar a interface marker
IF_AMDP_MARKER_HDB.
Exemplo completo: saldo de pedidos com agregação condicional
Primeiro a definição da Table Function (DDL), com parâmetros de entrada e a annotation @ClientDependent:
@EndUserText.label: 'Saldo consolidado de pedidos por cliente'
@ClientDependent: true
define table function ZTF_SALES_BALANCE
with parameters
p_year : abap.numc(4),
p_min_total : abap.curr(15,2)
returns {
client : abap.clnt;
key customer_id : kunnr;
key year_month : abap.char(6);
order_count : abap.int4;
@Semantics.amount.currencyCode: 'currency'
total_amount : abap.curr(15,2);
@Semantics.amount.currencyCode: 'currency'
avg_amount : abap.curr(15,2);
currency : waers;
classification : abap.char(10);
}
implemented by method
zcl_sales_balance_amdp=>get_balance;Agora a classe AMDP que implementa a lógica em SQLScript:
CLASS zcl_sales_balance_amdp DEFINITION
PUBLIC
FINAL
CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_amdp_marker_hdb.
CLASS-METHODS get_balance
FOR TABLE FUNCTION ztf_sales_balance.
ENDCLASS.
CLASS zcl_sales_balance_amdp IMPLEMENTATION.
METHOD get_balance BY DATABASE FUNCTION
FOR HDB
LANGUAGE SQLSCRIPT
OPTIONS READ-ONLY
USING vbak vbap.
RETURN
SELECT
v.mandt AS client,
v.kunnr AS customer_id,
TO_VARCHAR( v.erdat, 'YYYYMM' ) AS year_month,
COUNT( DISTINCT v.vbeln ) AS order_count,
SUM( p.netwr ) AS total_amount,
AVG( p.netwr ) AS avg_amount,
MAX( v.waerk ) AS currency,
CASE
WHEN SUM( p.netwr ) >= :p_min_total * 10 THEN 'PREMIUM'
WHEN SUM( p.netwr ) >= :p_min_total THEN 'STANDARD'
ELSE 'LOW'
END AS classification
FROM vbak AS v
INNER JOIN vbap AS p
ON v.mandt = p.mandt
AND v.vbeln = p.vbeln
WHERE v.mandt = SESSION_CONTEXT( 'CLIENT' )
AND TO_VARCHAR( v.erdat, 'YYYY' ) = :p_year
GROUP BY
v.mandt,
v.kunnr,
TO_VARCHAR( v.erdat, 'YYYYMM' )
HAVING SUM( p.netwr ) >= :p_min_total;
ENDMETHOD.
ENDCLASS.E o consumo — primeiro em ABAP SQL, depois empilhando outra CDS por cima:
" Em ABAP SQL — usa como qualquer CDS
SELECT customer_id, year_month, total_amount, classification
FROM ztf_sales_balance( p_year = '2025', p_min_total = '1000.00' )
WHERE classification = 'PREMIUM'
INTO TABLE @DATA(lt_premium).// Empilhando outra CDS sobre a Table Function
define view entity ZC_PREMIUM_CUSTOMERS
as select from ZTF_SALES_BALANCE( p_year: '2025', p_min_total: 1000 )
{
key customer_id,
key year_month,
total_amount,
classification
}
where classification = 'PREMIUM';Integridade: OPTIONS READ-ONLY informa ao otimizador que não há side effects, liberando paralelismo seguro. E o filtro de cliente via SESSION_CONTEXT( 'CLIENT' ) é a forma correta em ambientes multi-client — nunca confie em sy-mandt dentro de SQLScript.
Como usar AMDP Scalar Functions para fórmulas reutilizáveis?
Uma Scalar Function retorna um único valor e pode ser usada onde quer que um valor seja esperado: SELECT-list, WHERE, ORDER BY, expressões de outras CDS. É a forma correta de encapsular uma fórmula reutilizável com pushdown completo ao HANA. Desde o ABAP 7.55 existem dois tipos: a AMDP Scalar Function clássica (chamável de ABAP via NEW classe( )->metodo( )) e a CDS Scalar Function (chamável dentro de CDS via define scalar function).
Quando USAR
Fórmula matemática/financeira repetida em muitos lugares.
Conversão de unidades, formatação de strings, classificação por faixas.
Para substituir uma cadeia longa de
CASE WHENespalhada por várias views.Quando precisa do valor calculado dentro de um
WHEREcom pushdown.Quando seria um Virtual Element, mas você quer pushdown ao HANA.
Quando NÃO usar
Cálculo usado em um único lugar — use expressão inline em CDS.
Cálculo que precisa de loop ou DML — use AMDP Procedure.
Quando depende de tabelas grandes não-indexadas — cuidado com execução row-by-row.
Lógica que exige contexto da sessão ABAP (autorizações dinâmicas, parâmetros de tela) — use Virtual Element.
Exemplo: cálculo de margem de lucro
A AMDP Scalar Function clássica, chamável de ABAP:
CLASS zcl_profit_functions DEFINITION
PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_amdp_marker_hdb.
CLASS-METHODS calc_margin_percent
IMPORTING
VALUE(iv_revenue) TYPE p LENGTH 15 DECIMALS 2
VALUE(iv_cost) TYPE p LENGTH 15 DECIMALS 2
RETURNING
VALUE(rv_margin) TYPE p LENGTH 5 DECIMALS 2.
ENDCLASS.
CLASS zcl_profit_functions IMPLEMENTATION.
METHOD calc_margin_percent
BY DATABASE FUNCTION
FOR HDB
LANGUAGE SQLSCRIPT
OPTIONS READ-ONLY DETERMINISTIC.
rv_margin = CASE
WHEN :iv_revenue = 0 OR :iv_revenue IS NULL THEN 0
ELSE ROUND(
( ( :iv_revenue - :iv_cost ) / :iv_revenue ) * 100,
2 )
END;
ENDMETHOD.
ENDCLASS.A versão CDS Scalar Function, consumível dentro de uma view — a definição DDL e o método AMDP que a implementa:
// 1) Definição da CDS Scalar Function (DDL)
define scalar function ZSF_CALC_MARGIN
with parameters
revenue : abap.curr(15,2),
cost : abap.curr(15,2)
returns abap.dec(5,2)
implementation
for hdb
by method zcl_profit_functions=>calc_margin_for_cds;" 2) Método AMDP que implementa
CLASS-METHODS calc_margin_for_cds
FOR SCALAR FUNCTION zsf_calc_margin.
METHOD calc_margin_for_cds
BY DATABASE FUNCTION
FOR HDB
LANGUAGE SQLSCRIPT
OPTIONS READ-ONLY DETERMINISTIC.
margin = CASE
WHEN :revenue = 0 OR :revenue IS NULL THEN 0
ELSE ROUND( ( ( :revenue - :cost ) / :revenue ) * 100, 2 )
END;
ENDMETHOD.E o uso — pushdown completo dentro de uma CDS View, inclusive no filtro WHERE:
@EndUserText.label: 'Análise de Margem'
define view entity ZC_PRODUCT_MARGIN
as select from zproduct_sales
{
key product_id,
revenue,
cost,
ZSF_CALC_MARGIN( revenue => revenue,
cost => cost ) as margin_percent
}
where ZSF_CALC_MARGIN( revenue => revenue,
cost => cost ) > 20;" Em ABAP, chamando a AMDP Scalar diretamente
DATA(lv_margin) = NEW zcl_profit_functions( )->calc_margin_percent(
iv_revenue = '10000.00'
iv_cost = '7500.00' ).O flag DETERMINISTIC é crítico. Ele diz ao HANA que a mesma entrada sempre gera a mesma saída, liberando cache e paralelismo. Use somente se a função for de fato determinística — não consulta tabelas que mudam, não usa CURRENT_TIMESTAMP. Marcar como determinística uma função que não é leva a resultados incorretos cacheados.
Como usar Virtual Elements (e por que são o último recurso)?
Virtual Elements são campos calculados em CDS que não existem fisicamente na tabela. Tradicionalmente são calculados em ABAP no Application Server, depois do SELECT — o que viola o princípio de code pushdown. Existem duas variantes: o clássico (ABAP exit), que usa a interface IF_SADL_EXIT_CALC_ELEMENT_READ, e o via Scalar Function, que empurra o cálculo para o HANA (preferível sempre que possível).
Quando USAR o Virtual Element clássico
O cálculo depende de lógica ABAP pura que não cabe em SQLScript: chamadas a Function Modules/BAPIs, conversões via classes utility, acesso a configuração na memória da sessão, ou autorização dinâmica que depende do usuário.
O cálculo precisa do contexto do request OData (parâmetros, sessão Fiori).
O resultset é pequeno — centenas ou poucos milhares de linhas.
Quando NÃO usar
Quando o cálculo cabe em expressões CDS,
CASEou Scalar Functions.Em listas grandes (List Reports com milhares de linhas) — performance catastrófica.
Para filtros:
WHEREno Fiori não funciona em virtual elements sem suporte adicional.Em ordenação: ordenar exige materializar todo o resultset.
Em aplicações analíticas com agregação.
Performance: virtual elements rodam um LOOP AT no Application Server para cada linha. Numa List Report com 10.000 linhas, isso são 10.000 iterações ABAP depois que o banco já devolveu os dados — um efeito devastador.
O Virtual Element clássico (ABAP exit)
A definição na CDS marca os campos com @ObjectModel.virtualElement e aponta para a classe que os calcula:
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Pedidos com status calculado'
define root view entity ZC_SALES_ORDERS_VE
as select from vbak
association [0..*] to vbap as _items on $projection.vbeln = _items.vbeln
{
key vbeln as SalesOrder,
kunnr as Customer,
erdat as CreatedOn,
netwr as NetValue,
waerk as Currency,
@ObjectModel.virtualElement: true
@ObjectModel.virtualElementCalculatedBy: 'ABAP:ZCL_SO_VIRTUAL_STATUS'
@EndUserText.label: 'Status Customizado'
cast( '' as abap.char(20) ) as CustomStatus,
@ObjectModel.virtualElement: true
@ObjectModel.virtualElementCalculatedBy: 'ABAP:ZCL_SO_VIRTUAL_STATUS'
@EndUserText.label: 'Dias em Aberto'
cast( 0 as abap.int4 ) as DaysOpen,
_items
}E a classe implementa dois métodos da interface — um para declarar dependências, outro para calcular:
CLASS zcl_so_virtual_status DEFINITION
PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_sadl_exit_calc_element_read.
ENDCLASS.
CLASS zcl_so_virtual_status IMPLEMENTATION.
METHOD if_sadl_exit_calc_element_read~get_calculation_info.
" Declarar quais campos da view são NECESSÁRIOS para o cálculo.
" Isso permite ao framework otimizar a query (só seleciona o que se usa).
LOOP AT it_requested_calc_elements INTO DATA(lv_field).
CASE lv_field.
WHEN 'CUSTOMSTATUS'.
APPEND 'NETVALUE' TO et_requested_orig_elements.
APPEND 'CURRENCY' TO et_requested_orig_elements.
WHEN 'DAYSOPEN'.
APPEND 'CREATEDON' TO et_requested_orig_elements.
ENDCASE.
ENDLOOP.
SORT et_requested_orig_elements.
DELETE ADJACENT DUPLICATES FROM et_requested_orig_elements.
ENDMETHOD.
METHOD if_sadl_exit_calc_element_read~calculate.
DATA lt_data TYPE STANDARD TABLE OF ZC_SALES_ORDERS_VE WITH EMPTY KEY.
lt_data = CORRESPONDING #( it_original_data ).
LOOP AT lt_data REFERENCE INTO DATA(lr_row).
" Cálculo 1: status customizado
lr_row->CustomStatus = COND #(
WHEN lr_row->NetValue >= 100000 THEN 'STRATEGIC'
WHEN lr_row->NetValue >= 10000 THEN 'IMPORTANT'
ELSE 'STANDARD' ).
" Cálculo 2: dias em aberto (depende da data corrente — contexto)
lr_row->DaysOpen = sy-datum - lr_row->CreatedOn.
ENDLOOP.
ct_calculated_data = CORRESPONDING #( lt_data ).
ENDMETHOD.
ENDCLASS.Crítico para integridade: o método GET_CALCULATION_INFO precisa listar todos os campos originais necessários. Se esquecer um, ele virá vazio em it_original_data e o cálculo produzirá dados incorretos silenciosamente — sem nenhum erro.
A versão recomendada: trocar por Scalar Function
O mesmo exemplo pode (e deve) ser reescrito para rodar no HANA. O resultado: zero loops no Application Server, e ordenação e filtros funcionando nativamente.
// 1) Scalar Function para o status
define scalar function ZSF_SO_STATUS
with parameters net_value : abap.curr(15,2)
returns abap.char(20)
implementation for hdb
by method zcl_so_status_amdp=>get_status;
// 2) CDS View sem virtual element — pushdown total
define view entity ZC_SALES_ORDERS_FAST
as select from vbak
{
key vbeln as SalesOrder,
kunnr as Customer,
erdat as CreatedOn,
netwr as NetValue,
waerk as Currency,
ZSF_SO_STATUS( net_value => netwr ) as CustomStatus,
dats_days_between( erdat, $session.system_date ) as DaysOpen
}Matriz de decisão: qual escolher?
Quando a dúvida bater, esta tabela resolve a maioria dos casos. A regra mental é simples: precisa de tabela? Table Function. Precisa de valor reutilizável? Scalar Function. Precisa de ABAP de verdade no cálculo e o resultset é pequeno? Virtual Element.
Característica | Table Function | Scalar Function | Virtual Element (ABAP) |
|---|---|---|---|
Retorna tabela | ✅ | ❌ | ❌ |
Retorna valor único | ❌ | ✅ | ✅ (por linha) |
Executa no HANA | ✅ | ✅ | ❌ (AS) |
Code pushdown | ✅ | ✅ | ❌ |
Suporta filtros do Fiori | ✅ | ✅ | ⚠️ Limitado |
Suporta sort/order | ✅ | ✅ | ❌ |
Pode chamar FM/BAPI ABAP | ❌ | ❌ | ✅ |
Acessa contexto da sessão ABAP | ⚠️ Limitado | ⚠️ Limitado | ✅ |
Permite DML (write) | ❌ | ❌ | ❌ |
Reusável em outras CDS | ✅ | ✅ | ⚠️ Via projeção |
Performance em massa | 🟢 Excelente | 🟢 Excelente | 🔴 Ruim |
Em forma de árvore de decisão:
Preciso calcular um campo/dataset em CDS?
│
├─ É uma tabela com lógica SQL complexa?
│ └─ SIM → CDS Table Function + AMDP
│
├─ É um valor escalar reutilizado em vários lugares?
│ └─ SIM → Scalar Function (AMDP ou CDS)
│
├─ Preciso de FM/BAPI/classe ABAP no cálculo?
│ └─ SIM → Virtual Element (ABAP exit) — APENAS se o resultset for pequeno
│
└─ A lógica cabe em CDS puro com CASE/expressões?
└─ SIM → CDS puro (preferência absoluta)Como garantir a integridade dos dados em AMDP?
SQLScript não é ABAP, e essa diferença é a origem de quase todos os bugs sutis de AMDP. Sete cuidados resolvem a maioria deles.
1. Client handling (multi-tenancy)
O sy-mandt não existe dentro do SQLScript. Use SESSION_CONTEXT( 'CLIENT' ) ou a keyword CLIENT:
" ❌ ERRADO — não funciona em AMDP
WHERE mandt = sy-mandt
" ✅ CORRETO em Table Function client-dependent
WHERE mandt = SESSION_CONTEXT( 'CLIENT' )
" ✅ CORRETO passando como parâmetro implícito (keyword CLIENT)
METHOD get_data BY DATABASE FUNCTION FOR HDB LANGUAGE SQLSCRIPT
USING vbak CLIENT clnt.
RETURN SELECT * FROM vbak WHERE mandt = :clnt;
ENDMETHOD.2. Determinismo
Use OPTIONS DETERMINISTIC em Scalar Functions somente quando a função não consulta tabelas mutáveis, não usa funções de tempo corrente e não depende de configuração que muda em runtime. Marcar errado = resultados incorretos cacheados.
3. NULL handling
Em SQLScript, NULL + 5 = NULL, não 5. Sempre proteja agregações e comparações com COALESCE ou IFNULL:
-- ❌ Risco de NULL silencioso
SUM( amount )
-- ✅ Garantido
SUM( COALESCE( amount, 0 ) )
-- ✅ Em comparações
CASE WHEN COALESCE( status, '' ) = 'OPEN' THEN ...4. READ-ONLY
Sempre use OPTIONS READ-ONLY em Table/Scalar Functions. Sem isso, o HANA não pode otimizar nem paralelizar — e ainda abre brecha para DML acidental.
5. Listagem completa de USING
Toda tabela ou view referenciada precisa estar na cláusula USING. Esquecer uma quebra em ambiente cloud/Steampunk e gera dependências invisíveis ao transporte:
METHOD get_balance BY DATABASE FUNCTION FOR HDB
LANGUAGE SQLSCRIPT
OPTIONS READ-ONLY
USING vbak vbap kna1 zconfig_table. " ← TODAS!6. GET_CALCULATION_INFO em Virtual Elements
Esquecer de declarar um campo dependente faz ele chegar vazio (initial) em it_original_data, gerando cálculo errado sem erro visível. Sempre teste com seleção parcial de campos (ex.: $select= no OData) para validar.
7. Currency e Quantity
Em AMDP, conversões de moeda não são automáticas como em CDS puro. Use a função padrão e defina sempre o error_handling:
CURRENCY_CONVERSION(
amount => netwr,
source_currency => waerk,
target_currency => 'USD',
exchange_rate_date => erdat,
error_handling => 'SET_TO_NULL'
)6 anti-padrões que destroem a performance
Loop dentro de AMDP. Um
FOR ... DO ... END FORcom SELECT por iteração anula todo o ganho do pushdown. Reescreva como SQL set-based, com joins e window functions.Virtual Element em List Report grande. Milhares de linhas = milhares de iterações ABAP. Troque por Scalar Function ou expressão CDS.
Table Function para uma chamada pontual. Criar function + classe AMDP para usar uma única vez é overengineering. Use Open SQL ou uma AMDP Procedure local.
Misturar AMDP com lógica de negócio. AMDP é para acesso a dados. Validações, autorização e workflows ficam em classes ABAP regulares ou na camada de comportamento RAP.
Esquecer
@ClientDependent. Sem essa annotation numa Table Function sobre tabela client-dependent, você transporta dados de todos os mandantes.Não testar com SQL Trace. Rode ST05 (SQL Trace) ou SAT (Runtime Analyzer) após criar a AMDP. Verifique quantas linhas o banco retorna, se o filtro foi pushed-down e o tempo por etapa.
Perguntas frequentes
AMDP ou CDS puro: por onde começo?
Sempre por CDS puro. A hierarquia de pushdown da SAP coloca CDS Views no topo e AMDP logo abaixo, só quando o CDS não expressa a lógica. AMDP traz poder de SQLScript, mas também custo de manutenção e dependência de HANA — não é o ponto de partida.
Por que meu Virtual Element deixou a List Report lenta?
Porque virtual elements clássicos rodam no Application Server, num LOOP AT por linha, depois que o banco já respondeu. Em listas de milhares de linhas, o efeito é devastador. A solução é reescrever o cálculo como Scalar Function ou expressão CDS, que rodam no HANA.
Quando devo marcar a função como DETERMINISTIC?
Só quando a mesma entrada sempre produzir a mesma saída: nada de tabelas que mudam, CURRENT_TIMESTAMP ou configuração de runtime. O flag libera cache e paralelismo no HANA, mas marcá-lo numa função não-determinística devolve resultados antigos cacheados — um bug difícil de rastrear.
Table Function pode fazer INSERT ou UPDATE?
Não. Table Functions e Scalar Functions são read-only por natureza, e o OPTIONS READ-ONLY reforça isso. Para DML (INSERT/UPDATE/DELETE), use uma AMDP Procedure ou a camada de comportamento RAP. Misturar leitura e escrita numa function quebra o modelo.
Como trato cliente (mandt) corretamente em AMDP?
Nunca com sy-mandt — ele não existe no SQLScript. Use SESSION_CONTEXT( 'CLIENT' ) no WHERE, ou a keyword CLIENT na assinatura do método para receber o mandante como parâmetro. E marque a Table Function com @ClientDependent: true.
Conclusão
AMDP não é sobre escrever SQLScript bonito — é sobre colocar cada cálculo no lugar certo. Comece pelo CDS puro. Suba para Table Function quando precisar de tabelas com lógica que o CDS não expressa, e para Scalar Function quando precisar de um valor reutilizável com pushdown. Deixe o Virtual Element clássico para o caso específico em que só o ABAP resolve e o resultset é pequeno. Proteja a integridade com READ-ONLY, DETERMINISTIC consciente, COALESCE e SESSION_CONTEXT — e valide tudo com ST05.
Guarde a matriz de decisão por perto: na próxima vez que pensar em criar um campo calculado, a primeira pergunta é sempre "isso roda no HANA?".
Referências oficiais
Comentários 0
Ainda sem comentários
Seja o primeiro a comentar este artigo.
Entre na conversa
Faça login para deixar seu comentário neste artigo.