SAP ABAP Open SQL Interview Questions

Interviewers use open sql to test depth rather than coverage: the follow-up question is almost always "why does the system behave that way?", and that is where prepared answers usually run out.

Master Open SQL for fast, clean and HANA-friendly ABAP data access with joins, filters, aggregation, package processing and real project performance patterns.

This page carries 25 reviewed SAP ABAP open sql interview questions, each with a complete written answer and no sign-in required. The set breaks down into 2 foundational, 9 mid-level and 14 advanced questions, so you can start at the top for a first interview or skip ahead to the scenario-based items for a senior round.

If you can handle every question here without hesitating, open sql is unlikely to be what costs you an SAP ABAP interview — and the same reasoning pattern transfers to the neighbouring topics linked at the bottom of this page.

25 Open SQL questions with answers

easyOpen SQL

1. Describe the ORDER BY clause in Open SQL. What is the default sort order, and how do you specify descending order for specific fields? What are the performance implications?

ORDER BY sorts result rows by specified fields. Default sort order is ASCENDING (A) for all fields. To reverse for specific fields, use DESCENDING (D). Example: SELECT * FROM orders ORDER BY order_date DESCENDING, line_number ASCENDING retrieves orders newest first, then by line number within each order. Performance-wise, ORDER BY at the database level is efficient if the database has an index matching the ORDER BY sequence (covering index). If no matching index exists, the database performs an in-memory sort, which is fast for small result sets but can be expensive for large result sets. In S/4HANA, database pushdown is optimized for ORDER BY, so the sort typically happens at the database. Use ORDER BY only when ordering is semantically necessary; avoid sorting large result sets in ABAP when the database can do it more efficiently. Sorting after SELECT INTO TABLE in ABAP (using SORT statement) is an alternative but less efficient for large volumes. If no ORDER BY is specified, do not rely on the physical database return sequence; explicitly order when business logic depends on sequence.
easyOpen SQL

2. Write an Open SQL SELECT INTO TABLE statement that fetches material master header data from MARA for a list of materials. Include proper field selection, WHERE clause, and no-data handling.

A clean example is: SELECT matnr, mtart, meins FROM mara INTO TABLE @lt_mara WHERE matnr IN @lr_matnr AND mtart IN @lr_mtart. IF sy-subrc = 0. SORT lt_mara BY matnr. ELSE. MESSAGE 'No materials found for the given selection' TYPE 'I'. ENDIF. The important points are to fetch only the fields required instead of SELECT *, push filters into the WHERE clause, and keep the selection table/range validated before the SELECT. Open SQL automatically handles the client for normal client-dependent transparent tables, so you normally do not select or filter MANDT manually. If material descriptions are required, they are not in MARA; join or separately read MAKT using MATNR and SPRAS, because MAKTX belongs to MAKT. In S/4HANA, for reusable material queries or authorization-aware consumption, a released CDS view may be preferable.
mediumOpen SQL

3. Explain the GROUP BY and HAVING clauses in Open SQL. Provide an example that counts orders by customer and filters for customers with > 10 orders.

GROUP BY groups rows by specified fields and applies aggregates to each group. HAVING filters the grouped results (applied after GROUP BY, unlike WHERE which filters before). Example: SELECT customer_id, customer_name, COUNT(*) AS order_count, SUM(amount) AS total_sales FROM orders GROUP BY customer_id, customer_name HAVING COUNT(*) > 10 AND SUM(amount) > 10000 INTO TABLE lt_result. This query: (1) Groups all orders by customer_id and customer_name. (2) For each group, calculates order count and total sales. (3) HAVING clause filters groups: only customers with > 10 orders AND > 10000 in total sales are included. Distinction: WHERE filters rows before grouping; HAVING filters groups after aggregation. Performance: HAVING is applied at database level after grouping, reducing result rows early. If you need to filter on non-aggregated fields, use WHERE (faster). Example: WHERE order_date BETWEEN @v_start AND @v_end removes rows before grouping. HAVING counts(*) > 10 filters groups after counting. NULL handling: aggregates ignore NULL; COUNT(*) includes NULL rows. Expected result: list of top customers by order count and sales value. Use this pattern for customer segmentation, sales analysis, or performance metrics.
mediumOpen SQL

