The SAP Data Quirks Guide
SAP data makes a first extract fail in quiet ways: a missing client key joins tenants together, a numeric cast erases an identifier, and a classic table name can still answer reads after its physical rows moved. These eight patterns are the shortest path to a lakehouse model that reconciles, with copy-ready Databricks SQL. Last verified August 2026.
Snippets read the replicated source-aligned layer at <catalog>.<schema>. Replace every highlighted parameter before running them, and confirm field-level semantics in your system's DDIC because customer extensions and release differences are real.
MANDT is part of the data, not plumbing
In a client-dependent DDIC table, a leading CLNT field makes the table client-dependent; in the application tables here, that field is normally MANDT. SAP's application layer injects the logged-in client automatically. A raw lakehouse query does not. That means MANDT belongs in the bronze key, the silver key, the filter, and every join between two client-dependent tables.
Do not add MANDT mechanically to client-independent configuration tables that do not carry it. Read the key flags on each table page; the rule is metadata-driven, not naming folklore.
3 parameters not filled: <catalog>, <schema>, <client>
-- MANDT is the first key field on client-dependent application tables.
-- Filter it at the read boundary AND carry it through every join.
SELECT
h.MANDT,
h.EBELN,
i.EBELP,
i.MATNR,
i.MENGE,
i.MEINS
FROM <catalog>.<schema>.ekko h
JOIN <catalog>.<schema>.ekpo i
ON i.MANDT = h.MANDT
AND i.EBELN = h.EBELN
WHERE h.MANDT = '<client>';
-- Wrong: ON i.EBELN = h.EBELN
-- It looks fine in a one-client sandbox and fans out as soon as the landed
-- schema contains the same purchasing document number in another client.Identifiers are strings — keep the zeros
SAP uses numeric-looking text for document years and line items, and conversion exits such as ALPHA present fixed-width internal identifiers without their padding in the UI. NUMC is character data with numeric intent, not an integer. Business keys such as VBELN, EBELN, and MATNR are also character fields even when every value in a sample happens to contain digits.
Preserve source width in bronze. Normalize incoming search values to the source's internal form, and join stored values directly. This also protects alphanumeric keys and the S/4HANA material-number extension that widened MATNR beyond its classic length.
6 parameters not filled: <catalog>, <schema>, <client>, <sales_document>, <external_material>, <ddic_length>
-- Keep SAP identifiers as strings in bronze and silver.
-- Apply display-to-internal formatting to the INPUT, never by casting the
-- stored column to a number and back.
SELECT
v.VBELN,
p.POSNR,
p.MATNR
FROM <catalog>.<schema>.vbak v
JOIN <catalog>.<schema>.vbap p
ON p.MANDT = v.MANDT
AND p.VBELN = v.VBELN
WHERE v.MANDT = '<client>'
AND v.VBELN = lpad('<sales_document>', 10, '0');
-- Material number width is system/version dependent (18 in classic ECC;
-- S/4HANA supports 40). Read the DDIC length for your source before using:
-- lpad('<external_material>', <ddic_length>, '0')
-- Never CAST POSNR, GJAHR, BUZEI, BELNR, VBELN, EBELN, or MATNR to BIGINT.Dates and times arrive as character fields
DDIC DATS carries YYYYMMDD; TIMS carries HHMMSS. Their initial values are 00000000 and 000000, so a direct SQL cast can fail or manufacture a misleading value. Parse once in silver and keep the raw columns available for reconciliation.
A date and time pair does not prove UTC. Classic fields often reflect application-server or business-local time, while newer frameworks may carry explicit UTC timestamps. Add a timezone only when the field or extraction interface documents one.
3 parameters not filled: <catalog>, <schema>, <client>
-- DATS and TIMS are fixed-width character semantics in raw extracts.
-- Convert the zero-filled INITIAL date to NULL before parsing.
SELECT
b.BUKRS,
b.BELNR,
b.GJAHR,
to_date(nullif(b.BLDAT, '00000000'), 'yyyyMMdd') AS document_date,
to_date(nullif(b.BUDAT, '00000000'), 'yyyyMMdd') AS posting_date
FROM <catalog>.<schema>.bkpf b
WHERE b.MANDT = '<client>';
-- When a table carries a DATS/TIMS pair, keep the date guard and preserve
-- 000000 as a valid midnight time once the date itself is present.
CASE
WHEN t.ERDAT = '00000000' THEN NULL
ELSE to_timestamp(concat(t.ERDAT, lpad(t.ERZET, 6, '0')), 'yyyyMMddHHmmss')
END AS created_at_source_time
-- Do not append a timezone unless that field's documentation states one.
-- Many classic application stamps reflect the SAP application server time.Every amount and quantity needs its unit
DDIC CURR and QUAN fields have reference fields of type CUKY and UNIT. The reference can sit on the same table or on a related header/configuration table. Carry it into the fact row before aggregation: 100 USD and 100 JPY are two facts, not 200 currency.
Unit and currency configuration also controls presentation and decimal interpretation. Keep source values exact, group by the source unit, then apply governed conversion rates in a separate semantic layer. The catalog's field metadata tells you which columns are amounts, quantities, currencies, and units.
3 parameters not filled: <catalog>, <schema>, <client>
-- An amount without its currency and a quantity without its unit are
-- incomplete facts. Keep the reference fields in the same row and group.
SELECT
k.WAERS AS document_currency,
sum(s.WRBTR) AS amount_document_currency
FROM <catalog>.<schema>.bkpf k
JOIN <catalog>.<schema>.bseg s
ON s.MANDT = k.MANDT
AND s.BUKRS = k.BUKRS
AND s.BELNR = k.BELNR
AND s.GJAHR = k.GJAHR
WHERE k.MANDT = '<client>'
GROUP BY k.WAERS;
SELECT
e.MEINS AS order_unit,
sum(e.MENGE) AS ordered_quantity
FROM <catalog>.<schema>.ekpo e
WHERE e.MANDT = '<client>'
GROUP BY e.MEINS;
-- Convert currencies or units only after preserving this grain. Never SUM
-- mixed WAERS or MEINS values into one unlabeled number.Text tables multiply rows by language
SAP separates translatable descriptions from the business object. A text table's primary key adds a language field—commonly SPRAS—to the foreign key. Joining MAKT to MARA without choosing a language silently multiplies each material by every maintained translation.
Decide whether your warehouse stores one reporting language or a multilingual text dimension. If you need fallback behavior, rank preferred and fallback languages explicitly; do not rely on an arbitrary first() row.
4 parameters not filled: <catalog>, <schema>, <language_key>, <client>
-- MAKT is keyed by client + material + language. Choose one language
-- before joining descriptions onto the material master.
SELECT
m.MATNR,
t.MAKTX
FROM <catalog>.<schema>.mara m
LEFT JOIN <catalog>.<schema>.makt t
ON t.MANDT = m.MANDT
AND t.MATNR = m.MATNR
AND t.SPRAS = '<language_key>'
WHERE m.MANDT = '<client>';
-- A safer multilingual model keeps the text table at its native grain:
SELECT MANDT, MATNR, SPRAS, MAKTX
FROM <catalog>.<schema>.makt
WHERE MANDT = '<client>';
-- Join that dimension only after the consuming model has chosen a language.
-- The internal SAP language key is not necessarily the two-letter code your
-- presentation layer uses, so map it explicitly rather than guessing.A document number is rarely the document key
The number printed on a document is optimized for people, not for a global database key. In Financial Accounting, BELNRrepeats across company codes and fiscal years. In Materials Management, a material document needs MBLNR plus MJAHR, with ZEILE at item grain. Client is part of both when the table is client-dependent.
Treat the cataloged/DDIC key flags as executable modeling metadata. They define deduplication, CDC merge keys, uniqueness tests, and joins. A relationship diagram is a starting point; the full key is the contract.
3 parameters not filled: <catalog>, <schema>, <client>
-- Accounting document numbers repeat by company code and fiscal year.
-- The complete BKPF -> BSEG join also carries client and line-item grain.
SELECT
k.BUKRS,
k.BELNR,
k.GJAHR,
s.BUZEI,
s.HKONT,
s.WRBTR,
k.WAERS
FROM <catalog>.<schema>.bkpf k
JOIN <catalog>.<schema>.bseg s
ON s.MANDT = k.MANDT
AND s.BUKRS = k.BUKRS
AND s.BELNR = k.BELNR
AND s.GJAHR = k.GJAHR
WHERE k.MANDT = '<client>';
-- Material-document grain is also composite:
-- MANDT + MBLNR + MJAHR + ZEILE
-- Treat the key flags in DDIC/catalog metadata as the contract. The visible
-- document number is a label until every key column is present.Initial is not the same thing as NULL
ABAP types have initial values: spaces for many character fields, zeros for numeric types, and zero-filled date/time strings. Some DDIC fields may still permit database nulls, so the safe rule is not “SAP has no nulls.” The safe rule is to model both database null and the field's documented initial value.
Normalize only when the business semantics agree. A blank deletion indicator usually means “not deleted”; zero quantity may be real;000000 can be midnight. Preserve raw values and add semantic columns instead of erasing the distinction globally.
3 parameters not filled: <catalog>, <schema>, <client>
-- Normalize SAP INITIAL values only where the field semantics justify it.
SELECT
r.BANFN,
r.BNFPO,
nullif(trim(r.LOEKZ), '') AS deletion_indicator,
to_date(nullif(r.ERDAT, '00000000'), 'yyyyMMdd') AS requisition_date,
nullif(r.MATNR, '') AS material_number,
r.MENGE, -- zero can be a real quantity
r.MEINS
FROM <catalog>.<schema>.eban r
WHERE r.MANDT = '<client>';
-- Do not run blanket NULLIF(column, 0) across numeric fields. Zero is a valid
-- amount, quantity, status, or counter in many domains. Likewise, a blank
-- indicator can mean the active/default state rather than missing data.The classic name may no longer be the storage
S/4HANA preserves compatibility for applications while simplifying physical persistence. MSEG and MKPF can still answer classic reads through compatibility views while new material-document persistence lives in MATDOC. Stock quantities exposed through tables such as MARD can be calculated through NSDM proxy views rather than stored in the classic physical table.
Finance is equally precise: ACDOCA is the Universal Journal and the primary reporting source for many actuals, while BKPF and BSEG still serve header and entry view/open-item needs. “Everything moved to ACDOCA” is as dangerous as ignoring ACDOCA.
Use the lifecycle and access badges on each table page, then choose a released CDS view where its business semantics fit. The extraction guide explains why readable compatibility names and CDC sources are not interchangeable; the CDS catalog tracks released views and edition-specific release states.
3 parameters not filled: <catalog>, <schema>, <client>
-- S/4HANA can preserve a familiar classic name as a compatibility or proxy
-- view while the physical write moves elsewhere. Point CDC at persistence.
SELECT
d.MANDT,
d.MBLNR,
d.MJAHR,
d.ZEILE,
d.MATNR,
d.WERKS,
d.LGORT,
d.BWART,
d.MENGE,
d.MEINS
FROM <catalog>.<schema>.matdoc d
WHERE d.MANDT = '<client>';
-- Do not UNION MATDOC with MSEG in S/4HANA: the classic MSEG read is a
-- compatibility projection over the new material-document model.
--
-- Finance is nuanced rather than universal:
-- ACDOCA = Universal Journal / G/L view and most actual CO reporting
-- BKPF = accounting document header; it remains
-- BSEG = entry view and open-item/source-document detail; it remains
-- Choose the physical or released CDS source that matches the business view,
-- and verify the release state for your S/4HANA edition before production.Sources & verification
These patterns are grounded in SAP's DDIC type semantics and S/4HANA documentation, then checked against the field keys and lifecycle notes in this reference. Source-system configuration and release level still win; verify in DDIC, View Browser, or ADT before turning a pattern into a production contract.
- SAP ABAP Keyword Documentation — special character-like DDIC types (opens in new tab) — NUMC, CLNT/client dependency, LANG, and text-table key semantics.
- SAP ABAP Dictionary — initial values (opens in new tab) — type-specific initial values including DATS, TIMS, NUMC, and CLNT.
- SAP ABAP Dictionary — currency and quantity reference fields (opens in new tab) — the required CUKY/UNIT relationship for CURR and QUAN fields.
- SAP S/4HANA — Universal Journal FAQ (opens in new tab) — what ACDOCA contains and why BKPF/BSEG still remain.
- SAP S/4HANA Simplification List — material documents and NSDM (opens in new tab) — MATDOC persistence and classic compatibility-view behavior.