Mastering SAP Purchase Order Queries: EKKO and EKPO
Jan 26, 2025

Mastering SAP Purchase Order Queries: EKKO and EKPO

How to Query SAP Purchase Order Data Like a Pro

Master the art of extracting purchase order data from EKKO, EKPO, EKET, and EBAN tables with advanced SQL techniques and performance optimization strategies.

Whether you're an SAP MM consultant building custom reports or an ABAP developer optimizing slow-running programs, understanding how to efficiently query purchase order data is a critical skill. The SAP purchase order data model spans multiple interconnected tables—EKKO for headers, EKPO for line items, EKET for schedule lines, and EBAN for requisitions—and mastering their relationships unlocks powerful analytical capabilities.

In this comprehensive guide, we'll dive deep into the structure of SAP's core MM tables, explore real-world query patterns that professionals use daily, and reveal performance optimization techniques that can transform your data extraction from minutes to seconds. By the end, you'll have a professional toolkit for handling even the most complex purchase order scenarios.

Understanding the SAP Purchase Order Data Model

Before we jump into queries, let's establish a solid foundation. The SAP Materials Management (MM) module stores purchase order data across a hierarchical structure designed for maximum flexibility and auditability.

EKKO: The Purchase Order Header Table

EKKO (Purchasing Document Header) is the foundation of every purchase order. Each record represents a single PO document with its key identifier EBELN (Purchasing Document Number). This table contains vendor information, document type, creation date, and overall header-level data.

Key fields you'll use constantly:

  • EBELN – Purchase order number (primary key)
  • BUKRS – Company code
  • BSTYP – Document category (F=Purchase Order, A=Request for Quotation)
  • BSART – Document type (NB=Standard PO, UB=Stock Transport Order)
  • LIFNR – Vendor account number
  • EKORG – Purchasing organization
  • EKGRP – Purchasing group
  • BEDAT – Document date
  • AEDAT – Last change date

EKPO: Purchase Order Item Data

EKPO (Purchasing Document Item) stores line-level details for each purchase order. A single EKKO header can have multiple EKPO items, creating a one-to-many relationship. The combination of EBELN + EBELP (item number) forms the primary key.

Essential EKPO fields:

  • EBELN – Purchase order number (foreign key to EKKO)
  • EBELP – Item number
  • MATNR – Material number
  • WERKS – Plant
  • LGORT – Storage location
  • MENGE – Quantity ordered
  • MEINS – Unit of measure
  • NETPR – Net price
  • PEINH – Price unit
  • TXZ01 – Short text (material description)
  • LOEKZ – Deletion indicator

EKET: Schedule Line Data

EKET (Scheduling Agreement Schedule Lines) takes granularity one level deeper. Each EKPO item can have multiple delivery schedule lines, allowing for partial deliveries with different dates and quantities. The key is EBELN + EBELP + ETENR (schedule line number).

Critical EKET fields:

  • EBELN – Purchase order number
  • EBELP – Item number
  • ETENR – Delivery schedule line number
  • EINDT – Delivery date
  • MENGE – Scheduled quantity
  • WEMNG – Quantity of goods received
  • WAMNG – Quantity issued

EBAN: Purchase Requisition Data

EBAN (Purchase Requisition) is where the procurement cycle often begins. While not strictly part of the PO structure, understanding the requisition-to-PO connection is vital for end-to-end analysis. EBAN links to EKPO through the fields EBELN and EBELP stored in EBAN itself.

Key EBAN fields:

  • BANFN – Purchase requisition number
  • BNFPO – Item number of requisition
  • EBELN – Purchase order number (populated after PO creation)
  • EBELP – PO item number
  • MATNR – Material number
  • MENGE – Quantity requested
  • BADAT – Requisition date
  • LFDAT – Delivery date

Essential Query Patterns for SAP Purchase Orders

Now that we understand the data model, let's explore practical queries that SAP professionals use daily. These examples use Open SQL syntax (for ABAP) but translate easily to native SQL for database-level extraction.

Basic PO Header and Item Join

The most fundamental query combines EKKO and EKPO to retrieve complete purchase order information. This pattern forms the basis for nearly every PO report.

SELECT h~ebeln,
       h~bukrs,
       h~lifnr,
       h~ekorg,
       h~bedat,
       i~ebelp,
       i~matnr,
       i~txz01,
       i~werks,
       i~menge,
       i~netpr,
       i~meins
  FROM ekko AS h
  INNER JOIN ekpo AS i
    ON h~ebeln = i~ebeln
 WHERE h~bukrs = '1000'
   AND h~bedat BETWEEN '20250101' AND '20251231'
   AND h~bsart = 'NB'
   AND i~loekz = ''
 ORDER BY h~ebeln, i~ebelp.