4. Explain the difference between SELECT SINGLE and SELECT ... UP TO 1 ROWS in Open SQL. When would you use each, and what are the performance and determinism implications?

SELECT SINGLE is normally used when the WHERE clause identifies one logical row, ideally by a full primary key or another unique condition. It returns one matching row and sets sy-subrc to 0 if a row is found, but if the WHERE condition is not unique the returned row should not be treated as business-deterministic unless the requirement genuinely accepts any matching row. SELECT ... UP TO 1 ROWS is used when the query could match multiple rows but you intentionally need only one row; if the row must be deterministic, add an ORDER BY clause such as ORDER BY PRIMARY KEY or a business date/priority field. The key performance point is not the keyword alone, but whether the WHERE clause is selective and supported by a suitable index. A SELECT SINGLE with a poor non-key WHERE clause can still perform badly, while UP TO 1 ROWS with a selective indexed WHERE clause and proper ORDER BY can be efficient and correct. In code reviews, the main question is whether the SELECT expresses uniqueness or just 'give me any row', and whether ST05 confirms the database access path is efficient.
mediumOpen SQL

5. A SELECT statement returns duplicate rows due to a JOIN with a table that has multiple matches. How would you identify this issue and resolve it?

Issue: JOIN between sales orders (1) and line items (many) results in duplicate orders in the output. Example: 100 orders become 500 rows if each order has avg 5 line items. Detection: (1) Count expected rows vs. actual rows. If actual > expected, duplicates likely. (2) Use DISTINCT to check: SELECT DISTINCT order_id. If count reduces, duplicates confirmed. (3) Examine JOIN logic: verify join condition is correct and not creating unintended Cartesian products. Solution 1: Use DISTINCT in SELECT: SELECT DISTINCT o~order_id, o~amount FROM orders AS o INNER JOIN items AS i ON o~order_id = i~order_id. This removes duplicate order rows; trade-off is database must perform deduplication (extra cost). Solution 2: Remove unnecessary tables: if line item details are not needed, don't join. SELECT order_id, amount FROM orders WHERE order_id IN (SELECT DISTINCT order_id FROM items). Faster; avoids Cartesian product. Solution 3: Aggregate at JOIN level: SELECT o~order_id, SUM(i~quantity) AS total_qty FROM orders AS o INNER JOIN items AS i ON o~order_id = i~order_id GROUP BY o~order_id. Produces one row per order with aggregated item data. Recommendation: Understand the business requirement; if you need one row per order, use GROUP BY. If you need one row per item, the JOIN is correct—document this clearly. Always verify row count expectations before and after JOIN. Use ST05 to measure performance impact of DISTINCT vs. GROUP BY.
mediumOpen SQL

6. Explain aggregate functions in Open SQL (SUM, COUNT, AVG, MAX, MIN) and how they work with GROUP BY. Include how to handle NULL values.

Aggregate functions in Open SQL calculate values across multiple rows. SUM totals numeric values, COUNT counts rows (use COUNT(*) for all rows or COUNT(DISTINCT field) for unique values), AVG calculates average, MAX/MIN find highest/lowest values. GROUP BY groups rows by specified fields and applies aggregates to each group. For example, SELECT material, plant, SUM( quantity ) AS total_qty, COUNT(*) AS line_count FROM materials GROUP BY material, plant retrieves total quantity and line count per material per plant. NULL values are excluded from SUM, AVG, MAX, MIN calculations but are counted by COUNT(*). Use COALESCE to replace NULLs with defaults if needed. HAVING clause filters groups after aggregation (e.g., HAVING SUM(quantity) > 1000). In S/4HANA, consider whether to push complex aggregations to the database or use CDS for reusable aggregate logic. SY-SUBRC = 0 after an aggregate SELECT indicates success; if no matching rows exist, aggregates typically return 0 or NULL depending on the function.
mediumOpen SQL

