Chapter 06 Β· Core Classic ABAP
Open SQL β Reading & Writing Data
The statements that bring your Data Dictionary tables to life β SELECT, INSERT, UPDATE, DELETE, and everything in between.
In Chapter 5, you learned how SAP defines tables. The Data Dictionary holds the definitions β the shapes, the fields, the types. But a definition alone is just an empty container. What makes a table useful is the data inside it.
This chapter is where you learn how to put data into tables, take it out, change it, and remove it. That is done with Open SQL β ABAP’s database access language. It is one of the most important skills in the entire ABAP ecosystem, and it appears in nearly every interview for junior and mid-level roles.
You will learn the four fundamental statements β SELECT, INSERT, UPDATE, DELETE β plus JOINs, WHERE clauses, aggregate functions, and the critical role of sy-subrc after every database operation.
ZCUSTOMER table you created in Chapter 5, reads them back with SELECT, updates one record, and finally deletes it β all with proper sy-subrc checking.
π― Who Is This Chapter For?
This chapter has both paths running, with one important difference:
- If you are on the FREE BTP Trial β you will use ABAP SQL, the modern Cloud version of Open SQL. It is syntactically almost identical but has stricter rules (no direct table reads without a CDS View or RAP business object). You will see how ABAP SQL fits into the Clean Core model.
- If you have PAID SAP access β you will use classic Open SQL in SE38 report programs. This is where you can SELECT from any transparent table, INSERT new rows, UPDATE existing ones, and DELETE records freely. This is what 70% of ABAP jobs still do daily.
- Both paths teach the same core SQL syntax. SELECT, WHERE, JOIN, INSERT, UPDATE, DELETE β the language is fundamentally the same. Only the safety rules differ.
π― What You Will Learn in This Chapter
- What Open SQL is and how it differs from ABAP SQL in Cloud
- The
SELECTstatement in all its forms β single, list, aggregate - The
WHEREclause and how to filter data - How to use
JOINto read from multiple tables INSERT,UPDATE, andDELETEfor modifying data- How
sy-subrctells you whether each operation succeeded - Best practices for writing safe, performant database code
π§ Which Path Should I Choose?
Before writing code, decide which path applies to you. This chapter has two tracks.
Two learner types β find yourself
@ escape character.
What You Can Do With Each Path
| Task | π’ Free Path (BTP Trial) | π΅ Paid Path (SAP System) |
|---|---|---|
| SELECT from transparent table | β οΈ Via CDS Views only | β Direct SELECT |
| INSERT new records | β οΈ Via RAP business object | β Direct INSERT |
| UPDATE records | β οΈ Via RAP business object | β Direct UPDATE |
| DELETE records | β οΈ Via RAP business object | β Direct DELETE |
| JOINs | β Full support in CDS Views and ABAP SQL | β Full support |
| Aggregates (SUM, COUNT) | β Full support | β Full support |
| Editor | Eclipse ADT | SAP GUI + Eclipse ADT |
π What is Open SQL?
Open SQL is ABAP’s own subset of SQL. It looks like SQL, but it is not standard SQL. It is database-independent β the same Open SQL statement works on SAP HANA, Oracle, SQL Server, and every other database SAP supports, because SAP translates it into the specific database’s syntax behind the scenes.
SELECT * FROM zcustomer INTO TABLE @lt_customers., that single statement works on every database SAP supports. You do not need to rewrite it for Oracle or HANA. This is one of SAP’s superpowers.
Open SQL has four fundamental operations, often called CRUD:
| Letter | Operation | SQL Statement |
|---|---|---|
| C | Create | INSERT |
| R | Read | SELECT |
| U | Update | UPDATE |
| D | Delete | DELETE |
Every database operation in ABAP falls into one of these four categories. Master these and you can do anything with data in SAP.
ABAP SQL in Cloud
In ABAP Cloud, you use the same four operations but with the @ escape character for host variables. Direct table reads are restricted β you work through CDS Views and RAP business objects.
- Write SELECT in Eclipse ADT
- Use
@before every variable - Read from CDS Views or released tables
- Modify data through RAP only
Open SQL in SE38
In Classic ABAP, you have full freedom. Read, insert, update, and delete directly on any transparent table without restrictions. The @ character is optional in newer syntax but recommended.
- Open SE38 and create a report
- Write SELECT, INSERT, UPDATE, DELETE
- Check sy-subrc after every operation
- Activate and run with F8
π SELECT β Reading Data
The SELECT statement retrieves data from one or more tables. It is the most-used statement in ABAP, and it comes in several forms.
Form 1: Single Record
SELECT SINGLE * FROM zcustomer
INTO @DATA(ls_customer)
WHERE customer_id = @lv_id.
IF sy-subrc = 0.
cl_demo_output=>write( |Found: { ls_customer-customer_name }| ).
ELSE.
cl_demo_output=>write( 'No customer found.' ).
ENDIF.
SELECT SINGLE returns exactly one record (or none). Use it when you know the key or the WHERE clause will match a single row.
Form 2: Multiple Records into an Internal Table
DATA lt_customers TYPE TABLE OF zcustomer.
SELECT * FROM zcustomer
INTO TABLE @lt_customers
WHERE city = @lv_city.
IF sy-subrc = 0.
cl_demo_output=>write( |Found { lines( lt_customers ) } customers in { lv_city }| ).
ENDIF.
INTO TABLE reads multiple rows into an internal table (which you will learn deeply in Chapter 7). The lines( ) function returns the number of rows.
Form 3: Specific Fields Only
SELECT customer_id, customer_name, city
FROM zcustomer
INTO TABLE @DATA(lt_summary)
WHERE join_date >= @lv_from_date.
Reading only the fields you need is faster than SELECT * because less data travels from the database to your program. Always list only the fields you actually use.
@. This tells the compiler “this is an ABAP variable, not a database field.” Classic Open SQL did not require this, but the modern syntax does. Always use @.
SELECT in Cloud
You can SELECT from CDS Views the same way. In fact, in ABAP Cloud, most SELECT statements target CDS Views, not raw tables.
SELECT * FROM zi_customer
INTO TABLE @DATA(lt_customers).
SELECT in Classic
You can SELECT directly from any transparent table. The @ character is optional in older syntax but required if you use inline declarations.
SELECT * FROM zcustomer
INTO TABLE lt_customers.
π― WHERE β Filtering Your Data
The WHERE clause controls which rows the SELECT returns. Without it, you get everything in the table.
Common operators
| Operator | Meaning | Example |
|---|---|---|
= | Equal to | city = 'Pune' |
<> or NE | Not equal to | city <> 'Mumbai' |
< > <= >= | Comparison | salary > 50000 |
BETWEEN ... AND ... | Range | age BETWEEN 25 AND 40 |
IN ( ... ) | List of values | city IN ('Pune', 'Mumbai') |
LIKE '...%' | Pattern match | name LIKE 'Ra%' |
IS NULL | Empty value | email IS NULL |
AND, OR | Combine conditions | city = 'Pune' AND age > 25 |
Example: Combining Conditions
SELECT * FROM zcustomer
INTO TABLE @DATA(lt_filtered)
WHERE city = @lv_city
AND age > 25
AND join_date >= @lv_from_date.
π Aggregate Functions
ABAP SQL has five aggregate functions that perform calculations across rows:
| Function | What it does | Example |
|---|---|---|
COUNT( * ) | Counts rows | How many customers? |
SUM( field ) | Sum of numeric values | Total sales amount |
AVG( field ) | Average of numeric values | Average salary |
MIN( field ) | Smallest value | Earliest join date |
MAX( field ) | Largest value | Highest salary |
Example: Count and Sum
SELECT COUNT( * ), SUM( salary )
FROM zcustomer
INTO @DATA(ls_stats)
WHERE city = @lv_city.
cl_demo_output=>write( |Total customers: { ls_stats-count }| ).
cl_demo_output=>write( |Total salary: { ls_stats-sum }| ).
Example: Grouped Aggregates
SELECT city, COUNT( * ) AS customer_count
FROM zcustomer
INTO TABLE @DATA(lt_by_city)
GROUP BY city.
LOOP AT lt_by_city INTO DATA(ls_row).
cl_demo_output=>write( |{ ls_row-city }: { ls_row-customer_count }| ).
ENDLOOP.
GROUP BY collapses rows that share the same value in the specified column, which is perfect for reports that summarise data.
π JOINs β Reading from Multiple Tables
Real SAP data is spread across many tables. To combine them, you use a JOIN. The most common type is INNER JOIN, which returns only rows that match in both tables.
Example: Customer + Orders
SELECT c~customer_id, c~customer_name, o~order_id, o~order_total
FROM zcustomer AS c
INNER JOIN zorders AS o
ON c~customer_id = o~customer_id
INTO TABLE @DATA(lt_result)
WHERE c~city = @lv_city.
Notes:
- Table aliases (
cando) make the query readable - The tilde (
~) separates alias from field name:c~customer_id - The
ONclause specifies which fields must match
Join types
| Type | Returns | When to use |
|---|---|---|
| INNER JOIN | Rows that match in both tables | You only want complete data |
| LEFT OUTER JOIN | All rows from the left table, matched or not | You want all customers, even those without orders |
| RIGHT OUTER JOIN | All rows from the right table | Rare β usually rewritten as a left join |
β INSERT β Adding New Records
To create new data, use INSERT. You will use it less often than SELECT, but it is essential for any data-entry program.
Insert a single record
DATA ls_new TYPE zcustomer.
ls_new-customer_id = 'C001'.
ls_new-customer_name = 'Rahul Sharma'.
ls_new-city = 'Pune'.
ls_new-join_date = sy-datum.
INSERT zcustomer FROM @ls_new.
IF sy-subrc = 0.
cl_demo_output=>write( 'Customer inserted successfully.' ).
ELSE.
cl_demo_output=>write( 'Insert failed.' ).
ENDIF.
Insert multiple records
DATA lt_new_customers TYPE TABLE OF zcustomer.
" Fill lt_new_customers with data ...
INSERT zcustomer FROM TABLE @lt_new_customers.
sy-subrc returns 4. Always check sy-subrc β never assume the insert worked.
βοΈ UPDATE β Modifying Existing Records
To change data, use UPDATE. You can update specific fields or entire records.
Update specific fields
UPDATE zcustomer
SET city = @lv_new_city
WHERE customer_id = @lv_id.
IF sy-subrc = 0.
cl_demo_output=>write( 'Customer updated.' ).
ENDIF.
Update from a structure
DATA ls_update TYPE zcustomer.
ls_update-customer_id = 'C001'.
ls_update-customer_name = 'Rahul Sharma (updated)'.
ls_update-city = 'Mumbai'.
UPDATE zcustomer FROM @ls_update.
ποΈ DELETE β Removing Records
To remove data, use DELETE. Just like UPDATE, always use a WHERE clause unless you truly want to clear the whole table.
DELETE FROM zcustomer
WHERE customer_id = @lv_id.
IF sy-subrc = 0.
cl_demo_output=>write( 'Customer deleted.' ).
ELSE.
cl_demo_output=>write( 'No customer matched the ID.' ).
ENDIF.
β οΈ sy-subrc β The Critical Check
Every database operation updates the system field sy-subrc. It tells you whether the operation succeeded. Ignoring it is the most common mistake in junior ABAP code.
| Statement | sy-subrc = 0 | sy-subrc != 0 |
|---|---|---|
SELECT SINGLE | Record found | No record matched |
SELECT ... INTO TABLE | At least one row found | No rows matched |
INSERT | Insert succeeded | Duplicate key or other error |
UPDATE | Update succeeded | No row matched the WHERE clause |
DELETE | Delete succeeded | No row matched the WHERE clause |
The pattern is always the same:
SELECT SINGLE * FROM zcustomer
INTO @DATA(ls_customer)
WHERE customer_id = @lv_id.
IF sy-subrc = 0.
" success path
ELSE.
" error handling
ENDIF.
π Full Worked Example
Let’s put everything together into a working program. This example inserts, reads, updates, and deletes β with proper sy-subrc checks at each step.
π΅ Paid Path (Classic ABAP in SE38)
REPORT zsql_ch6_demo.
DATA: ls_customer TYPE zcustomer,
lt_results TYPE TABLE OF zcustomer,
lv_id TYPE zcustomer-customer_id VALUE 'C100'.
" 1. INSERT a new customer
ls_customer-customer_id = lv_id.
ls_customer-customer_name = 'Test Customer'.
ls_customer-city = 'Pune'.
ls_customer-join_date = sy-datum.
INSERT zcustomer FROM ls_customer.
IF sy-subrc = 0.
WRITE: / 'Step 1: INSERT succeeded.'.
ELSE.
WRITE: / 'Step 1: INSERT failed (may already exist).'.
ENDIF.
" 2. SELECT the record back
SELECT SINGLE * FROM zcustomer
INTO ls_customer
WHERE customer_id = lv_id.
IF sy-subrc = 0.
WRITE: / 'Step 2: Found', ls_customer-customer_name.
ELSE.
WRITE: / 'Step 2: No record found.'.
ENDIF.
" 3. UPDATE the record
UPDATE zcustomer
SET city = 'Mumbai'
WHERE customer_id = lv_id.
IF sy-subrc = 0.
WRITE: / 'Step 3: UPDATE succeeded.'.
ENDIF.
" 4. SELECT all records in Mumbai
SELECT * FROM zcustomer
INTO TABLE lt_results
WHERE city = 'Mumbai'.
WRITE: / 'Step 4: Records in Mumbai:', lines( lt_results ).
" 5. DELETE the record
DELETE FROM zcustomer WHERE customer_id = lv_id.
IF sy-subrc = 0.
WRITE: / 'Step 5: DELETE succeeded.'.
ENDIF.
π’ Free Path (ABAP Cloud in Eclipse ADT)
In Cloud, direct INSERT/UPDATE/DELETE on transparent tables is not permitted. You would use a RAP Business Object (Chapter 17) to modify data, or a released CDS View to read it. The syntax you learned above is almost identical, but the safety model is different:
METHOD run.
" Reading from a CDS View (only allowed way in Cloud)
SELECT * FROM zi_customer
INTO TABLE @DATA(lt_customers)
WHERE city = @lv_city.
IF sy-subrc = 0.
cl_demo_output=>write( |Found { lines( lt_customers ) } customers.| ).
ELSE.
cl_demo_output=>write( 'No customers found.' ).
ENDIF.
cl_demo_output=>display( ).
ENDMETHOD.
π§ What Just Happened Under the Hood
Understanding how ABAP talks to the database helps you write faster code and debug more effectively.
| Stage | What SAP Does |
|---|---|
| Parse | Reads your Open SQL statement and checks syntax |
| Translate | Converts Open SQL to the database-specific SQL (HANA, Oracle, etc.) |
| Optimize | Chooses the fastest execution plan using indexes and statistics |
| Execute | Runs the query on the database |
| Transfer | Sends result rows back to ABAP |
| Set sy-subrc | Updates the system field with success or failure code |
This is why checking sy-subrc matters β it is the only reliable signal that the operation completed successfully on the database side.
π Chapter Summary
- Open SQL is ABAP’s database-independent SQL subset. The same statement works on every database SAP supports.
- The four fundamental operations are SELECT, INSERT, UPDATE, DELETE (CRUD).
SELECT SINGLEreturns one record.SELECT ... INTO TABLEreturns many records into an internal table.- The
WHEREclause filters rows. Combine conditions withAND,OR,IN,BETWEEN, andLIKE. - Aggregate functions (
COUNT,SUM,AVG,MIN,MAX) summarize data. UseGROUP BYfor category summaries. - INNER JOIN combines tables; LEFT OUTER JOIN keeps unmatched rows from the left table.
- In modern ABAP SQL, every ABAP variable inside a statement must be prefixed with
@. - Always check
sy-subrcafter every SELECT, INSERT, UPDATE, and DELETE. - Never write UPDATE or DELETE without a WHERE clause unless you truly want to modify every row.
- In ABAP Cloud, direct modification of transparent tables is restricted β you use RAP business objects instead.
π€ Interview Questions & Answers
Open SQL questions appear in nearly every ABAP interview. Know these answers cold.
Open SQL is ABAP’s own SQL subset. It is database-independent β the same statement runs on SAP HANA, Oracle, SQL Server, and any other supported database. SAP translates it to the specific database behind the scenes. It works only on tables and views defined in the ABAP Data Dictionary.
Native SQL is the database’s own SQL. It uses the exact syntax of the underlying database, supports database-specific features, and works on any table in the database, not just DDIC tables. Native SQL is not portable β the same statement may not work on a different database.
Rule of thumb: use Open SQL for normal development. Use Native SQL only when you need a database-specific feature that Open SQL does not provide.
SELECT SINGLE reads exactly one record into a flat structure. Use it when you know the WHERE clause will match a single row, typically by primary key.
SELECT ... INTO TABLE reads all matching rows into an internal table. Use it when the WHERE clause may match many rows and you need all of them.
Performance note: SELECT SINGLE stops at the first match, so it is very fast if you query by primary key. SELECT ... INTO TABLE processes every match, so use the WHERE clause carefully to avoid pulling huge result sets.
sy-subrc is the system field that tells you whether the previous database operation succeeded. A value of 0 means success. Any non-zero value means failure or an unexpected result.
Without checking it, your program cannot know if a SELECT found a record, if an INSERT failed due to a duplicate key, or if an UPDATE matched no rows. The program may silently continue with invalid or empty data, causing wrong output or runtime errors further down.
In real projects, code without sy-subrc checks fails code review. It is one of the most common interview questions because it reveals whether a candidate has worked on real systems.
INNER JOIN returns only rows where the join condition matches in both tables. If a customer has no orders, that customer is excluded from the result.
LEFT OUTER JOIN returns all rows from the left table, whether or not they match in the right table. Customers without orders still appear, with empty values for the order fields.
Use INNER JOIN when you only want complete data. Use LEFT OUTER JOIN when you want all records from one side regardless of matches on the other β for example, all customers with their order totals, including customers who have ordered nothing.
In modern ABAP SQL (and in all ABAP Cloud development), every ABAP variable used inside a SQL statement must be prefixed with @. This is called the escape character.
It tells the compiler: “This is an ABAP variable, not a database field.” Without @, the compiler cannot tell the difference between city (a database column) and city (an ABAP variable).
Example: WHERE city = @lv_city (the @ tells ABAP that lv_city is the ABAP variable holding the value).
The syntax is nearly identical β SELECT, INSERT, UPDATE, DELETE, WHERE, JOIN, aggregates all work the same way. The differences are in the safety rules:
- Read access: In Cloud, you read from CDS Views or released ABAP SQL views. In Classic, you can read from any transparent table.
- Write access: In Cloud, data modification goes through RAP business objects. In Classic, you can INSERT, UPDATE, and DELETE directly.
- The @ escape: Required in Cloud and in modern Classic syntax. Optional in older Classic code.
The reason for these restrictions is Clean Core β Cloud enforces upgrade-safe code so that custom logic does not break during SAP updates.
π Practice Task for This Chapter
Complete the tasks for your path before moving to Chapter 7.
π’ Free Path (BTP Trial)
- Create a new class
ZCL_SQL_CH6in your package. - Add a method
runthat performs aSELECTfrom a CDS View (you can use a released SAP CDS View likeI_CurrencyorI_Country). - Output the results with
cl_demo_output. - Add a
sy-subrccheck that prints “Data found” or “No data found”. - Save, activate (Ctrl + F3), and run (F9). Take a screenshot.
π΅ Paid Path (SAP System)
- Open SE38 and create a report called
ZSQL_PRACTICE_CH6. - Write a full CRUD demo:
- INSERT three customer records into your
ZCUSTOMERtable from Chapter 5 - SELECT them all back with a WHERE clause filtering by city
- UPDATE one record to change the city
- DELETE one record
- Check sy-subrc after every operation
- INSERT three customer records into your
- Output a summary at the end: how many records were inserted, updated, and deleted successfully.
- Activate with Ctrl + F2 and run with F8. Take a screenshot.
- Open SE16N and confirm that your inserts and updates are visible in the table.
Test: Kon Banega Crorepati?
5 questions. βΉ1 Crore. Prove you understood Chapter 6. No pressure β restart anytime.
Explore Our Instructor-Led SAP & IT Courses
Self-study works. But if you want guided training, real project practice, and placement support β here are the courses we offer. Book a free demo before you decide.
WhatsApp us