ABAP SELECT Patterns on S/4HANA — Real Queries from Working Reports

SELECT queries actually used in our custom Z reports, organized by use case. The code is taken verbatim from working sources. New cases are added whenever a new program is built.

Each entry: Case / Explanation (+pitfalls) / The actual query. Last updated: 2026-08-13

CO — Standard Cost / Actuals — from ZCO0020 (standard cost vs actual)

▷ Standard cost header — latest released estimate (KEKO) — The latest released (FREIG='X') standard cost estimate (KALKA='01') by costing date (KADKY). SELECT SINGLE does not allow ORDER BY, so use UP TO 1 ROWS + ORDER BY instead

SELECT kalnr, kalka, kadky, tvers, bwvar, losgr, meins
  FROM keko
  WHERE matnr = @iv_matnr
    AND werks = @p_werks
    AND kalka = '01'
    AND tvers = '01'
    AND freig = 'X'
  ORDER BY kadky DESCENDING
  INTO @DATA(ls_keko)
  UP TO 1 ROWS.
ENDSELECT.

▷ Standard cost itemization (CKIS) — Explode the input lines using the estimate header keys (KALNR/KALKA/KADKY/TVERS/BWVAR). TYPPS: M = material, E = activity, I = other (e.g. subcontracting purchase price)

SELECT typps, kstar, matnr, kostl, lstar, menge, meeht, wertn
  FROM ckis
  WHERE kalnr = @ls_keko-kalnr
    AND kalka = @ls_keko-kalka
    AND kadky = @ls_keko-kadky
    AND tvers = @ls_keko-tvers
    AND bwvar = @ls_keko-bwvar
  ORDER BY posnr
  INTO TABLE @DATA(lt_ckis).

▷ Production order consumption actuals (MSEG, FOR ALL ENTRIES) — Aggregate period consumption (261/262) and subcontracting components (543/544) for a list of production orders. FOR ALL ENTRIES silently removes fully duplicate rows, so the document key (MBLNR/MJAHR/ZEILE) MUST be in the SELECT list — otherwise identical lines from different documents collapse into one and totals go wrong (this is exactly why our subcontracting actuals once showed zero)

SELECT mblnr, mjahr, zeile, bwart, matnr, menge, meins, dmbtr
  FROM mseg
  FOR ALL ENTRIES IN @lt_aufnr
  WHERE aufnr = @lt_aufnr-aufnr
    AND bwart IN ('261','262','543','544')
    AND budat_mkpf BETWEEN @gv_dat_from AND @gv_dat_to
  INTO TABLE @DATA(lt_mseg).

▷ Goods receipt actuals for a product (MSEG) — 101/102 by material + plant — covers both production GRs and subcontracting (purchase order) GRs. 102 offsets with a -1 sign

SELECT mblnr, mjahr, bwart, menge, dmbtr, ebeln
  FROM mseg
  WHERE matnr = @iv_matnr
    AND werks = @p_werks
    AND bwart IN ('101','102')
    AND budat_mkpf BETWEEN @gv_dat_from AND @gv_dat_to
  INTO TABLE @DATA(lt_gr).

▷ Subcontracting processing fees (ACDOCA, KTOSL='FRL') — Subcontracting fees are stored in ACDOCA with account determination key FRL together with the parent material (MATNR) — sum directly by material + period

SELECT racct, hsl
  FROM acdoca
  WHERE rldnr  = '0L'
    AND rbukrs = @p_bukrs
    AND ktosl  = 'FRL'
    AND matnr  = @iv_matnr
    AND budat BETWEEN @gv_dat_from AND @gv_dat_to
  INTO TABLE @DATA(lt_frl).

▷ Activity actuals on production orders (ACDOCA) — Activity lines allocated to orders — on the receiver side LSTAR is empty and only the sender cost center (SCNTR) is filled, so identify them with SCNTR <> '' and match by cost element (RACCT)

SELECT belnr, docln, racct, scntr, msl, runit, hsl
  FROM acdoca
  FOR ALL ENTRIES IN @lt_aufnr
  WHERE rldnr  = '0L'
    AND rbukrs = @p_bukrs
    AND aufnr  = @lt_aufnr-aufnr
    AND scntr <> ''
    AND budat BETWEEN @gv_dat_from AND @gv_dat_to
  INTO TABLE @DATA(lt_activity).