7. A SELECT query returns results, but you notice unexpected NULL values in certain fields. How would you handle NULL values in Open SQL, and what are the implications for business logic?

NULL values in SQL represent missing or undefined data. In Open SQL, NULL values are returned as space (for character fields) or zero (for numeric fields) when fetched into ABAP variables, unless explicitly handled. To manage NULLs: (1) Use COALESCE function: SELECT COALESCE(discount, 0) AS discount FROM orders. Returns 0 if discount is NULL. (2) Use CASE statement: SELECT CASE WHEN discount IS NULL THEN 0 ELSE discount END AS discount FROM orders. (3) Filter NULLs in WHERE: WHERE discount IS NOT NULL. (4) Check field definition in table design (SE11) to see if NULL is allowed (initial value checkbox). Implications for business logic: NULLs can cause incorrect calculations (SUM ignores NULLs, potentially understating totals). Comparisons with NULLs (e.g., discount > 0) exclude NULL rows in the result. In reports, NULL representation as 0 or space can be misleading. Best practice: define NOT NULL constraints in table design when a value is mandatory. In S/4HANA, NULL handling is often implicit in CDS Views through semantic clarity. Always document NULL handling expectations in complex queries to prevent downstream bugs.
mediumOpen SQL

8. You notice that a SELECT statement with an IN operator and a large list (5000 values) is slow. Explain the performance issue and propose alternatives.

Issue: WHERE material_id IN @lt_materials with 5000 entries creates a large WHERE clause sent to the database, causing parse overhead and optimizer difficulty. The database may resort to full table scan instead of index usage for such large IN lists. Solution 1: Use FOR ALL ENTRIES instead. SELECT * FROM stock INTO TABLE lt_stock FOR ALL ENTRIES IN lt_materials WHERE material_id = lt_materials-material_id. FOR ALL ENTRIES can be a better option for larger driver lists if the driver is deduplicated and checked for empty, but it must still be measured. Solution 2: Split the IN list into chunks. LOOP AT lt_materials ASSIGNING FIELD-SYMBOL(<fs>) BY 1000. 'Build WHERE clause for 1000 items'. SELECT * FROM stock WHERE material_id IN @lt_chunk. 'Append to result'. ENDLOOP. This reduces parse overhead and allows index usage. Solution 3: Use JOIN if materials and stock are related at database level. SELECT s~* FROM materials AS m INNER JOIN stock AS s ON m~material_id = s~material_id INTO TABLE lt_result. Performance comparison: IN with 5000 values = 10 seconds; FOR ALL ENTRIES = 2 seconds; chunked IN (1000 at a time) = 3 seconds; JOIN = 0.5 seconds. Recommendation: Use FOR ALL ENTRIES or JOIN; avoid IN with > 1000 entries. Measure with ST05 to confirm.
mediumOpen SQL

9. Your SELECT statement uses FOR ALL ENTRIES but the driver table is sometimes empty during execution. How does this affect the query result, and how would you code defensively?

FOR ALL ENTRIES must never be executed blindly with an empty driver table. In ABAP Open SQL, if the FOR ALL ENTRIES internal table is empty, the comparison conditions that reference that table can be ignored, which may result in the database returning far more rows than intended, potentially even the whole table depending on the remaining WHERE conditions. This is one of the most dangerous Open SQL production bugs because it can look fine in tests where the driver table is populated, but suddenly create a full-table read in production. The defensive pattern is: IF lt_driver IS NOT INITIAL. SELECT ... FOR ALL ENTRIES IN @lt_driver WHERE key = @lt_driver-key ... INTO TABLE @lt_result. ELSE. CLEAR lt_result. Log or raise a business message depending on the requirement. ENDIF. Also deduplicate the driver table before the SELECT to reduce redundant OR conditions and improve database execution.
mediumOpen SQL

10. Describe how client handling (MANDT) works in Open SQL. When is client filtering automatic, and when must it be handled explicitly?