Pro tip: Always filter on EKKO fields first (especially indexed fields like BUKRS and BEDAT) before applying EKPO conditions. This allows the database optimizer to reduce the dataset early, dramatically improving performance on large systems.

Finding Open PO Quantities with EKET

One of the most common business questions is "What's still outstanding on our purchase orders?" This requires joining to EKET and calculating the difference between ordered and received quantities.

SELECT h~ebeln,
       h~lifnr,
       i~ebelp,
       i~matnr,
       i~werks,
       s~etenr,
       s~eindt AS delivery_date,
       s~menge AS scheduled_qty,
       s~wemng AS received_qty,
       ( s~menge - s~wemng ) AS open_qty
  FROM ekko AS h
  INNER JOIN ekpo AS i
    ON h~ebeln = i~ebeln
  INNER JOIN eket AS s
    ON i~ebeln = s~ebeln
   AND i~ebelp = s~ebelp
 WHERE h~bukrs = '1000'
   AND s~eindt <= sy-datum
   AND ( s~menge - s~wemng ) > 0
   AND i~loekz = ''
 ORDER BY s~eindt, h~ebeln.

This query identifies overdue deliveries by filtering on eindt <= sy-datum (delivery date in the past) and checking for remaining open quantity. It's invaluable for expediting and vendor performance analysis.

Tracking Requisition-to-PO Conversion

Understanding the flow from requisition to purchase order requires linking EBAN to EKPO. This query shows which requisitions have been converted and which are still waiting.

SELECT r~banfn,
       r~bnfpo,
       r~matnr,
       r~menge AS req_quantity,
       r~badat AS req_date,
       r~lfdat AS requested_delivery,
       r~ebeln AS po_number,
       r~ebelp AS po_item,
       i~bedat AS po_date,
       CASE
         WHEN r~ebeln IS NULL OR r~ebeln = ''
           THEN 'Not Converted'
         ELSE 'Converted to PO'
       END AS conversion_status
  FROM eban AS r
  LEFT OUTER JOIN ekko AS h
    ON r~ebeln = h~ebeln
  LEFT OUTER JOIN ekpo AS i
    ON r~ebeln = i~ebeln
   AND r~ebelp = i~ebelp
 WHERE r~badat BETWEEN '20250101' AND '20251231'
   AND r~loekz = ''
 ORDER BY conversion_status, r~badat.

Using a LEFT OUTER JOIN ensures we capture requisitions that haven't yet been converted to POs. The CASE statement provides clear status reporting for stakeholders.

Aggregating PO Spend by Vendor

Financial analysis often requires rolling up item-level data to vendor totals. This query demonstrates proper value calculation including price units.

SELECT h~lifnr,
       v~name1 AS vendor_name,
       h~bukrs,
       h~ekorg,
       COUNT( DISTINCT h~ebeln ) AS po_count,
       COUNT( i~ebelp ) AS line_item_count,
       SUM( i~menge * i~netpr / i~peinh ) AS total_value,
       h~waers AS currency
  FROM ekko AS h
  INNER JOIN ekpo AS i
    ON h~ebeln = i~ebeln
  LEFT OUTER JOIN lfa1 AS v
    ON h~lifnr = v~lifnr
 WHERE h~bukrs = '1000'
   AND h~bedat BETWEEN '20250101' AND '20251231'
   AND h~bsart = 'NB'
   AND i~loekz = ''
 GROUP BY h~lifnr,
          v~name1,
          h~bukrs,
          h~ekorg,
          h~waers
HAVING SUM( i~menge * i~netpr / i~peinh ) > 0
 ORDER BY total_value DESCENDING.

Critical calculation: The formula i~menge * i~netpr / i~peinh properly handles SAP's price unit concept. For example, if a price is $10 per 100 units (peinh = 100), ordering 500 units should calculate as 500 * 10 / 100 = $50, not $5,000.

Advanced: Multi-Plant Delivery Analysis

In complex supply chain scenarios, you might need to analyze delivery performance across multiple plants and schedule lines. This query combines all four tables for comprehensive insight.