CO — CO-PA / Allocation Cycles — from ZCO0030 · ZCO0041

▷ CO-PA line items — dynamic table name (CE1xxxx) — The table differs per operating concern (CE1OC10 / CE1JNC2), so SELECT with the table name in a variable. A PALEDGER='01' filter is mandatory — without it both valuation ledgers (01/02) come back and everything doubles. VRGAR='F' = actuals (billing-based)

SELECT bukrs, gjahr, perde, budat, kndnr, artnr, werks, vkorg, vtweg, spart,
       absmg, absmg_me, vv010, vv020, vv021, vv022, vv023, vv030, vv040, vv090,
       rec_waers, rbeln, rposn
  FROM (gv_ce1tab)
  WHERE bukrs    = @p_bukrs
    AND gjahr    = @lv_gjahr
    AND perde    = @lv_perde
    AND vrgar    = 'F'
    AND paledger = '01'
    AND werks    IN @s_werks
    AND kndnr    IN @s_kndnr
    AND artnr    IN @s_artnr
  INTO CORRESPONDING FIELDS OF TABLE @lt_ce1.

▷ Distribution cycles + segments (T811C ⨝ T811S) — Cycle headers and segments in one shot — ALART='V' (distribution), TAB='CCSS' (classic CO-OM). SRULE/RRULE/SPERCENT/RCDATA/NEGTEST hold the segment rules

SELECT c~cycle, c~sdate, c~edate,
       s~seqnr, s~name AS segname, s~srule, s~rrule, s~spercent,
       s~rcdata, s~negtest
  FROM t811c AS c
  INNER JOIN t811s AS s
    ON  s~tab   = c~tab
    AND s~cycle = c~cycle
    AND s~sdate = c~sdate
  WHERE c~tab   =  @gc_tab
    AND c~alart =  'V'
    AND c~sdate IN @s_sdate
  ORDER BY c~cycle, c~sdate, s~seqnr
  INTO CORRESPONDING FIELDS OF TABLE @gt_seg.

▷ Cycle → controlling area link (T811K SEQNR='0000') — T811C has no KOKRS field — the controlling area sits in the cycle-level selection criteria (T811K SEQNR='0000', FIELD='KOKRS') as VALMIN. Used for F4 helps and filters

SELECT k~valmin AS kokrs, c~cycle, c~sdate, l~txt
  FROM t811c AS c
  INNER JOIN t811k AS k
    ON  k~tab   = c~tab
    AND k~cycle = c~cycle
    AND k~sdate = c~sdate
    AND k~seqnr = '0000'
    AND k~field = 'KOKRS'
  LEFT OUTER JOIN t811l AS l
    ON  l~tab   = c~tab
    AND l~cycle = c~cycle
    AND l~sdate = c~sdate ...

▷ Segment criteria / receiver factors (T811K, T811F) — T811K criteria per SETKIND (0=cycle, 1=sender control, 2=sender, 3=receiver, 4=allocation base) and T811F receiver values per SRFLAG ('R'=portion/%, 'W'=weighting factor). Both use FOR ALL ENTRIES on the segment list

SELECT * FROM t811k
  FOR ALL ENTRIES IN @gt_seg
  WHERE tab   = @gc_tab
    AND cycle = @gt_seg-cycle
    AND sdate = @gt_seg-sdate
  INTO TABLE @gt_key.

SELECT * FROM t811f
  FOR ALL ENTRIES IN @gt_seg
  WHERE tab   = @gc_tab
    AND cycle = @gt_seg-cycle
    AND sdate = @gt_seg-sdate
  INTO TABLE @gt_fac.

FI — Financial Statements / Line Items — from ZFI0030 · ZFI0040

▷ P&L / BS balance aggregation (ACDOCA GROUP BY) — Period totals by account (RACCT) + functional area (RFAREA) — matched against the FSV item account ranges to build the financial statement tree. Note HSL stores credits (revenue) as negative

SELECT racct, rfarea, SUM( hsl ) AS amt
  FROM acdoca
  WHERE rldnr  = @p_rldnr
    AND rbukrs = @p_bukrs
    AND gjahr  = @p_gjahr
    AND poper BETWEEN @p_perf AND @p_pert
  GROUP BY racct, rfarea
  INTO TABLE @DATA(lt_rpt).

