Blog SAPienteSAPiente
AMDPCDS ViewsABAP Cloud

Como usar AMDP no S/4HANA: Table Functions, Scalar Functions e Virtual Elements

SAPiente9 de jun. de 2026· 5 min read

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 WHERE adicional não tem a mesma flexibilidade do CDS puro — o filtro vem do consumidor.

  • O cliente (mandt) é tratado manualmente, via SESSION_CONTEXT( 'CLIENT' ) ou a keyword CLIENT.

  • 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 WHEN espalhada por várias views.

  • Quando precisa do valor calculado dentro de um WHERE com 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, CASE ou Scalar Functions.

  • Em listas grandes (List Reports com milhares de linhas) — performance catastrófica.

  • Para filtros: WHERE no 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

  1. Loop dentro de AMDP. Um FOR ... DO ... END FOR com SELECT por iteração anula todo o ganho do pushdown. Reescreva como SQL set-based, com joins e window functions.

  2. Virtual Element em List Report grande. Milhares de linhas = milhares de iterações ABAP. Troque por Scalar Function ou expressão CDS.

  3. Table Function para uma chamada pontual. Criar function + classe AMDP para usar uma única vez é overengineering. Use Open SQL ou uma AMDP Procedure local.

  4. 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.

  5. Esquecer @ClientDependent. Sem essa annotation numa Table Function sobre tabela client-dependent, você transporta dados de todos os mandantes.

  6. 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

TagsDesenvolvedorHANA
Avalie este conteúdo
para avaliar
SAPiente
@sapiente

Blog sobre SAP, ABAP, BTP, Fiori e tudo que envolve o ecossistema SAP.

Ver perfil →
FacebookInstagramYouTubeTwitter

Comentários 0

Entre na conversa

Faça login para deixar seu comentário neste artigo.

Ainda sem comentários

Seja o primeiro a comentar este artigo.