Many SAP application tables are client-dependent and contain MANDT as part of the key, but not every SAP table is client-dependent; some customizing/system tables are client-independent. For normal Open SQL SELECTs on client-dependent tables, SAP automatically restricts access to the current client (sy-mandt), so developers usually should not manually add MANDT to the WHERE clause. This implicit client handling is important for tenant/client isolation and helps prevent accidental cross-client access. Cross-client reads are exceptional and should only be used for authorized administrative/reporting requirements; in classic Open SQL this requires explicit client handling such as CLIENT SPECIFIED together with a MANDT condition, depending on release and syntax. In CDS, client handling is controlled through annotations and framework behavior. During debugging, if a record exists in another client but not in the current one, a normal SELECT will not find it, which is expected. The safest rule is: rely on implicit client handling for normal business code, and only use explicit cross-client logic after confirming authorization, release syntax, and business justification.
mediumOpen SQL

11. After executing a SELECT statement in Open SQL, your SY-SUBRC is 0 but the result set is logically empty (e.g., all rows filtered out in your application logic). How does this differ from a true database 'no rows found' scenario, and what debugging approach would you take?

SY-SUBRC = 0 after SELECT means the query executed successfully and at least one row was returned by the database. If your application logic then filters all rows away, SY-SUBRC remains 0 because it reflects database execution status, not application filtering. True 'no rows found' occurs when the WHERE clause matches zero rows at the database level (SY-SUBRC = 4). To debug, inspect the actual fetched row count before and after application filtering. Use ST05 (SQL Trace) to see the exact SELECT statement and row count returned by the database. Alternatively, capture the internal table's line count immediately after SELECT (DESCRIBE TABLE it LINES lv_count) to see how many rows the database actually returned before application filtering. Check your WHERE clause logic to ensure it's pushing the right filters down to the database rather than fetching excessive rows in ABAP.
hardOpen SQL

12. A SELECT statement into an internal table takes 30 seconds. After analyzing the code, you realize the WHERE clause doesn't use any indexed fields, and a full table scan is occurring. How would you refactor this, and what tools would you use to verify improvement?

Root cause: full table scan on a large table due to WHERE clause not matching available indices. Refactoring steps: (1) Analyze indices on the table (SE11 > Indices tab) to understand what indices exist. (2) Review WHERE clause fields and rewrite to use leading index fields if possible. Example: old WHERE status = 'OPEN', new WHERE created_date BETWEEN @v_start AND @v_end AND status = 'OPEN' (if index exists on created_date). (3) If no suitable index exists, propose one through normal SE11/index governance on WHERE clause fields in the correct order. (4) Use ST05 to baseline current performance (full table scan, execution time). (5) Deploy refactored code with new WHERE clause or index. (6) Re-run ST05 to compare: access pattern should change from Full Table Scan to Index Access, execution time should drop significantly. (7) If results are still poor, apply PACKAGE SIZE to batch-fetch or consider CDS View. Expected outcome: 30 seconds → 2-3 seconds with proper index. Before/after ST05 comparison is the proof point. Document the index creation and WHERE clause refactoring in your code comments for maintenance.
hardOpen SQL

13. Design a robust error handling and monitoring strategy for a production Open SQL batch job that processes 10 million rows. Include recovery, logging, and performance tracking.

A production Open SQL batch job processing 10 million rows needs controlled package design, audit logging, and restartability. First, define package boundaries by key range, date range, or a checkpoint sequence so each package can be selected, processed, written, and committed independently. Use TRY...CATCH around database and processing sections, catching relevant exceptions such as cx_sy_open_sql_db and logging the technical message, package key range, row counts, user, timestamp, and job name. After each successful package, write a checkpoint record and COMMIT WORK so restart can begin from the last completed package. Do not commit inside an active database cursor; ensure the package SELECT has completed before commit. Monitoring should include SM37 job logs, ST05 for SQL traces during performance tests, SM50 for active work process observation, SM12 for lock/enqueue issues if updates are involved, ST22 for dumps, and an application log/custom log table for business status. Track rows per second and package duration, and alert if the rate drops below threshold. Recovery should be idempotent: re-running a package should not duplicate records, either by using unique keys/upsert logic, processed flags, or a staging table with status.
hardOpen SQL