SELECT h~ebeln,
       h~lifnr,
       v~name1 AS vendor_name,
       i~ebelp,
       i~matnr,
       m~maktx AS material_desc,
       i~werks,
       t~name1 AS plant_name,
       s~etenr,
       s~eindt,
       s~menge,
       s~wemng,
       ( s~menge - s~wemng ) AS open_qty,
       CASE
         WHEN s~eindt < sy-datum AND ( s~menge - s~wemng ) > 0
           THEN 'Overdue'
         WHEN s~eindt = sy-datum AND ( s~menge - s~wemng ) > 0
           THEN 'Due Today'
         WHEN s~wemng >= s~menge
           THEN 'Completed'
         ELSE 'Future Delivery'
       END AS delivery_status,
       r~banfn AS source_requisition
  FROM ekko AS h
  INNER JOIN ekpo AS i
    ON h~ebeln = i~ebeln
  INNER JOIN eket AS s
    ON i~ebeln = s~ebeln
   AND i~ebelp = s~ebelp
  LEFT OUTER JOIN lfa1 AS v
    ON h~lifnr = v~lifnr
  LEFT OUTER JOIN makt AS m
    ON i~matnr = m~matnr
   AND m~spras = sy-langu
  LEFT OUTER JOIN t001w AS t
    ON i~werks = t~werks
  LEFT OUTER JOIN eban AS r
    ON i~ebeln = r~ebeln
   AND i~ebelp = r~ebelp
 WHERE h~bukrs = '1000'
   AND i~werks IN ( '1010', '1020', '1030' )
   AND s~eindt BETWEEN '20250101' AND '20250331'
   AND i~loekz = ''
 ORDER BY delivery_status, s~eindt, h~ebeln.

This professional-grade query includes master data joins (LFA1 for vendor names, MAKT for material descriptions, T001W for plant names) to produce a business-ready report without requiring additional lookups.

Performance Optimization Strategies

On production systems with millions of purchase orders, poorly written queries can bring the system to its knees. Here are proven optimization techniques that separate junior developers from seasoned professionals.

Leverage Database Indexes Effectively

SAP maintains standard indexes on MM tables, but you must structure your queries to use them. Check available indexes using transaction SE11 or DB02.

Key EKKO indexes:

  • Primary (0): EBELN
  • EKK (index name may vary): BUKRS + BSTYP + BEDAT
  • Additional: LIFNR, EKORG, etc.

Best practice: Always include BUKRS in your WHERE clause when querying EKKO—this field is part of secondary indexes and dramatically reduces the search space. Similarly, filtering on BEDAT (document date) helps the optimizer use date-range indexes.

Use FOR ALL ENTRIES Wisely

The ABAP FOR ALL ENTRIES construct can be powerful but dangerous. It works well with small internal tables (under 1,000 entries) but degrades rapidly with larger datasets due to excessive database round-trips.

DATA: lt_ebeln TYPE TABLE OF ebeln.

" First, get relevant PO numbers with restrictions
SELECT ebeln
  FROM ekko
  INTO TABLE lt_ebeln
 WHERE bukrs = '1000'
   AND bedat BETWEEN '20250101' AND '20251231'
   AND bsart = 'NB'.

" Then fetch items only for those POs
IF lt_ebeln IS NOT INITIAL.
  SELECT ebeln, ebelp, matnr, menge, netpr
    FROM ekpo
    INTO TABLE @DATA(lt_items)
     FOR ALL ENTRIES IN @lt_ebeln
   WHERE ebeln = @lt_ebeln-ebeln
     AND loekz = ''.
ENDIF.

Critical safety check: Always verify IF lt_ebeln IS NOT INITIAL before using FOR ALL ENTRIES. An empty internal table causes the WHERE condition to be ignored, returning all records from the database—a catastrophic performance issue.

Project Only Required Fields

Avoid using SELECT * at all costs. EKPO contains over 130 fields; transferring unnecessary data across the network wastes bandwidth and memory.

" Bad: Retrieves all 130+ fields
SELECT * FROM ekpo INTO TABLE @DATA(lt_items).

" Good: Projects only needed fields
SELECT ebeln, ebelp, matnr, werks, menge, netpr
  FROM ekpo
  INTO TABLE @DATA(lt_items)
 WHERE ebeln = @lv_po_number.

On a system with 10 million PO items, selecting all fields might transfer 50 GB of data, while projecting 6 fields reduces this to under 2 GB—a 25x improvement.

Consider Database Views for Repeated Patterns

If your organization repeatedly queries the same EKKO/EKPO/EKET combination, create a database view (transaction SE11) or CDS view (for S/4HANA). This centralizes join logic and allows the database to pre-optimize execution plans.

