CDS table function + AMDP
What you'll learn
When CDS can't express it, a table function over a read-only AMDP method can — but you must filter the client yourself with where mandt = :p_client.
- Use a table function + AMDP only when CDS can't express it (recursive CTE, window functions).
- AMDP read method: `by database function for hdb language sqlscript options read-only using ...`.
- `options read-only` enables routing/optimization; `using` must list every referenced object.
When CDS cannot express the logic — a recursive CTE, or a window function such as `row_number()` for ranking before CDS supported it — drop one rung to a CDS table function whose body is an AMDP method. The table function declares the returned structure and parameters in DDIC-like syntax; the AMDP method supplies the SQLScript that produces those rows. The function is then consumable from Open SQL and other CDS just like a view.
The AMDP read method is implemented `by database function for hdb language sqlscript options read-only using ...`. `options read-only` tells HANA the method does not write, which lets it route and optimize the call on scale-out systems; the `using` clause must name every database object the SQLScript touches so the dependency analyzer wires activation correctly. The body returns a SELECT — here with `row_number() over ( partition by ... order by ... )` to rank rows per customer.
The non-negotiable rule is client handling: AMDP has *no implicit MANDT injection* the way Open SQL does. You must filter `where mandt = :p_client` yourself, where `p_client` is a parameter annotated `@Environment.systemField: #CLIENT` so the framework fills it with the caller's client. Forget it and the method returns cross-client data — a defect ATC's AMDP checks catch, but only when those checks are enabled in your variant.
Key points
- Use a table function + AMDP only when CDS can't express it (recursive CTE, window functions).
- AMDP read method: `by database function for hdb language sqlscript options read-only using ...`.
- `options read-only` enables routing/optimization; `using` must list every referenced object.
- No implicit client — you must filter `where mandt = :p_client` explicitly.
- Bind the client parameter with `@Environment.systemField: #CLIENT` so the framework fills it.
Examples
Declares the returned columns and a client parameter bound to the system field; the body lives in an AMDP method.
ABAP@EndUserText.label: 'Order Ranking'
define table function ZI_OrderRanking
with parameters
@Environment.systemField: #CLIENT
p_client : abap.clnt
returns {
client : abap.clnt;
OrderId : zorder_id;
CustomerId : kunnr;
RankPerCust : abap.int4;
}
implemented by method zcl_order_ranking=>get;read-only, every table listed in using, and a window function — with the mandatory where mandt = :p_client because AMDP has no implicit client.
ABAPclass zcl_order_ranking definition public.
public section.
class-methods get for table function zi_orderranking.
endclass.
class zcl_order_ranking implementation.
method get by database function for hdb
language sqlscript
options read-only
using zorder_hdr.
return
select
mandt as client,
order_id as "OrderId",
kunnr as "CustomerId",
row_number() over ( partition by kunnr order by total desc )
as "RankPerCust"
from zorder_hdr
where mandt = :p_client;
endmethod.
endclass.Source notes: clean-core-curriculum §6.3
Ask Claude
Build a prompt from this lesson + your question and open a fresh Claude chat with it pre-filled — handy for adapting a before/after pattern to your own object.