14. Compare CDS Views vs. Open SQL for data retrieval in SAP S/4HANA. When would you recommend each approach?

CDS Views are ABAP-managed declarative data models with built-in semantics, annotations, and optimizations. Open SQL is direct SQL written in ABAP code. CDS advantages: (1) Reusability across applications and reports. (2) Semantic clarity through annotations (filters, access control, client handling). (3) Performance: database optimizer understands CDS intent and often produces better execution plans. (4) Maintainability: centralize data logic; change once, all consumers benefit. (5) Authorization control through DCL/access control annotations when designed for it. Open SQL advantages: (1) Ad-hoc queries for specific, one-time reports. (2) Dynamic WHERE clauses based on runtime parameters. (3) Simpler for lightweight queries without reuse potential. (4) Familiarity for developers new to S/4HANA. Recommendation: Use CDS Views for standard queries (e.g., material masters, sales orders, GL entries) accessed by multiple reports. Use Open SQL for highly custom, parameter-driven one-off reports. In practice, S/4HANA best practices favor CDS Views for most data retrieval. Example: sales report uses SELECT from CDS View C_SalesOrderTP instead of direct VBAK/VBAP query. Monitor adoption with framework tools (HANA DB trace, plan cache analysis). Documentation: SAP S/4HANA Core Data Services (CDS) Overview and SAP ABAP Keyword Reference.
hardOpen SQL

15. You are tasked with fetching 500,000 sales orders and their line items for a month-end batch. Describe your approach to avoid memory issues and database timeouts, including PACKAGE SIZE, batching, and commit strategy.

For large-volume reads, avoid loading all orders and items into one giant internal table. Use package-based processing: read a controlled package of order keys using a selective WHERE clause on date/company/status, process that package, fetch its related line items in bulk using JOIN or FOR ALL ENTRIES with an empty-driver check, write results, then clear/free package tables before moving to the next package. The package size should be chosen after testing, for example 10,000 to 50,000 rows depending on row width and memory. If the job writes to application tables or a staging table, commit at safe package boundaries after the package is fully processed and all database cursors for that package are closed; do not keep one very long LUW for the full month-end run. Add restart/checkpoint logic so a failed job can resume from the last completed package rather than starting from zero. Use ST05 for SQL behavior, SM37 for job runtime, SM12 for lock checks if updates are involved, and ST22 for dumps.
hardOpen SQL

16. You are debugging a SELECT query using ST05 (SQL Trace). Walk through the key metrics you would examine to diagnose performance issues and identify the root cause.

In ST05, examine: (1) Query execution time (Exec Time) - compare against expected baseline; (2) Rows returned (Records Returned) - identify if query is fetching unexpected volume; (3) Records Read - if much higher than Records Returned, a WHERE clause is inefficient or missing; (4) Access pattern - Full Table Scan vs. Index Access; (5) For Index Access, check which index is used and whether it matches your WHERE clause sequence; (6) Sort information - presence of Sort Operation indicates no covering index; (7) Table join sequence if multiple tables are accessed; (8) Network traffic (Packet Size * Round Trips) to identify if excessive data is transferred. Example diagnosis: SELECT returns 10 rows but reads 1 million rows → WHERE clause is filtering at ABAP level instead of database level, or index doesn't support the WHERE clause. Access pattern shows Full Table Scan instead of Index Access → no suitable index exists or WHERE clause doesn't match available indexes. ST05 helps prioritize: optimize WHERE clause, create/rebuild index, or refactor query to JOIN. Always baseline before optimizing to measure improvement accurately.
hardOpen SQL

17. Explain the relationship between index field order and WHERE-clause selectivity in Open SQL. Does the written order of conditions in the WHERE clause matter?