@AbapCatalog.sqlViewName: 'ZVPO_OPEN_ITEMS'
@EndUserText.label: 'Open PO Items with Schedule Lines'
define view Z_PO_OPEN_ITEMS
  as select from ekko as h
    inner join ekpo as i on h.ebeln = i.ebeln
    inner join eket as s on  i.ebeln = s.ebeln
                         and i.ebelp = s.ebelp
{
  h.ebeln,
  h.bukrs,
  h.lifnr,
  h.bedat,
  i.ebelp,
  i.matnr,
  i.werks,
  s.etenr,
  s.eindt,
  s.menge,
  s.wemng,
  s.menge - s.wemng as open_quantity
}
where i.loekz = ''
  and s.menge > s.wemng;

CDS views in S/4HANA provide additional benefits like annotations for OData exposure, analytical capabilities, and integration with SAP Fiori applications.

Parallel Processing for Large Volumes

When processing millions of records, leverage ABAP's parallel processing capabilities to distribute work across multiple application servers.

DATA: lt_ebeln TYPE TABLE OF ebeln.

" Get all POs to process
SELECT ebeln
  FROM ekko
  INTO TABLE lt_ebeln
 WHERE bukrs = '1000'
   AND bedat BETWEEN '20250101' AND '20251231'.

" Split into packages and process in parallel
CALL FUNCTION 'Z_PROCESS_PO_ITEMS'
  STARTING NEW TASK 'TASK1'
  CALLING process_complete ON END OF TASK
  EXPORTING
    it_po_numbers = lt_ebeln[1 TO 10000].

CALL FUNCTION 'Z_PROCESS_PO_ITEMS'
  STARTING NEW TASK 'TASK2'
  CALLING process_complete ON END OF TASK
  EXPORTING
    it_po_numbers = lt_ebeln[10001 TO 20000].

This pattern can reduce processing time from hours to minutes on multi-core application servers, though it requires careful handling of shared resources and commit work.

Common Pitfalls and How to Avoid Them

Ignoring Deletion Indicators

SAP uses soft deletes extensively. Always filter on LOEKZ (deletion indicator) in EKPO unless you specifically need deleted items. Similarly, check FRGKE (release indicator) if you only want released POs.

Incorrect Price Calculations

The biggest mistake beginners make is calculating value as MENGE * NETPR without accounting for PEINH. This can cause 100x or 1000x errors in financial reporting.

Missing Schedule Line Aggregation

When joining EKPO to EKET, remember that one item can have multiple schedule lines. If you're calculating totals, aggregate at the schedule line level first, then roll up.

" Aggregate schedule lines to item level
SELECT ebeln,
       ebelp,
       SUM( menge ) AS total_scheduled,
       SUM( wemng ) AS total_received
  FROM eket
  INTO TABLE @DATA(lt_schedule_totals)
 GROUP BY ebeln, ebelp.

Forgetting Document Types

EKKO contains more than just purchase orders. Filter on BSTYP = 'F' for purchase orders specifically, or you'll include contracts (K), requests for quotation (A), and scheduling agreements (L) in your results.

Real-World Scenarios and Solutions

Scenario 1: Vendor Performance Scorecard

Your procurement team needs a monthly scorecard showing on-time delivery rates by vendor. This requires comparing EKET scheduled dates with actual goods receipt dates from MKPF/MSEG.

SELECT h~lifnr,
       COUNT( DISTINCT s~ebeln ) AS total_pos,
       SUM( CASE WHEN m~budat <= s~eindt THEN 1 ELSE 0 END ) AS on_time,
       SUM( CASE WHEN m~budat > s~eindt THEN 1 ELSE 0 END ) AS late,
       CAST( SUM( CASE WHEN m~budat <= s~eindt THEN 1 ELSE 0 END ) AS FLOAT ) /
       CAST( COUNT( * ) AS FLOAT ) * 100 AS on_time_percentage
  FROM ekko AS h
  INNER JOIN ekpo AS i ON h~ebeln = i~ebeln
  INNER JOIN eket AS s ON i~ebeln = s~ebeln AND i~ebelp = s~ebelp
  INNER JOIN mseg AS m ON i~ebeln = m~ebeln AND i~ebelp = m~ebelp
  INNER JOIN mkpf AS d ON m~mblnr = d~mblnr AND m~mjahr = d~mjahr
 WHERE h~bukrs = '1000'
   AND d~budat BETWEEN '20250101' AND '20250131'
   AND m~bwart = '101'  " Goods receipt
 GROUP BY h~lifnr
 ORDER BY on_time_percentage DESCENDING.

Scenario 2: Maverick Spend Detection

Identify purchases made without requisitions (maverick spend) by finding PO items with no corresponding EBAN record.