▷ G/L open items (BSIS_VIEW) — In S/4HANA a direct reference to BSIS is a syntax error — SELECT from the compatibility view BSIS_VIEW (cleared items: BSAS_VIEW). CDS views cannot anchor SELECT-OPTIONS though, so anchor them on SKB1/BKPF

SELECT bukrs, hkont, belnr, buzei, gjahr, blart, budat, bldat,
       shkzg, dmbtr, waers, zuonr, sgtxt
  FROM bsis_view
  WHERE hkont IN @s_hkont
    AND bukrs IN @s_bukrs
    AND budat IN @s_budat
    AND blart IN @s_blart
  INTO TABLE @DATA(lt_open).

Logistics — SD / MM — from ZSD0010 · ZMM0030 · ZCO0020

▷ Sales order list — header + item + customer name join (VBAK ⨝ VBAP) — S/4HANA removed the status tables (VBUK/VBUP), so status fields are read directly from VBAP — LFSTA (delivery) / FKSAA (billing) / GBSTA (overall). Customer name via LEFT OUTER JOIN to KNA1

SELECT a~vbeln, b~posnr, a~auart, a~vkorg, a~vtweg, a~spart,
       a~kunnr, c~name1, a~erdat, a~audat,
       b~matnr, b~arktx, b~werks, b~kwmeng, b~vrkme,
       b~netwr, a~waerk,
       b~lfsta, b~fksaa, b~gbsta, b~abgru
  FROM vbak AS a
  INNER JOIN vbap AS b
    ON b~vbeln = a~vbeln
  LEFT OUTER JOIN kna1 AS c
    ON c~kunnr = a~kunnr
  WHERE a~vbeln IN @s_vbeln
    AND a~vkorg IN @s_vkorg
    ...
  ORDER BY a~vbeln, b~posnr
  INTO CORRESPONDING FIELDS OF TABLE @gt_list.

▷ Has the PO been invoiced? (EKBE history, FOR ALL ENTRIES) — EKPO-EREKZ (final invoice flag) stays off for partial invoices, so it must not be used for this — judge by the existence of VGABE='2' (invoice receipt) lines in the PO history. GROUP BY is not allowed with FOR ALL ENTRIES, so dedupe with SORT + DELETE ADJACENT DUPLICATES

SELECT ebeln, ebelp
  FROM ekbe
  FOR ALL ENTRIES IN @gt_list
  WHERE ebeln = @gt_list-ebeln
    AND ebelp = @gt_list-ebelp
    AND vgabe = '2'
  INTO TABLE @DATA(lt_inv).

SORT lt_inv BY ebeln ebelp.
DELETE ADJACENT DUPLICATES FROM lt_inv COMPARING ebeln ebelp.

▷ Material list — three-way master join (MARC ⨝ MARA + MAKT) — Semi-finished/finished goods existing in a plant, with names — the basic pattern of starting from the plant view (MARC) and joining general data (MARA) and names (MAKT)

SELECT a~matnr, a~mtart, k~maktx
  FROM marc AS c
  INNER JOIN mara AS a ON a~matnr = c~matnr
  LEFT OUTER JOIN makt AS k ON k~matnr = a~matnr AND k~spras = @sy-langu
  WHERE c~werks = @p_werks
    AND a~mtart IN ('FERT','HALB')
    AND a~matnr IN @s_matnr
  ORDER BY a~matnr
  INTO TABLE @DATA(lt_mat).
Where these documents come from
Everything on this blog comes from one small dumpling factory built end-to-end in an S/4HANA sandbox — one plant, one product, one month, every document number real. The whole build is written up in two books.
· Free 16-page sample — the opening chapters, no email required
· How Sarah Joined a K-Dumpling Company and Became an SAP Genius — the business novel, 53 pages, $25
· The Dumpling Factory: Building a Complete SAP S/4HANA Company from Scratch — the build manual, 85 pages, $50

Comments

Popular posts from this blog

Modern ABAP Syntax Notes — Patterns and Pitfalls from Real Development

SAP S/4HANA Table Reference — Tables I Actually Query, by Topic

A 36% margin became 6% at month-end, and every posting was correct