The written order of conditions in an Open SQL WHERE clause normally does not matter because the database optimizer can reorder predicates. What matters is whether the fields used in the WHERE clause match the leading columns of an available database index and whether those predicates are selective. For example, an index on (MATNR, WERKS, LGORT) is very effective when the WHERE clause includes MATNR, or MATNR plus WERKS, because the leading index columns are supplied. A query using only WERKS and LGORT skips the leading MATNR column, so the same index is much less useful and the optimizer may choose another index or a full scan. This is often called the leading-column principle. In SAP work, you should inspect existing indexes in SE11, avoid unnecessary custom indexes, and use ST05 to confirm which index the database actually chose. If a different query pattern is frequent and critical, a separate index may be justified, but only after considering write overhead and Basis/DB governance.
hardOpen SQL

18. You are designing a high-performance batch job to export 2 million sales orders to a data warehouse. Walk through your Open SQL strategy, including PACKAGE SIZE, indexing, and monitoring.

Strategy for 2M order export: (1) Index analysis: verify index exists on (company_code, creation_date) to efficiently filter order date range. Create if missing (SE14). (2) WHERE clause optimization: SELECT order_id, customer, amount FROM orders PACKAGE SIZE 50000 INTO TABLE lt_orders WHERE company_code = @v_company AND order_date BETWEEN @v_start_date AND @v_end_date. (3) Loop and export: LOOP AT lt_orders INTO wa_order. 'Write to file or staging table'. CLEAR lt_orders. ENDLOOP. COMMIT WORK after each batch to free locks. (4) Monitoring: use ST05 SQL trace for first batch to confirm index usage and row volume. Use SM50 to monitor job progress and check for Lock waits (SM12). Use ST06 to monitor CPU and memory. (5) Parallelization: consider splitting by company_code and running parallel jobs (SM37) to complete faster. (6) Failure handling: implement restart logic to skip already-processed batches (checkpoint table). (7) Performance target: aim for 50K rows/second; 2M rows should complete in ~1 hour. Expected improvements: optimized WHERE clause reduces initial fetch; PACKAGE SIZE manages memory; parallel jobs reduce single-job duration. Document the batch design in a BASIS job catalog. Monitor production execution weekly.
hardOpen SQL

19. A performance report queries sales data across 10 SAP tables. Explain your approach to optimize this using Open SQL, including when you would consider a CDS View or materialized aggregates.

Optimization approach for multi-table query: (1) Schema analysis: map data dependencies to identify join order and cardinality. Largest tables should be joined last. (2) Index strategy: verify indices exist on all join fields and WHERE clause fields. Consider composite indices if multiple joins share leading fields. (3) Open SQL optimization: SELECT s~sales_doc, s~amount, c~customer_name, m~material, p~profit_center FROM sales AS s INNER JOIN customer AS c ON s~cust_id = c~cust_id INNER JOIN material AS m ON s~matnr = m~matnr INNER JOIN plant AS p ON s~werks = p~werks WHERE s~sales_date BETWEEN @v_start AND @v_end INTO TABLE lt_result PACKAGE SIZE 50000. (4) Performance limit: if 10 joins result in slow execution even with indices, consider CDS View. CDS benefits: pre-optimized join logic, semantic clarity, reusability. (5) Materialized aggregates: if report aggregates data (SUM, COUNT), consider a separate aggregate/summary table updated nightly. Query the summary instead of live tables. (6) Partitioning: if data is very large, partition table by date and query only relevant partitions. (7) Measurement: ST05 baseline before optimization. Compare join orders, index usage, and execution time. Expected outcome: multi-table query optimized from 2+ minutes to sub-second with proper design. Documentation: include query logic and index design in functional design document (FDD).
hardOpen SQL

20. Explain the concept of database pushdown in S/4HANA and how it influences your Open SQL design decisions.

