SQL

Joining BKPF and BSEG without losing your mind

The four key fields that link an SAP accounting document header to its line items — and why order matters.

·3 min read·#sap#finance#sql

In SAP FI, an accounting document is split across two tables: BKPF holds the header (one row per document) and BSEG holds the line items (many rows per document). Get the join keys wrong and you either duplicate rows or silently drop postings from other company codes.

The four keys

A document is only unique across all four of these together:

  • MANDT — client
  • BUKRS — company code
  • BELNR — document number
  • GJAHR — fiscal year

Leaving out BUKRS or GJAHR is the classic mistake: BELNR is only unique within a company code and fiscal year, so a two-field join fans out across every company code that happens to reuse the same document number.

Header joined to line items on the full key
SELECT
  h.bukrs,
  h.belnr,
  h.gjahr,
  h.blart,   -- document type
  h.budat,   -- posting date
  l.buzei,   -- line item number
  l.hkont,   -- G/L account
  l.shkzg,   -- debit/credit indicator (S/H)
  l.dmbtr    -- amount in local currency
FROM bkpf AS h
JOIN bseg AS l
  ON  l.mandt = h.mandt
  AND l.bukrs = h.bukrs
  AND l.belnr = h.belnr
  AND l.gjahr = h.gjahr
WHERE h.bukrs = '1000'
  AND h.gjahr = '2026'
ORDER BY h.belnr, l.buzei;

Amounts are unsigned — read SHKZG

DMBTR is always stored as a positive number. The debit/credit direction lives in SHKZG (S = debit, H = credit). If you sum DMBTR directly you get gross turnover, not a net balance.

Signed balance per G/L account
SELECT
  l.hkont,
  SUM(CASE WHEN l.shkzg = 'H' THEN -l.dmbtr ELSE l.dmbtr END) AS net_local
FROM bkpf AS h
JOIN bseg AS l
  ON  l.mandt = h.mandt
  AND l.bukrs = h.bukrs
  AND l.belnr = h.belnr
  AND l.gjahr = h.gjahr
WHERE h.bukrs = '1000'
  AND h.gjahr = '2026'
GROUP BY l.hkont;

When not to use it

For reporting, don't reconstruct balances from BKPF/BSEG line by line — that's what ACDOCA (S/4) or the summary tables (GLT0, FAGLFLEXT in classic) are for. Reach for the header/line join when you need document-level detail: audit trails, drill-downs, or reconciling a specific posting.

Built with in Amsterdam( ) by Gravam