SELECT h~ebeln,
       i~ebelp,
       i~matnr,
       i~menge * i~netpr / i~peinh AS item_value,
       h~lifnr,
       h~ekgrp
  FROM ekko AS h
  INNER JOIN ekpo AS i ON h~ebeln = i~ebeln
  LEFT OUTER JOIN eban AS r
    ON i~ebeln = r~ebeln
   AND i~ebelp = r~ebelp
 WHERE h~bukrs = '1000'
   AND h~bedat BETWEEN '20250101' AND '20251231'
   AND r~banfn IS NULL  " No requisition found
   AND i~menge * i~netpr / i~peinh > 1000  " Threshold value
 ORDER BY item_value DESCENDING.

Scenario 3: Contract Leakage Analysis

Find POs created outside of existing contracts for the same vendor/material combination, indicating potential savings opportunities.

SELECT po~ebeln,
       po~ebelp,
       po~matnr,
       po~lifnr,
       po~netpr AS actual_price,
       ct~netpr AS contract_price,
       ( po~netpr - ct~netpr ) AS price_variance,
       po~menge * ( po~netpr - ct~netpr ) / po~peinh AS missed_savings
  FROM ekpo AS po
  INNER JOIN ekko AS h ON po~ebeln = h~ebeln
  LEFT OUTER JOIN (
    SELECT lifnr, matnr, MIN( netpr ) AS netpr
      FROM ekpo AS ci
      INNER JOIN ekko AS ch ON ci~ebeln = ch~ebeln
     WHERE ch~bstyp = 'K'  " Contracts
       AND ch~bukrs = '1000'
     GROUP BY lifnr, matnr
  ) AS ct
    ON h~lifnr = ct~lifnr
   AND po~matnr = ct~matnr
 WHERE h~bukrs = '1000'
   AND h~bstyp = 'F'  " Purchase orders
   AND h~bedat BETWEEN '20250101' AND '20251231'
   AND ct~netpr IS NOT NULL
   AND po~netpr > ct~netpr  " Paid more than contract
 ORDER BY missed_savings DESCENDING.

Advanced Topics for Expert Users

Working with Historical Data (CDPOS)

For audit trails and change analysis, query the change document tables (CDHDR/CDPOS) to see who modified PO data and when. The object class for purchase orders is 'EINKBELEG'.

Integration with MM-IM (Inventory Management)

Connect PO data to goods movements (MSEG), material documents (MKPF), and accounting documents (BKPF/BSEG) for complete procure-to-pay visibility. Key linking fields include EBELN, EBELP, and ZEKKN (account assignment sequence number).

Handling Account Assignments (EKKN)

For POs with account assignments (cost centers, projects, etc.), join to EKKN (Account Assignment in Purchasing Document). This is crucial for financial reporting and cost allocation.

SELECT h~ebeln,
       i~ebelp,
       a~zekkn,
       a~kostl AS cost_center,
       a~aufnr AS order_number,
       a~ps_psp_pnr AS wbs_element,
       i~menge * i~netpr / i~peinh AS total_value
  FROM ekko AS h
  INNER JOIN ekpo AS i ON h~ebeln = i~ebeln
  INNER JOIN ekkn AS a ON i~ebeln = a~ebeln AND i~ebelp = a~ebelp
 WHERE h~bukrs = '1000'
   AND a~kostl IN ( '1000101', '1000102', '1000103' )
 ORDER BY a~kostl, h~ebeln.

Conclusion: Building Your Professional Toolkit

Mastering SAP purchase order queries requires understanding not just the SQL syntax, but the business logic embedded in the MM data model. The relationship between EKKO, EKPO, EKET, and EBAN forms the foundation of procurement analytics, and knowing how to navigate these tables efficiently separates average developers from true experts.

Start with the basic patterns we've covered—header/item joins, open quantity calculations, requisition tracking—and gradually incorporate advanced techniques like parallel processing, CDS views, and cross-module integration. Always keep performance in mind: use indexes effectively, project only required fields, and test your queries on production-scale datasets before deployment.

The real-world scenarios we've explored—vendor scorecards, maverick spend detection, contract leakage—represent just a fraction of what's possible when you truly understand the MM tables. As you encounter new business requirements, refer back to these fundamental patterns and adapt them to your specific needs.

Remember that SAP's data model is designed for flexibility and audit compliance. Every field, every deletion indicator, every document type serves a purpose. By respecting this design and working with it rather than against it, you'll build queries that are not only fast and accurate but also maintainable and scalable as your organization grows.

Now get out there and start querying like a pro. Your procurement team is counting on you for those insights hidden in EKKO, EKPO, EKET, and EBAN—and you now have the professional toolkit to deliver them.