Database pushdown means moving computation logic from ABAP application layer to the database layer (HANA). S/4HANA's HANA database is column-oriented and optimized for analytical workloads, making it efficient for complex filtering, aggregation, and calculations. Pushdown benefits: (1) Aggregations (SUM, COUNT, GROUP BY) are faster at database level than ABAP. (2) Filtering (WHERE, HAVING) happens before data transfer, reducing network traffic. (3) Joins are optimized by HANA; avoid implementing join logic in ABAP. (4) Sorting (ORDER BY) is at database level, faster than ABAP SORT. Impact on design: (1) Maximize WHERE clause filtering—push all business logic to WHERE/HAVING if possible. (2) Use aggregate functions in SELECT instead of looping in ABAP. (3) Use JOINs for complex relationships; avoid fetching multiple tables separately. (4) Minimize data fetch—select only needed fields, not SELECT *. Example: Old approach (ABAP logic): SELECT * FROM orders, SUM and filter in ABAP loop. New approach (pushdown): SELECT company_code, SUM(amount) FROM orders WHERE order_date BETWEEN @v_start AND @v_end GROUP BY company_code HAVING SUM(amount) > 10000. HANA handles the heavy lifting. Measurement: ST05 reveals execution at HANA vs. ABAP. CDS Views in S/4HANA are optimized for pushdown; prefer CDS over direct Open SQL for standard queries. Performance gain: logic pushed to HANA often reduces execution time from minutes to seconds.
hardOpen SQL

21. A report uses multiple SELECT statements inside a loop, fetching related data for each order. Redesign this using a single query with JOINs and explain the performance benefit.

Anti-pattern: LOOP AT lt_orders INTO wa_order. SELECT * FROM items WHERE order_id = wa_order-order_id INTO TABLE lt_items. SELECT * FROM shipments WHERE order_id = wa_order-order_id INTO TABLE lt_shipments. 'Process items and shipments'. ENDLOOP. This is N+1+N problem: 1 outer query + N items queries + N shipments queries = 1+2N total. Refactored solution (single query with JOINs): SELECT o~order_id, o~customer, o~order_date, i~item_id, i~material, s~shipment_id, s~status FROM orders AS o LEFT OUTER JOIN items AS i ON o~order_id = i~order_id LEFT OUTER JOIN shipments AS s ON o~order_id = s~order_id INTO TABLE @lt_result WHERE o~order_date BETWEEN @v_start AND @v_end. LOOP AT lt_result INTO wa_result. 'Process result'. ENDLOOP. Benefits: (1) Single database round-trip instead of 1+2N. (2) Database optimizer handles joining efficiently. (3) Network traffic reduced significantly. (4) Lock contention eliminated. (5) Response time scales linearly instead of exponentially. For 1,000 orders: old approach = 2,001 queries; new approach = 1 query. Expected time reduction: 8 hours to 5 minutes. Trade-off: result set may be larger if there are many items/shipments per order (Cartesian product); handle in ABAP by grouping or data aggregation.
hardOpen SQL

22. A production system is experiencing slow response times. You suspect a SELECT inside a LOOP is the culprit. Describe the anti-pattern, its performance impact, and provide a refactored solution.

The SELECT-inside-LOOP anti-pattern executes one database query for each row in the driving internal table, creating an N+1 query problem. For example, looping over 10,000 sales orders and running SELECT SINGLE for each order item can cause 10,000 database round trips, high network latency, and unnecessary database load. A better design is to collect the required keys first, deduplicate them, then fetch related rows in one set-based query using a JOIN or FOR ALL ENTRIES. With FOR ALL ENTRIES, always check that the driver table is not empty before the SELECT, because an empty driver can cause the FAE condition to be ignored and may return a much larger result set than intended. After bulk fetching the child rows, store them in a sorted or hashed internal table and join back to the driver data by key in memory. If a clean join is possible, a single INNER JOIN or LEFT OUTER JOIN is often better because the database optimizer can process the relationship in one statement. ST05 should be used before and after refactoring to confirm the reduction in SQL calls and records read.
hardOpen SQL

23. A monthly report processes sales orders but runs for 8 hours, causing a production backup. Using ST05, you identify that the query scans 50 million rows but returns only 10,000. Propose and implement a solution.

The issue is a full table scan with heavy filtering. Solution steps: (1) Review WHERE clause in the SELECT—likely missing fields that should filter early at the database level. Current code might be SELECT * FROM orders WHERE SOME_FLAG = 'X' AND amount > 0, missing order_date filter. Refactor to SELECT * FROM orders WHERE order_date BETWEEN @v_start_date AND @v_end_date AND SOME_FLAG = 'X' AND amount > 0. (2) Check if a composite index exists for (order_date, SOME_FLAG, amount) in table design (SE11). If not, propose one through normal SE11/index governance. (3) Use ST05 to confirm index is being used (Index Access instead of Full Table Scan). (4) If query is still slow, apply PACKAGE SIZE to process in batches and reduce memory. (5) Consider CDS View with pre-filtered data if report is regular. (6) Parallelize using background jobs if business allows. Expected outcome: runtime drops from 8 hours to minutes. Before and after ST05 comparison proves improvement. Code change example: old (8 hours): SELECT * FROM orders INTO TABLE lt_orders WHERE flag = 'X'. New (5 minutes): SELECT * FROM orders PACKAGE SIZE 50000 INTO TABLE lt_orders WHERE order_date IN @lt_dates AND flag = 'X' ORDER BY order_date.
hardOpen SQL

24. A report queries purchase orders from multiple vendors. You have a list of 800 vendor IDs and need to fetch orders for all of them efficiently. Compare FOR ALL ENTRIES vs. JOIN approaches and recommend based on your experience.

FOR ALL ENTRIES approach: SELECT * FROM orders INTO TABLE lt_orders FOR ALL ENTRIES IN lt_vendors WHERE vendor_id = lt_vendors-vendor_id. Results in 2 queries if driver table (lt_vendors) is not empty. If lt_vendors is empty, the FOR ALL ENTRIES condition can be ignored and the query may return a much larger result set than intended, so handle it separately with IF lt_vendors IS NOT EMPTY. JOIN approach: SELECT o~*, v~vendor_name FROM orders AS o INNER JOIN vendors AS v ON o~vendor_id = v~vendor_id INTO TABLE lt_result WHERE o~vendor_id IN @lt_vendor_ids. Results in 1 query. FOR ALL ENTRIES is beneficial if: orders table has few columns or selective field fetch is preferred, or if vendors table is very large (JOIN might be inefficient). JOIN is preferred if: relationship is well-defined, need vendor details in one fetch, and both tables have good indexes on join fields. In S/4HANA, JOINs are often optimized by the database and CDS Views; prefer JOIN if semantic. For 800 vendors, either works, but JOIN (1 query) outperforms FOR ALL ENTRIES (2 queries) in network latency and database connection overhead. Always run ST05 on both approaches with actual data volume to confirm.
hardOpen SQL

25. A background job frequently fails with cursor-related dumps or lock-timeout symptoms during large Open SQL processing. How would you diagnose and redesign it safely?

Cursor-related dumps usually happen when a program keeps a database cursor open for too long, exits a SELECT loop incorrectly, or performs actions that invalidate the cursor while the SELECT is still active. Lock timeouts usually come from update processing or long-running LUWs, not from a plain read SELECT itself. I would first check ST22 for the exact dump, SM37 for the job log, ST05 for the long-running SQL, and SM12 if the job also updates locked objects. From a design point of view, avoid SELECT...ENDSELECT with heavy processing inside the cursor loop. Prefer reading a bounded package or key range into an internal table, closing that read scope, processing the package, then committing only after the package's updates are completed. If package processing uses an open cursor style, do not place COMMIT WORK in a way that invalidates the active cursor; instead use explicit key-range pagination or controlled chunks where each SELECT finishes before commit. This makes the job restartable, avoids open-cursor issues, and keeps LUWs shorter. Add checkpoint logging so a failed run can resume from the last completed key range or batch.

Related lesson

SELECT SINGLE vs UP TO 1 ROWS

Related topics

Next practice step