
Sap Sqlscript
- 341 installs
- 399 repo stars
- Updated August 4, 2026
- secondsky/sap-skills
Author, debug, and optimize SAP HANA SQLScript procedures, functions, and table types for enterprise SAP data models and reporting pipelines.
About
Guides creation and refinement of SAP HANA SQLScript for enterprise workloads, covering procedures, functions, table types, cursors, and performance patterns inside secondsky/sap-skills.
- HANA SQLScript syntax and patterns
- Stored procedures and functions
- Table types and cursors
- Performance tuning for SAP workloads
- Enterprise SAP data modeling
Sap Sqlscript by the numbers
- 341 all-time installs (skills.sh)
- +34 installs in the week ending Jul 27, 2026 (Skillselion tracking)
- Ranked #160 of 911 Databases skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/secondsky/sap-skills --skill sap-sqlscriptAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 341 |
|---|---|
| repo stars | ★ 399 |
| Last updated | August 4, 2026 |
| Repository | secondsky/sap-skills ↗ |
What it does
Author, debug, and optimize SAP HANA SQLScript procedures, functions, and table types for enterprise SAP data models and reporting pipelines.
Files
SAP SQLScript Development Guide
When to Use This Skill
Use this skill when writing SQLScript procedures, anonymous blocks, table/scalar functions, AMDP methods, exception handlers, cursor logic, bulk operations, or HANA performance-sensitive database logic that should run close to the data.
Overview
SQLScript is SAP HANA's procedural extension to SQL, enabling complex data-intensive logic execution directly within the database layer. It follows the code-to-data paradigm, pushing computation to where data resides rather than moving data to the application layer.
Key Characteristics
- Case-insensitive language
- All statements end with semicolons
- Variables use colon prefix when referenced (
:variableName) - No colon when assigning values
- Use
DUMMYtable for single-row operations
Two Logic Types
| Type | Description | Execution |
|---|---|---|
| Declarative | Pure SQL sequences | Converted to data flow graphs, processed in parallel |
| Imperative | Control structures (IF, WHILE, FOR) | Processed sequentially, prevents parallel execution |
---
Table of Contents
- Overview
- Container Types
- Anonymous Blocks
- Stored Procedures
- User-Defined Functions
- Data Types
- Variable Declaration
- Control Structures
- Table Types
- Cursors
- Exception Handling
- AMDP Integration
- Performance Best Practices
- System Limits
- Debugging Tools
- Quick Reference
- Additional Resources
---
Container Types
1. Anonymous Blocks
Single-use logic not stored in the database. Useful for testing and ad-hoc execution.
DO [(<parameter_clause>)]
BEGIN [SEQUENTIAL EXECUTION]
<body>
END;Example:
DO
BEGIN
DECLARE lv_count INTEGER;
SELECT COUNT(*) INTO lv_count FROM "MYTABLE";
SELECT :lv_count AS record_count FROM DUMMY;
END;2. Stored Procedures
Reusable database objects with input/output parameters.
CREATE [OR REPLACE] PROCEDURE <procedure_name>
(
[IN <param> <datatype>],
[OUT <param> <datatype>],
[INOUT <param> <datatype>]
)
LANGUAGE SQLSCRIPT
[SQL SECURITY {DEFINER | INVOKER}]
[DEFAULT SCHEMA <schema_name>]
[READS SQL DATA | READS SQL DATA WITH RESULT VIEW <view_name>]
AS
BEGIN
<procedure_body>
END;3. User-Defined Functions
Scalar UDF - Returns single value:
CREATE FUNCTION <function_name> (<input_parameters>)
RETURNS <scalar_type>
LANGUAGE SQLSCRIPT
AS
BEGIN
<function_body>
RETURN <value>;
END;Table UDF - Returns table (read-only):
CREATE FUNCTION <function_name> (<input_parameters>)
RETURNS TABLE (<column_definitions>)
LANGUAGE SQLSCRIPT
READS SQL DATA
AS
BEGIN
RETURN SELECT ... FROM ...;
END;---
Data Types
SQLScript supports comprehensive data types for different use cases. See references/data-types.md for complete documentation including:
- Numeric types (TINYINT, INTEGER, DECIMAL, etc.)
- Character types (VARCHAR, NVARCHAR, CLOB, etc.)
- Date/Time types (DATE, TIME, TIMESTAMP, SECONDDATE)
- Binary types (VARBINARY, BLOB)
- Type conversion functions (CAST, TO_ functions)
- NULL handling patterns
---
Variable Declaration
Scalar Variables
DECLARE <variable_name> <datatype> [:= <initial_value>];
-- Examples
DECLARE lv_name NVARCHAR(100);
DECLARE lv_count INTEGER := 0;
DECLARE lv_date DATE := CURRENT_DATE;Note: Uninitialized variables default to NULL.
Table Variables
Implicit declaration:
lt_result = SELECT * FROM "MYTABLE" WHERE status = 'A';Explicit declaration:
DECLARE lt_data TABLE (
id INTEGER,
name NVARCHAR(100),
amount DECIMAL(15,2)
);Using TABLE LIKE:
DECLARE lt_copy TABLE LIKE :lt_original;Arrays
DECLARE arr INTEGER ARRAY := ARRAY(1, 2, 3, 4, 5);
-- Access: arr[1], arr[2], etc. (1-based index)
-- Note: Arrays cannot be returned from procedures---
Control Structures
IF-ELSE Statement
IF <condition1> THEN
<statements>
[ELSEIF <condition2> THEN
<statements>]
[ELSE
<statements>]
END IF;Comparison Operators:
| Operator | Meaning |
|---|---|
= | Equal to |
> | Greater than |
< | Less than |
>= | Greater than or equal |
<= | Less than or equal |
!=, <> | Not equal |
Important: IF-ELSE cannot be used within SELECT statements. Use CASE WHEN instead.
WHILE Loop
WHILE <condition> DO
<statements>
END WHILE;FOR Loop
-- Numeric range
FOR i IN 1..10 DO
<statements>
END FOR;
-- Reverse
FOR i IN REVERSE 10..1 DO
<statements>
END FOR;
-- Cursor iteration
FOR row AS <cursor_name> DO
<statements using row.column_name>
END FOR;LOOP with EXIT
LOOP
<statements>
IF <condition> THEN
BREAK;
END IF;
END LOOP;---
Table Types
Define reusable table structures:
CREATE TYPE <type_name> AS TABLE (
<column1> <datatype>,
<column2> <datatype>,
...
);Usage in procedures:
CREATE PROCEDURE get_employees (OUT et_result MY_TABLE_TYPE)
LANGUAGE SQLSCRIPT AS
BEGIN
et_result = SELECT * FROM "EMPLOYEES";
END;---
Cursors
Cursors handle result sets row by row. Pattern: Declare → Open → Fetch → Close
Performance Note: Cursors bypass the database optimizer and process rows sequentially. Use primarily with primary key-based queries. Prefer set-based operations when possible.
DECLARE CURSOR <cursor_name> FOR
SELECT <columns> FROM <table> [WHERE <condition>];
OPEN <cursor_name>;
FETCH <cursor_name> INTO <variables>;
CLOSE <cursor_name>;Complete Example:
DO
BEGIN
DECLARE lv_id INTEGER;
DECLARE lv_name NVARCHAR(100);
DECLARE CURSOR cur_employees FOR
SELECT id, name FROM "EMPLOYEES" WHERE dept = 'IT';
OPEN cur_employees;
FETCH cur_employees INTO lv_id, lv_name;
WHILE NOT cur_employees::NOTFOUND DO
-- Process row
SELECT :lv_id, :lv_name FROM DUMMY;
FETCH cur_employees INTO lv_id, lv_name;
END WHILE;
CLOSE cur_employees;
END;FOR Loop Alternative:
FOR row AS cur_employees DO
SELECT row.id, row.name FROM DUMMY;
END FOR;---
Exception Handling
EXIT HANDLER
Suspends execution and performs cleanup when exceptions occur.
DECLARE EXIT HANDLER FOR <condition_value>
<statement>;Condition values:
SQLEXCEPTION- Any SQL exceptionSQL_ERROR_CODE <number>- Specific error code
Access error details:
::SQL_ERROR_CODE- Numeric error code::SQL_ERROR_MESSAGE- Error message text
Example:
CREATE PROCEDURE safe_insert (IN iv_id INTEGER, IN iv_name NVARCHAR(100))
LANGUAGE SQLSCRIPT AS
BEGIN
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
SELECT ::SQL_ERROR_CODE AS err_code,
::SQL_ERROR_MESSAGE AS err_msg FROM DUMMY;
END;
INSERT INTO "MYTABLE" VALUES (:iv_id, :iv_name);
END;CONDITION
Associate user-defined names with error codes:
DECLARE <condition_name> CONDITION FOR SQL_ERROR_CODE <number>;
-- Example
DECLARE duplicate_key CONDITION FOR SQL_ERROR_CODE 301;
DECLARE EXIT HANDLER FOR duplicate_key
SELECT 'Duplicate key error' FROM DUMMY;SIGNAL and RESIGNAL
Throw user-defined exceptions (codes 10000-19999):
-- Throw exception
SIGNAL <condition_name> SET MESSAGE_TEXT = '<message>';
-- Re-throw in handler
RESIGNAL [<condition_name>] [SET MESSAGE_TEXT = '<message>'];Common Error Codes:
| Code | Description |
|---|---|
| 301 | Unique constraint violation |
| 1299 | No data found |
---
AMDP Integration
ABAP Managed Database Procedures allow SQLScript within ABAP classes.
Class Definition
CLASS zcl_my_amdp DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_amdp_marker_hdb. " Required interface
TYPES: BEGIN OF ty_result,
id TYPE i,
name TYPE string,
END OF ty_result,
tt_result TYPE STANDARD TABLE OF ty_result.
CLASS-METHODS: get_data
IMPORTING VALUE(iv_filter) TYPE string
EXPORTING VALUE(et_result) TYPE tt_result.
ENDCLASS.Method Implementation
CLASS zcl_my_amdp IMPLEMENTATION.
METHOD get_data BY DATABASE PROCEDURE
FOR HDB
LANGUAGE SQLSCRIPT
OPTIONS READ-ONLY
USING ztable.
et_result = SELECT id, name
FROM ztable
WHERE category = :iv_filter;
ENDMETHOD.
ENDCLASS.AMDP Restrictions
- Parameters must be pass-by-value (no RETURNING)
- Only scalar types, structures, internal tables allowed
- No nested tables or deep structures
- COMMIT/ROLLBACK not permitted
- Must use Eclipse ADT for development
- Auto-created on first invocation
---
Performance Best Practices
1. Reduce Data Volume Early
-- Good: Filter and project early
lt_filtered = SELECT col1, col2 FROM "BIGTABLE" WHERE status = 'A';
lt_result = SELECT a.col1, b.name
FROM :lt_filtered AS a
JOIN "LOOKUP" AS b ON a.id = b.id;
-- Bad: Join then filter
lt_result = SELECT a.col1, b.name
FROM "BIGTABLE" AS a
JOIN "LOOKUP" AS b ON a.id = b.id
WHERE a.status = 'A';2. Prefer Declarative Over Imperative
-- Good: Set-based operation
lt_result = SELECT id, amount * 1.1 AS new_amount FROM "ORDERS";
-- Bad: Row-by-row processing
FOR row AS cur_orders DO
UPDATE "ORDERS" SET amount = row.amount * 1.1 WHERE id = row.id;
END FOR;3. Avoid Engine Mixing
- Don't mix Row Store and Column Store tables in same query
- Avoid Calculation Engine functions with pure SQL
- Use consistent storage types
4. Use UNION ALL Instead of UNION
-- Faster when duplicates impossible or acceptable
SELECT * FROM table1 UNION ALL SELECT * FROM table2;
-- Slower: removes duplicates
SELECT * FROM table1 UNION SELECT * FROM table2;5. Avoid Dynamic SQL
-- Bad: Re-optimized each execution
EXECUTE IMMEDIATE 'SELECT * FROM ' || :lv_table;
-- Good: Static SQL with parameters
SELECT * FROM "MYTABLE" WHERE id = :lv_id;6. Position Imperative Logic Last
Place control structures at the end of procedures to maximize parallel processing of declarative statements.
---
System Limits
| Limit | Value |
|---|---|
| Table locks per transaction | 16,383 |
| Tables in a statement | 4,095 |
| SQL statement length | 2 GB |
| Procedure size | Bounded by SQL statement length (2 GB) |
Note: Actual limits may vary by HANA version. Consult SAP documentation for version-specific limits.
---
Debugging Tools
- SQLScript Debugger - SAP Web IDE / Business Application Studio
- Plan Visualizer - Analyze execution plans
- Expensive Statement Trace - Identify bottlenecks
- SQL Analyzer - Query optimization recommendations
---
Quick Reference
String Concatenation
lv_result = lv_str1 || ' ' || lv_str2;NULL Handling
COALESCE(value, default_value)
IFNULL(value, default_value)
NULLIF(value1, value2)Date Operations
ADD_DAYS(date, n)
ADD_MONTHS(date, n)
DAYS_BETWEEN(date1, date2)
CURRENT_DATE
CURRENT_TIMESTAMPType Conversion
CAST(value AS datatype)
TO_VARCHAR(value)
TO_INTEGER(value)
TO_DATE(string, 'YYYY-MM-DD')
TO_TIMESTAMP(string, 'YYYY-MM-DD HH24:MI:SS')---
Related Skills
For comprehensive SAP development, combine this skill with:
| Skill | Use Case |
|---|---|
| sap-abap | ABAP programming patterns for AMDP context |
| sap-abap-cds | CDS views that consume SQLScript procedures |
| sap-cap-capire | CAP framework database procedures integration |
| sap-hana-cli | HANA CLI for procedure deployment and testing |
| sap-btp-cloud-platform | BTP deployment of HANA artifacts |
---
Bundled Resources
Reference Documentation
references/skill-reference-guide.md- Index of all references with quick navigationreferences/glossary.md- SQLScript terminology and conceptsreferences/syntax-reference.md- Complete SQLScript syntax referencereferences/built-in-functions.md- Built-in functions catalogreferences/data-types.md- Data types and conversionreferences/exception-handling.md- Exception handling patternsreferences/amdp-integration.md- AMDP integration patternsreferences/performance-guide.md- Optimization techniquesreferences/advanced-features.md- Lateral joins, JSON, query hints, currency conversionreferences/troubleshooting.md- Common errors and solutions
Production-Ready Templates
Copy and customize these templates for common patterns:
templates/simple-procedure.sql- Basic stored procedure with error handlingtemplates/procedure-with-error-handling.sql- Comprehensive error handling patternstemplates/table-function.sql- Table UDF with validationtemplates/scalar-function.sql- Scalar UDF examplestemplates/amdp-class.abap- Complete AMDP class boilerplatetemplates/amdp-procedure.sql- AMDP implementation templatetemplates/cursor-iteration.sql- Cursor patterns (classic and FOR loop)templates/bulk-operations.sql- High-performance bulk operations
Specialized Agents
- sqlscript-analyzer - Analyze code for performance issues and best practices
- procedure-generator - Generate procedures interactively from requirements
- amdp-helper - Assist with AMDP class creation and debugging
Slash Commands
/sqlscript-validate- Validate code with auto-fix capability/sqlscript-optimize- Performance analysis and optimization suggestions/sqlscript-convert- Convert between standalone and AMDP formats
Validation Hooks
Automatic code quality checks on Write/Edit operations:
- Error handling completeness
- Security vulnerabilities
- Performance anti-patterns
- Naming conventions
- AMDP compliance
SAP SQLScript Skill
Comprehensive SQLScript development skill for SAP HANA database programming.
Capability Index
| Capability | Status |
|---|---|
| Commands | 4: /sqlscript-convert, /sqlscript-optimize, /sqlscript-setup, /sqlscript-validate |
| Agents | 3: amdp-helper, procedure-generator, sqlscript-analyzer |
| Hooks | Yes: hooks/hooks.json |
| MCP | No |
| LSP | No |
| Source Freshness | last_verified: 2026-05-31; SQLScript setup command and hook behavior validated locally. |
| Verification | npm run validate; live HANA execution-plan checks pending. |
Overview
This skill provides complete guidance for SQLScript development, including:
- Stored procedures and user-defined functions
- Anonymous blocks for ad-hoc execution
- Control structures and exception handling
- Built-in functions (string, date, numeric, aggregate, window)
- AMDP (ABAP Managed Database Procedures) integration
- Performance optimization techniques
- Troubleshooting common errors
Plugin Components
This plugin includes specialized agents, commands, templates, and validation hooks for comprehensive SQLScript development support.
Agents
| Agent | Purpose | Trigger Phrases |
|---|---|---|
| sqlscript-analyzer | Analyze SQLScript code for performance issues and best practices | "analyze my SQLScript", "review HANA procedure", "check procedure performance" |
| procedure-generator | Generate procedures interactively (asks clarifying questions first) | "create a SQLScript procedure", "generate HANA procedure", "write stored procedure for" |
| amdp-helper | Help with AMDP class creation and debugging | "create an AMDP class", "help with AMDP", "ABAP managed database procedure" |
Slash Commands
| Command | Usage | Description |
|---|---|---|
/sqlscript-validate | /sqlscript-validate [file] --fix | Validate SQLScript code with auto-fix capability |
/sqlscript-optimize | /sqlscript-optimize [file] --fix | Analyze performance issues with auto-fix |
/sqlscript-convert | `/sqlscript-convert [file] --to amdp\ | standalone\ |
Templates
Production-ready templates with full error handling:
| Template | Description |
|---|---|
simple-procedure.sql | Basic stored procedure with error handling and input validation |
procedure-with-error-handling.sql | Comprehensive error handling with logging and custom conditions |
table-function.sql | Table UDF with parameter validation |
scalar-function.sql | Scalar UDF examples (name formatting, calculations, status mapping) |
amdp-class.abap | Complete AMDP class with interface and multiple method types |
amdp-procedure.sql | AMDP implementation with type mapping reference |
cursor-iteration.sql | Cursor patterns (classic, FOR loop, nested, set-based alternatives) |
bulk-operations.sql | Bulk INSERT, UPDATE, DELETE, MERGE, batch processing |
Validation Hooks
Automatic code quality checks on Write/Edit operations:
- Error handling completeness (EXIT HANDLER, cursor management)
- Security vulnerabilities (hardcoded schemas, SQL injection)
- Performance anti-patterns (cursors in loops, SELECT *, missing WHERE)
- Naming conventions (lv_, lt_, iv_, et_ prefixes)
- AMDP compliance (interface, USING clause, pass-by-value)
Keywords
Technology Terms
- SQLScript
- SAP HANA
- HANA database
- SAP HANA Cloud
- SAP HANA Platform
- SQL Script
- HANA SQL
- database procedure
- stored procedure
- user-defined function
- UDF
- scalar UDF
- table UDF
- table function
- anonymous block
Programming Concepts
- code-to-data paradigm
- declarative logic
- imperative logic
- procedural SQL
- cursor
- table variable
- table type
- array
- exception handling
- EXIT HANDLER
- SIGNAL
- RESIGNAL
- CONDITION
HANA Specific
- Column Store
- Row Store
- Calculation Engine
- Plan Visualizer
- Expensive Statement Trace
- SQL Analyzer
- HANA Studio
- SAP Web IDE
- Business Application Studio
ABAP Integration
- AMDP
- ABAP Managed Database Procedures
- IF_AMDP_MARKER_HDB
- BY DATABASE PROCEDURE
- code pushdown
- ABAP CDS
- S/4HANA
Control Structures
- IF THEN ELSE
- ELSEIF
- WHILE DO
- FOR loop
- LOOP
- BREAK
- CONTINUE
- CASE WHEN
Data Types
- INTEGER
- BIGINT
- SMALLINT
- TINYINT
- DECIMAL
- DOUBLE
- REAL
- VARCHAR
- NVARCHAR
- ALPHANUM
- DATE
- TIME
- TIMESTAMP
- SECONDDATE
- CLOB
- BLOB
Built-in Functions
- string functions
- date functions
- numeric functions
- aggregate functions
- window functions
- conversion functions
- CONCAT
- SUBSTRING
- LENGTH
- TRIM
- UPPER
- LOWER
- ADD_DAYS
- DAYS_BETWEEN
- CURRENT_DATE
- CURRENT_TIMESTAMP
- TO_VARCHAR
- TO_DATE
- TO_INTEGER
- CAST
- SUM
- COUNT
- AVG
- MIN
- MAX
- ROW_NUMBER
- RANK
- DENSE_RANK
- LEAD
- LAG
- PARTITION BY
- TO_DATS
- TO_TIMS
- CONVERT_CURRENCY
- session_context
- record_count
- lateral join
- JSON functions
- query hints
- APPLY_FILTER
- ARRAY_AGG
- TRIM_ARRAY
- CE functions
- CONTINUE HANDLER
- Code Analyzer
- Plan Profiler
- Pragmas
Error Handling
- SQL_ERROR_CODE
- SQL_ERROR_MESSAGE
- DECLARE EXIT HANDLER
- SQLEXCEPTION
- error code 301
- unique constraint violation
- error logging
Performance
- query optimization
- parallel execution
- UNION ALL vs UNION
- avoid dynamic SQL
- reduce data volume
- set-based operations
- execution plan
- index optimization
Common Tasks
- create procedure
- create function
- create table type
- declare variable
- declare cursor
- fetch cursor
- insert data
- update data
- delete data
- select into
- execute immediate
- dynamic SQL
Error Messages
- invalid column name
- invalid table name
- variable not defined
- cursor not open
- memory allocation failed
- insufficient privilege
- unique constraint violation
- foreign key violation
Plugin Features
- sqlscript-validate
- sqlscript-optimize
- sqlscript-convert
- sqlscript-analyzer
- procedure-generator
- amdp-helper
- auto-fix
- code validation
- performance analysis
File Structure
sap-sqlscript/
├── .claude-plugin/
│ └── plugin.json # Plugin manifest
├── skills/sap-sqlscript/
│ ├── SKILL.md # Main skill file
│ ├── README.md # This file
│ ├── references/
│ │ ├── skill-reference-guide.md # Index of all references
│ │ ├── glossary.md # SQLScript terminology
│ │ ├── syntax-reference.md # Complete syntax patterns
│ │ ├── built-in-functions.md # All function categories
│ │ ├── data-types.md # Data types and conversion
│ │ ├── exception-handling.md # Error handling patterns
│ │ ├── amdp-integration.md # AMDP implementation guide
│ │ ├── performance-guide.md # Optimization techniques
│ │ ├── advanced-features.md # Lateral joins, JSON, query hints
│ │ └── troubleshooting.md # Common errors and solutions
│ └── templates/
│ ├── simple-procedure.sql
│ ├── procedure-with-error-handling.sql
│ ├── table-function.sql
│ ├── scalar-function.sql
│ ├── amdp-class.abap
│ ├── amdp-procedure.sql
│ ├── cursor-iteration.sql
│ └── bulk-operations.sql
├── agents/
│ ├── sqlscript-analyzer.md # Performance analysis agent
│ ├── procedure-generator.md # Interactive procedure generator
│ └── amdp-helper.md # AMDP assistance agent
├── commands/
│ ├── sqlscript-validate.md # Validation command
│ ├── sqlscript-optimize.md # Optimization command
│ └── sqlscript-convert.md # Conversion command
└── hooks/
└── hooks.json # Validation hooks configurationUsage
This skill is automatically triggered when working with:
- SAP HANA stored procedures
- SQLScript development
- AMDP classes in ABAP
- HANA database functions
- SQL performance optimization in HANA
Using Agents
Agents are triggered automatically based on context:
"Analyze my procedure for performance issues"
→ sqlscript-analyzer agent reviews your code
"Create a stored procedure to calculate order totals"
→ procedure-generator agent asks clarifying questions, then generates
"Help me create an AMDP class for customer data"
→ amdp-helper agent guides you through AMDP creationUsing Commands
Commands are invoked directly:
/sqlscript-validate src/procedures/calc_totals.sql
/sqlscript-validate src/procedures/calc_totals.sql --fix
/sqlscript-optimize src/procedures/process_orders.sql
/sqlscript-convert src/procedures/get_data.sql --to amdpUsing Templates
Templates are copied and customized:
"Create a new procedure using the simple-procedure template"
→ Claude copies templates/simple-procedure.sql and customizes it
"I need a bulk update operation"
→ Claude uses templates/bulk-operations.sql patternsDocumentation Sources
The skill content is derived from official SAP documentation and community resources:
- SAP HANA SQLScript Reference (PDF)
- URL:
https://help.sap.com/doc/6254b3bb439c4f409a979dc407b49c9b/2.0.08/en-US/SAP_HANA_SQL_Script_Reference_en.pdf
- SAP HANA Cloud SQLScript Reference
- URL:
https://help.sap.com/docs/hana-cloud-database/sap-hana-cloud-sap-hana-sqlscript-reference/
- SAP HANA SQL Functions
- URL:
https://help.sap.com/docs/SAP_HANA_PLATFORM/4fe29514fd584807ac9f2a04f6754767/20a61f29751910149f99f0300dd95cd9.html
- SAP Tutorials - SQLScript
- URL:
https://developers.sap.com/tutorial-navigator.html?tag=programming-tool:sqlscript
- AMDP Cheat Sheet (SAP Samples)
- URL:
https://github.com/SAP-samples/abap-cheat-sheets/blob/main/12_AMDP.md
- SAP Community - SQL Scripts in SAP HANA
- URL:
https://community.sap.com/t5/technology-blog-posts-by-members/sql-scripts-in-sap-hana/ba-p/13738376
Version Information
- Skill Version: 2.1.0
- SAP HANA Platform: 2.0 SPS08
- SAP HANA Cloud: QRC 1/2026
- AMDP: ABAP 7.40 SP05+
- Last Updated: 2026-05-31
License
GPL-3.0
SQLScript Advanced Features Reference
Table of Contents
- Loop Variations
- DO n TIMES Loop
- DO n TIMES with Counter
- Lateral Joins
- Syntax
- Example
- Query Hints
- Common Hints
- Hint Syntax
- JSON Functions
- JSON_VALUE
- JSON_QUERY
- JSON_TABLE
- Spatial Functions
- Spatial Data Types
- Common Functions
- Time-Series Functions
- Moving Averages
- Series Generation
- SAP-Specific Conversion Functions
- TO_DATS
- TO_TIMS
- Usage in Date Logic
- CONVERT_CURRENCY Function
- Syntax
- Example
- Complete Example with Dynamic Date
- Session and System Functions
- session_context()
- record_count()
- current_line_number
- Parallel Mode Exit Triggers
- Best Practice
- SET Operations Alternatives
- INTERSECT Alternative
- EXCEPT Alternative
- Procedure Management
- DROP PROCEDURE
- ALTER PROCEDURE Limitation
- DROP FUNCTION
- Security Considerations
- SQL Injection Prevention
- AMDP Advantages Over Procedure Proxy
- APPLY_FILTER Function
- Syntax
- Parameters
- Example
- Use Cases
- Array Functions
- ARRAY_AGG
- TRIM_ARRAY
- Array Concatenation
- CARDINALITY
- CONTINUE HANDLER
- Syntax
- Example
- EXIT vs CONTINUE Handler
- CE Functions (Calculation Engine)
- Common CE Functions
- CE_PROJECTION Example
- SQLScript Analysis Tools
- SQLScript Code Analyzer
- SQLScript Plan Profiler
- SQLScript Code Coverage
- SQLScript Pragmas
- Syntax
- Common Pragmas
- Example
Loop Variations
DO n TIMES Loop
Repeat a block a fixed number of times:
DO 10 TIMES
BEGIN
INSERT INTO "LOG_TABLE" (message) VALUES ('Iteration');
END;DO n TIMES with Counter
DO
BEGIN
DECLARE i INTEGER := 0;
WHILE :i < 10 DO
INSERT INTO "LOG_TABLE" (counter) VALUES (:i);
i := :i + 1;
END WHILE;
END;---
Lateral Joins
Lateral joins enable subqueries in the FROM clause to reference columns from preceding table expressions.
Syntax
SELECT <columns>
FROM <table1>,
LATERAL (<subquery referencing table1>) AS <alias>
WHERE <condition>;Example
SELECT TA.a1, TB.b1
FROM TA,
LATERAL (SELECT b1, b2 FROM TB WHERE b3 = TA.a3) TB
WHERE TA.a2 = TB.b2;Use Cases:
- Correlated subqueries in FROM clause
- Row-by-row transformations
- Dependent data lookups
---
Query Hints
Provide optimization guidance to the SQL parser.
Common Hints
-- Force parallel execution
SELECT /*+ PARALLEL_EXECUTION */ * FROM "LARGE_TABLE";
-- Use OLAP execution plan
SELECT /*+ USE_OLAP_PLAN */ * FROM "ANALYTICS_TABLE";
-- Disable parallel execution
SELECT /*+ NO_PARALLEL */ * FROM "SMALL_TABLE";
-- Route to specific engine
SELECT /*+ ROUTE_TO(VOLUME_ID) */ * FROM "TABLE";Hint Syntax
SELECT /*+ HINT1 HINT2 */ <columns> FROM <table>;Note: Use hints sparingly. The optimizer usually makes good decisions.
Version Note: Hint availability and syntax may vary across HANA versions. Verify hint support in your specific HANA version's documentation.
---
JSON Functions
SAP HANA provides functions to parse and extract data from JSON objects.
JSON_VALUE
Extract scalar value from JSON:
SELECT JSON_VALUE('{"name": "John", "age": 30}', '$.name') FROM DUMMY;
-- Returns: JohnJSON_QUERY
Extract JSON object or array:
SELECT JSON_QUERY('{"items": [1, 2, 3]}', '$.items') FROM DUMMY;
-- Returns: [1, 2, 3]JSON_TABLE
Convert JSON to relational format:
SELECT jt.*
FROM JSON_TABLE(
'[{"id": 1, "name": "A"}, {"id": 2, "name": "B"}]',
'$[*]'
COLUMNS (
id INTEGER PATH '$.id',
name VARCHAR(100) PATH '$.name'
)
) AS jt;---
Spatial Functions
SAP HANA supports geospatial data processing.
Spatial Data Types
ST_POINT- Point geometryST_LINESTRING- Line geometryST_POLYGON- Polygon geometryST_GEOMETRY- Generic geometry
Common Functions
-- Create point
SELECT NEW ST_POINT(10.0, 20.0) FROM DUMMY;
-- Calculate distance
SELECT point1.ST_DISTANCE(point2) FROM "LOCATIONS";
-- Check containment
SELECT polygon.ST_CONTAINS(point) FROM "AREAS";
-- Calculate area
SELECT polygon.ST_AREA() FROM "REGIONS";---
Time-Series Functions
Moving Averages
SELECT date_col,
AVG(value) OVER (ORDER BY date_col ROWS 7 PRECEDING) AS moving_avg_7day
FROM "TIME_SERIES";Series Generation
SELECT GENERATED_PERIOD_START, GENERATED_PERIOD_END
FROM SERIES_GENERATE_TIMESTAMP('INTERVAL 1 DAY', '2024-01-01', '2024-12-31');Performance Note: Generating large date ranges (multiple years with fine granularity) can impact memory and performance. For large ranges, consider chunking into smaller periods or materializing results into a calendar table.
---
SAP-Specific Conversion Functions
TO_DATS
Convert DATE to SAP date format (YYYYMMDD):
SELECT TO_DATS(CURRENT_DATE) FROM DUMMY;
-- Returns: 20241123TO_TIMS
Convert TIME to SAP time format (HHMMSS):
SELECT TO_TIMS(CURRENT_TIME) FROM DUMMY;
-- Returns: 143022Usage in Date Logic
DECLARE lv_date NVARCHAR(10);
SELECT SUBSTRING(TO_DATS(CURRENT_DATE), 1, 6) INTO lv_date FROM DUMMY;
-- Returns: 202411 (YYYYMM)---
CONVERT_CURRENCY Function
Currency conversion using exchange rates from SAP tables.
Syntax
CONVERT_CURRENCY(
AMOUNT => <amount>,
SOURCE_UNIT => <source_currency>,
TARGET_UNIT => <target_currency>,
SCHEMA => <schema_name>,
REFERENCE_DATE => <date>,
CLIENT => <client>,
CONVERSION_TYPE => <type>
)Example
SELECT
doc_currcy,
deb_cre_dc,
CONVERT_CURRENCY(
AMOUNT => deb_cre_dc,
SOURCE_UNIT => doc_currcy,
SCHEMA => 'SAPABAP1',
TARGET_UNIT => 'EUR',
REFERENCE_DATE => '2024-01-01',
CLIENT => '100',
CONVERSION_TYPE => 'EURX'
) AS deb_cre_eur
FROM "FINANCIAL_DATA";Complete Example with Dynamic Date
CREATE PROCEDURE convert_amounts (OUT outTab TABLE(...))
LANGUAGE SQLSCRIPT AS
BEGIN
DECLARE lv_date NVARCHAR(10);
SELECT SUBSTRING(TO_DATS(CURRENT_DATE), 1, 6) INTO lv_date FROM DUMMY;
IF :lv_date <= '202106' THEN
lv_date := '2020-12-01';
ELSE
SELECT TO_NVARCHAR(CURRENT_DATE) INTO lv_date FROM DUMMY;
END IF;
outTab = SELECT
doc_currcy,
CONVERT_CURRENCY(
AMOUNT => amount,
SOURCE_UNIT => doc_currcy,
SCHEMA => 'SAPABAP1',
TARGET_UNIT => 'EUR',
REFERENCE_DATE => :lv_date,
CLIENT => '100',
CONVERSION_TYPE => 'EURX'
) AS converted_amount
FROM "SOURCE_TABLE";
END;---
Session and System Functions
session_context()
Retrieve session context values. Standard keys include:
| Key | Description |
|---|---|
'CLIENT' | SAP client (mandant) |
'APPLICATIONUSER' | Application user name |
'LOCALE' | Session locale |
'LOCALE_SAP' | SAP locale setting |
-- Get client (SAP system)
SELECT SESSION_CONTEXT('CLIENT') FROM DUMMY;
-- Get application user
SELECT SESSION_CONTEXT('APPLICATIONUSER') FROM DUMMY;
-- Get SAP locale
SELECT SESSION_CONTEXT('LOCALE_SAP') FROM DUMMY;Note: Custom session variables can be set using SET SESSION '<key>' = '<value>' and retrieved with SESSION_CONTEXT. Check SAP documentation for version-specific available keys.record_count()
Get row count of table variable:
DECLARE lt_data TABLE (...);
lt_data = SELECT * FROM "TABLE";
lv_count = RECORD_COUNT(:lt_data);current_line_number
Get current line number in FOR loop:
FOR i IN 1..10 DO
SELECT :i AS line_number FROM DUMMY; -- or use CURRENT_LINE_NUMBER
END FOR;---
Parallel Mode Exit Triggers
SQLScript exits parallel execution mode when encountering:
| Trigger | Description |
|---|---|
| Local scalar variables | Variables block parallel data flow |
| Scalar parameters in expressions | Parameters passed to expressions |
| DML/DDL in processing blocks | INSERT, UPDATE, DELETE, CREATE |
| Imperative logic | IF, WHILE, FOR, LOOP |
| Unassigned SQL statements | SELECT without assignment |
Best Practice
CREATE PROCEDURE optimized ()
LANGUAGE SQLSCRIPT AS
BEGIN
-- PARALLEL SECTION: Declarative statements first
lt_data1 = SELECT * FROM "TABLE1" WHERE active = 1;
lt_data2 = SELECT * FROM "TABLE2" WHERE status = 'A';
lt_joined = SELECT * FROM :lt_data1 a JOIN :lt_data2 b ON a.id = b.id;
-- SEQUENTIAL SECTION: Imperative logic last
FOR row AS (SELECT * FROM :lt_joined) DO
IF row.amount > 1000 THEN
-- Process high-value items
END IF;
END FOR;
END;---
SET Operations Alternatives
INTERSECT Alternative
Replace INTERSECT with JOIN for better Column Engine utilization:
Original (slower):
SELECT column_a FROM table_1
INTERSECT
SELECT column_a FROM table_2;Optimized (faster):
SELECT DISTINCT table_1.column_a
FROM table_1
JOIN table_2 ON table_1.column_a = table_2.column_a;Note: JOIN approach works when column_a has no NULL values. Handle NULLs separately if needed.
EXCEPT Alternative
Replace EXCEPT with LEFT JOIN:
Original:
SELECT id FROM table_a
EXCEPT
SELECT id FROM table_b;Optimized:
SELECT DISTINCT a.id
FROM table_a a
LEFT JOIN table_b b ON a.id = b.id
WHERE b.id IS NULL;NULL Handling Caveat: The LEFT JOIN approach does not correctly handle NULL values. In SQL, NULL = NULL evaluates to UNKNOWN, not TRUE. If the column may contain NULLs, use this pattern instead:>
```sql
SELECT DISTINCT a.id
FROM table_a a
LEFT JOIN table_b b ON a.id = b.id OR (a.id IS NULL AND b.id IS NULL)
WHERE b.id IS NULL AND NOT (a.id IS NULL AND EXISTS (SELECT 1 FROM table_b WHERE id IS NULL));
```
>
Alternatively, keep the original EXCEPT for NULL-safe semantics when correctness outweighs performance.
---
Procedure Management
DROP PROCEDURE
DROP PROCEDURE <schema_name>.<procedure_name>;
DROP PROCEDURE <procedure_name> CASCADE;ALTER PROCEDURE Limitation
Important: ALTER PROCEDURE cannot change the number or types of parameters. You must DROP and recreate the procedure:
-- Cannot do this:
ALTER PROCEDURE my_proc ADD PARAMETER (new_param INTEGER);
-- Must do this instead:
DROP PROCEDURE my_proc;
CREATE PROCEDURE my_proc (old_param INTEGER, new_param INTEGER) ...Schema Change Strategy: For zero-downtime deployments, consider versioned procedure naming (e.g.,my_proc_v2) or wrapper procedures. Seereferences/troubleshooting.mdfor error handling patterns when managing procedure dependencies.
DROP FUNCTION
DROP FUNCTION <schema_name>.<function_name>;
DROP FUNCTION <function_name> CASCADE;---
Security Considerations
SQL Injection Prevention
Dynamic SQL opens potential for unauthorized queries and SQL injection:
Vulnerable Code:
-- DANGEROUS: User input directly in SQL string
lv_sql := 'SELECT * FROM ' || :user_table || ' WHERE id = ' || :user_id;
EXECUTE IMMEDIATE :lv_sql;Safe Alternatives:
1. Use static SQL with parameters:
SELECT * FROM "FIXED_TABLE" WHERE id = :user_id;2. Validate input against whitelist:
IF :user_table NOT IN ('TABLE_A', 'TABLE_B', 'TABLE_C') THEN
SIGNAL SQL_ERROR_CODE 10001 SET MESSAGE_TEXT = 'Invalid table name';
END IF;3. Use USING clause for parameters:
EXECUTE IMMEDIATE 'SELECT * FROM TABLE WHERE id = ?' USING :user_id;---
AMDP Advantages Over Procedure Proxy
| Feature | AMDP | Procedure Proxy |
|---|---|---|
| Development approach | Top-down (ABAP first) | Bottom-up (DB first) |
| Lifecycle management | Automatic with ABAP transport | Manual DB deployment |
| HANA access required | No | Yes |
| Procedure creation | On first invocation | Manual activation |
| Code location | In ABAP class | Separate DB object |
| Version control | ABAP repository | Separate tracking |
| Transport | With ABAP objects | Separate transport |
---
APPLY_FILTER Function
Dynamic filtering function that applies a filter string to a table, table variable, or view at runtime.
Syntax
APPLY_FILTER(<dataset>, <filter_string>)Parameters
| Parameter | Description |
|---|---|
<dataset> | Table, view, calculation view, or table variable |
<filter_string> | WHERE clause condition as string |
Example
CREATE PROCEDURE dynamic_filter (
IN iv_filter NVARCHAR(500),
OUT et_result TABLE (id INTEGER, name NVARCHAR(100))
)
LANGUAGE SQLSCRIPT AS
BEGIN
et_result = APPLY_FILTER("CUSTOMERS", :iv_filter);
END;
-- Call with dynamic filter
CALL dynamic_filter('country = ''US'' AND status = ''ACTIVE''', ?);Use Cases
- User-defined search criteria
- Dynamic report filtering
- Configurable data extraction
Note: Prefer APPLY_FILTER over dynamic SQL with 'IN' clauses for better performance and security.
---
Array Functions
ARRAY_AGG
Aggregates column values into an array:
DECLARE arr_ids INTEGER ARRAY;
-- Convert table column to array
arr_ids = ARRAY_AGG(:lt_data.id ORDER BY id ASC);
-- With ordering
arr_names = ARRAY_AGG(:lt_employees.name ORDER BY name DESC);Note: ARRAY_AGG overwrites existing array contents; it doesn't append.
TRIM_ARRAY
Removes elements from the end of an array:
DECLARE arr INTEGER ARRAY := ARRAY(1, 2, 3, 4, 5);
-- Remove last 2 elements
arr = TRIM_ARRAY(:arr, 2);
-- Result: ARRAY(1, 2, 3)Array Concatenation
Combine two arrays using CONCAT or ||:
DECLARE arr1 INTEGER ARRAY := ARRAY(1, 2, 3);
DECLARE arr2 INTEGER ARRAY := ARRAY(4, 5, 6);
-- Concatenate arrays
arr_combined = :arr1 || :arr2;
-- Result: ARRAY(1, 2, 3, 4, 5, 6)CARDINALITY
Get the number of elements in an array:
DECLARE arr INTEGER ARRAY := ARRAY(10, 20, 30);
DECLARE lv_count INTEGER;
lv_count = CARDINALITY(:arr);
-- Result: 3
-- Check if array is empty
IF CARDINALITY(:arr) = 0 THEN
-- Array is empty
END IF;---
CONTINUE HANDLER
Unlike EXIT HANDLER which terminates execution, CONTINUE HANDLER allows execution to continue after handling the exception.
Syntax
DECLARE CONTINUE HANDLER FOR <condition>
<statement>;Example
CREATE PROCEDURE process_with_continue ()
LANGUAGE SQLSCRIPT AS
BEGIN
DECLARE lv_errors INTEGER := 0;
-- Continue processing even after errors
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
BEGIN
lv_errors := :lv_errors + 1;
-- Log error but continue
INSERT INTO "ERROR_LOG" VALUES (::SQL_ERROR_CODE, ::SQL_ERROR_MESSAGE);
END;
-- These will all be attempted even if some fail
INSERT INTO "TABLE1" VALUES (1, 'A');
INSERT INTO "TABLE2" VALUES (2, 'B'); -- May fail
INSERT INTO "TABLE3" VALUES (3, 'C'); -- Still executed
-- Check total errors
IF :lv_errors > 0 THEN
SELECT :lv_errors || ' errors occurred' FROM DUMMY;
END IF;
END;EXIT vs CONTINUE Handler
| Feature | EXIT HANDLER | CONTINUE HANDLER |
|---|---|---|
| After handling | Execution stops | Execution continues |
| Use case | Critical errors | Recoverable errors |
| Subsequent code | Not executed | Executed |
---
CE Functions (Calculation Engine)
Deprecation Notice: CE Functions are legacy features. SAP recommends using standard SQL instead for new development.
CE Functions provide direct access to the HANA Calculation Engine:
Common CE Functions
| Function | Purpose |
|---|---|
CE_PROJECTION | Select/rename columns, apply filters |
CE_JOIN | Join tables |
CE_LEFT_OUTER_JOIN | Left outer join |
CE_UNION_ALL | Union tables |
CE_COLUMN_TABLE | Access column table |
CE_CALC | Calculated columns |
CE_AGGREGATION | Aggregate data |
CE_PROJECTION Example
-- Restrict columns and apply filter
lt_filtered = CE_PROJECTION(
:lt_products,
["PRODUCTID", "PRICE", "NAME"],
'"PRICE" > 50'
);Recommendation: Use standard SQL SELECT statements instead of CE functions for better maintainability and optimizer support.
---
SQLScript Analysis Tools
SQLScript Code Analyzer
Identifies code quality, security, and performance issues.
Analysis Procedures:
-- Analyze existing objects
CALL SYS.ANALYZE_SQLSCRIPT_OBJECTS('SCHEMA_NAME', 'PROCEDURE_NAME', NULL);
-- Analyze source code before creation
CALL SYS.ANALYZE_SQLSCRIPT_DEFINITION('<source_code>');Common Analysis Rules:
| Rule | Description |
|---|---|
UNCHECKED_SQL_INJECTION_SAFETY | Potential SQL injection vulnerability |
USE_OF_DYNAMIC_SQL | Dynamic SQL detected |
UNNECESSARY_VARIABLE | Variable declared but not used |
UNUSED_PARAMETER | Parameter not used in procedure |
SQLScript Plan Profiler
Performance profiling for SQLScript execution.
Enable Profiling:
ALTER SYSTEM ALTER CONFIGURATION ('indexserver.ini', 'SYSTEM')
SET ('sqlscript', 'plan_profiler_enabled') = 'true';View Results:
SELECT * FROM M_SQLSCRIPT_PLAN_PROFILER_RESULTS
WHERE PROCEDURE_NAME = 'MY_PROCEDURE';SQLScript Code Coverage
Tracks which statements are executed during testing.
-- Enable code coverage
ALTER SYSTEM ALTER CONFIGURATION ('indexserver.ini', 'SYSTEM')
SET ('sqlscript', 'code_coverage') = 'true';
-- View coverage results
SELECT * FROM M_SQLSCRIPT_CODE_COVERAGE_RESULTS;---
SQLScript Pragmas
Compiler directives that control SQLScript behavior.
Syntax
DO
BEGIN
PRAGMA <pragma_name> = <value>;
-- Code affected by pragma
END;Common Pragmas
| Pragma | Purpose |
|---|---|
AUTONOMOUS_TRANSACTION | Execute in separate transaction |
SUPPRESS_WARNINGS | Suppress specific warnings |
Example
CREATE PROCEDURE autonomous_logging (IN iv_message NVARCHAR(500))
LANGUAGE SQLSCRIPT AS
BEGIN
PRAGMA AUTONOMOUS_TRANSACTION = ON;
-- This INSERT commits independently
INSERT INTO "AUDIT_LOG" (message, logged_at)
VALUES (:iv_message, CURRENT_TIMESTAMP);
END;
AMDP (ABAP Managed Database Procedures) Integration Guide
Table of Contents
- Overview
- Key Benefits
- Prerequisites
- AMDP Class Structure
- Interface Definition
- Method Definition
- Method Implementation
- Data Type Mapping
- ABAP to SQLScript Types
- Table Types
- Complex Types
- AMDP Method Types
- Procedures
- Functions
- CDS Views
- Implementation Examples
- Simple Procedure
- Table Function
- Using CDS Entities
- Advanced Features
- Cursor Operations
- Dynamic SQL
- Exception Handling
- Performance Hints
- Best Practices
- Common Pitfalls
- Debugging AMDP
- Transport and Deployment
- Performance Considerations
- AMDP vs CDS View vs HANA View
Overview
ABAP Managed Database Procedures (AMDP) allow developers to write SQLScript code directly within ABAP classes. Introduced in ABAP 7.40 SP05, AMDP enables the code-to-data paradigm from the ABAP layer, pushing data-intensive operations to the SAP HANA database.
---
Key Benefits
| Benefit | Description |
|---|---|
| Performance | Execute logic in database rather than application server |
| Simplified Lifecycle | Procedures auto-created on first invocation |
| ABAP Integration | Use ABAP types and data structures |
| Version Control | Stored with ABAP code in transport system |
| No HANA Access Required | Develop without direct database access |
---
Prerequisites
- SAP NetWeaver 7.40 SP05 or higher
- SAP HANA database
- ABAP Development Tools (Eclipse ADT)
Important: AMDP classes cannot be edited in SAP GUI. Eclipse ADT is required.
---
Basic Structure
1. Interface Declaration
Every AMDP class must implement the IF_AMDP_MARKER_HDB interface:
CLASS zcl_my_amdp DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_amdp_marker_hdb. " Required marker interface
" Method declarations...
ENDCLASS.2. Method Declaration
AMDP methods are declared like regular ABAP methods:
CLASS zcl_my_amdp DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_amdp_marker_hdb.
" Instance method
METHODS get_sales_data
IMPORTING VALUE(iv_year) TYPE gjahr
EXPORTING VALUE(et_result) TYPE tt_sales.
" Static method
CLASS-METHODS get_customer_count
IMPORTING VALUE(iv_country) TYPE land1
RETURNING VALUE(rv_count) TYPE i.
ENDCLASS.3. Method Implementation
Use the BY DATABASE PROCEDURE or BY DATABASE FUNCTION syntax:
CLASS zcl_my_amdp IMPLEMENTATION.
METHOD get_sales_data BY DATABASE PROCEDURE
FOR HDB
LANGUAGE SQLSCRIPT
OPTIONS READ-ONLY
USING vbak vbap.
" SQLScript code here
et_result = SELECT vbeln, posnr, matnr, kwmeng
FROM vbak
JOIN vbap ON vbak.vbeln = vbap.vbeln
WHERE gjahr = :iv_year;
ENDMETHOD.
ENDCLASS.---
Syntax Elements
BY DATABASE PROCEDURE
METHOD <method_name> BY DATABASE PROCEDURE
FOR HDB
LANGUAGE SQLSCRIPT
[OPTIONS <options>]
[USING <db_entities>].| Element | Description |
|---|---|
FOR HDB | Target database (currently only HDB supported) |
LANGUAGE SQLSCRIPT | Programming language |
OPTIONS | Execution options |
USING | Database entities accessed |
BY DATABASE FUNCTION
For table functions that can be used in SELECT statements:
METHOD <method_name> BY DATABASE FUNCTION
FOR HDB
LANGUAGE SQLSCRIPT
OPTIONS READ-ONLY
USING <db_entities>.OPTIONS
| Option | Description |
|---|---|
READ-ONLY | No data modifications allowed |
DETERMINISTIC | Same input always produces same output |
SUPPRESS WARNINGS | Suppress specific warnings |
USING Clause
List all database tables and views accessed:
USING mara makt vbak vbap. " Multiple tables
USING ztable. " Custom table
USING zcl_other_amdp=>method_name. " Other AMDP method---
Parameter Restrictions
Allowed Parameter Types
| Type | Allowed |
|---|---|
| Elementary types | Yes |
| Structures | Yes |
| Internal tables | Yes |
| Nested tables | No |
| Deep structures | No |
Parameter Passing
| Mode | Requirement |
|---|---|
IMPORTING | Must use VALUE() |
EXPORTING | Must use VALUE() |
CHANGING | Not supported |
RETURNING | Not supported for procedures, allowed for functions |
Example with Types
CLASS zcl_amdp_types DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_amdp_marker_hdb.
" Define types in class
TYPES: BEGIN OF ty_customer,
kunnr TYPE kunnr,
name1 TYPE name1,
land1 TYPE land1,
END OF ty_customer.
TYPES tt_customer TYPE STANDARD TABLE OF ty_customer WITH DEFAULT KEY.
" Method using defined types
METHODS get_customers
IMPORTING VALUE(iv_country) TYPE land1
EXPORTING VALUE(et_customers) TYPE tt_customer.
ENDCLASS.---
Complete Examples
Example 1: Basic Data Retrieval
CLASS zcl_material_amdp DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_amdp_marker_hdb.
TYPES: BEGIN OF ty_material,
matnr TYPE matnr,
mtart TYPE mtart,
matkl TYPE matkl,
meins TYPE meins,
END OF ty_material.
TYPES tt_material TYPE STANDARD TABLE OF ty_material WITH DEFAULT KEY.
CLASS-METHODS get_materials_by_type
IMPORTING VALUE(iv_mtart) TYPE mtart
EXPORTING VALUE(et_materials) TYPE tt_material.
ENDCLASS.
CLASS zcl_material_amdp IMPLEMENTATION.
METHOD get_materials_by_type BY DATABASE PROCEDURE
FOR HDB
LANGUAGE SQLSCRIPT
OPTIONS READ-ONLY
USING mara.
et_materials = SELECT matnr, mtart, matkl, meins
FROM mara
WHERE mtart = :iv_mtart;
ENDMETHOD.
ENDCLASS.Example 2: Aggregation and Joins
CLASS zcl_sales_amdp DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_amdp_marker_hdb.
TYPES: BEGIN OF ty_sales_summary,
vkorg TYPE vkorg,
matnr TYPE matnr,
total_qty TYPE kwmeng,
total_value TYPE netwr,
END OF ty_sales_summary.
TYPES tt_sales_summary TYPE STANDARD TABLE OF ty_sales_summary WITH DEFAULT KEY.
CLASS-METHODS get_sales_summary
IMPORTING VALUE(iv_year) TYPE gjahr
EXPORTING VALUE(et_summary) TYPE tt_sales_summary.
ENDCLASS.
CLASS zcl_sales_amdp IMPLEMENTATION.
METHOD get_sales_summary BY DATABASE PROCEDURE
FOR HDB
LANGUAGE SQLSCRIPT
OPTIONS READ-ONLY
USING vbak vbap.
et_summary = SELECT vbak.vkorg,
vbap.matnr,
SUM(vbap.kwmeng) AS total_qty,
SUM(vbap.netwr) AS total_value
FROM vbak
INNER JOIN vbap ON vbak.vbeln = vbap.vbeln
WHERE SUBSTRING(vbak.erdat, 1, 4) = :iv_year
GROUP BY vbak.vkorg, vbap.matnr;
ENDMETHOD.
ENDCLASS.Example 3: With Exception Handling
CLASS zcl_order_amdp DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_amdp_marker_hdb.
TYPES: BEGIN OF ty_order,
vbeln TYPE vbeln,
erdat TYPE erdat,
netwr TYPE netwr,
END OF ty_order.
TYPES tt_orders TYPE STANDARD TABLE OF ty_order WITH DEFAULT KEY.
CLASS-METHODS get_orders_safe
IMPORTING VALUE(iv_kunnr) TYPE kunnr
EXPORTING VALUE(et_orders) TYPE tt_orders
VALUE(ev_error) TYPE string.
ENDCLASS.
CLASS zcl_order_amdp IMPLEMENTATION.
METHOD get_orders_safe BY DATABASE PROCEDURE
FOR HDB
LANGUAGE SQLSCRIPT
OPTIONS READ-ONLY
USING vbak.
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
ev_error = 'Error: ' || ::SQL_ERROR_CODE || ' - ' || ::SQL_ERROR_MESSAGE;
et_orders = SELECT NULL AS vbeln, NULL AS erdat, NULL AS netwr
FROM dummy WHERE 1 = 0;
END;
et_orders = SELECT vbeln, erdat, netwr
FROM vbak
WHERE kunnr = :iv_kunnr;
ev_error = '';
ENDMETHOD.
ENDCLASS.Example 4: Table Function for CDS Views
CLASS zcl_stock_amdp DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_amdp_marker_hdb.
TYPES: BEGIN OF ty_stock,
matnr TYPE matnr,
werks TYPE werks_d,
labst TYPE labst,
END OF ty_stock.
TYPES tt_stock TYPE STANDARD TABLE OF ty_stock WITH DEFAULT KEY.
" Table function - can be used in CDS views
CLASS-METHODS get_stock
IMPORTING VALUE(iv_werks) TYPE werks_d
RETURNING VALUE(rt_stock) TYPE tt_stock.
ENDCLASS.
CLASS zcl_stock_amdp IMPLEMENTATION.
METHOD get_stock BY DATABASE FUNCTION
FOR HDB
LANGUAGE SQLSCRIPT
OPTIONS READ-ONLY
USING mard.
RETURN SELECT matnr, werks, labst
FROM mard
WHERE werks = :iv_werks
AND labst > 0;
ENDMETHOD.
ENDCLASS.---
Calling AMDP Methods
From ABAP
DATA: lt_materials TYPE zcl_material_amdp=>tt_material.
" Call static method
zcl_material_amdp=>get_materials_by_type(
EXPORTING
iv_mtart = 'FERT'
IMPORTING
et_materials = lt_materials
).
" Call instance method
DATA(lo_amdp) = NEW zcl_my_amdp( ).
lo_amdp->get_data(
EXPORTING
iv_param = 'value'
IMPORTING
et_result = lt_result
).From CDS Views (Table Functions Only)
@AbapCatalog.sqlViewName: 'ZSTOCK_CDS'
define view ZI_STOCK_VIEW as
select from zcl_stock_amdp=>get_stock( werks: $parameters.p_werks ) as Stock
{
matnr,
werks,
labst
}---
Restrictions and Limitations
Not Allowed in AMDP
| Feature | Restriction |
|---|---|
COMMIT | Not permitted |
ROLLBACK | Not permitted |
| DDL statements | Not permitted |
| Dynamic SQL with table variables | Limited support |
| Nested tables | Not as parameters |
| Deep structures | Not as parameters |
RETURNING parameters | Only in DB functions |
CHANGING parameters | Not supported |
Table Buffering
Writing to tables with active SAP buffering may cause issues. Use unbuffered tables or disable buffering.
---
Debugging AMDP
In Eclipse ADT
1. Set breakpoint in SQLScript code 2. Enable AMDP debugging in preferences 3. Run ABAP program calling the AMDP 4. Debugger switches to SQLScript debug mode
Debug Output
Use TRACE in SQLScript (HANA 2.0+):
DECLARE lv_debug NVARCHAR(1000);
lv_debug = 'Processing customer: ' || :iv_kunnr;
TRACE :lv_debug;Viewing TRACE Output:
- Eclipse ADT: Open Debug perspective → Console view shows TRACE output during debugging session
- SAP HANA Web IDE: Debug Console panel displays trace messages
- SQL Console: TRACE output appears in the Messages tab after execution
- Programmatic Access: Query
M_SQLSCRIPT_TRACEsystem view for trace history (requires appropriate privileges)
---
Best Practices
1. Use Appropriate Container
| Use Case | Container |
|---|---|
| Data modifications | BY DATABASE PROCEDURE |
| Read-only queries in CDS | BY DATABASE FUNCTION |
2. Minimize Data Transfer
" Good: Filter in database
et_result = SELECT * FROM table WHERE condition;
" Avoid: Return all, filter in ABAP3. Use Set-Based Operations
" Good: Set-based
et_result = SELECT a.*, b.name
FROM table_a a
JOIN table_b b ON a.id = b.id;
" Avoid: Cursor-based
FOR row AS cursor DO ...4. Proper Error Handling
Always include EXIT HANDLER for production code:
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
ev_error = ::SQL_ERROR_MESSAGE;
END;5. Document USING Clause
" List ALL tables accessed, even in nested calls
USING vbak vbap mara makt.---
Standard SAP Examples
Explore these standard SAP classes for AMDP patterns:
| Class | Description |
|---|---|
CL_CS_BOM_AMDP | Bill of Materials processing |
CL_SALV_AMDP_UTILS | ALV utilities |
CL_ABAP_AMDP_TEST | Test examples |
Check methods like MAT_REVISION_LEVEL_SELECT, MAT_BOM_CALC_QUANTITY for real-world implementations.
SQLScript Built-in Functions Reference
Table of Contents
- String Functions
- Character Manipulation
- String Operations
- Case Conversion
- Trimming and Padding
- Search and Replace
- Numeric Functions
- Basic Operations
- Rounding
- Logarithmic
- Trigonometric
- Bitwise Operations
- Random
- Date and Time Functions
- Current Date/Time
- Date Extraction
- EXTRACT Function
- Date Arithmetic
- Date Utilities
- Conversion Functions
- Type Conversion
- String Conversion
- Date/Time Conversion
- Binary Conversion
- Date/Time Format Codes
- Aggregate Functions
- Window Functions
- Syntax
- Frame Clause
- Ranking Functions
- Navigation Functions
- NULL Handling Functions
- Miscellaneous Functions
- SESSION_CONTEXT Keys
- SAP-Specific Functions
- TO_DATS
- TO_TIMS
- CONVERT_CURRENCY
- CONVERT_UNIT
- SQLScript Libraries
String Functions
Character Manipulation
| Function | Description | Example |
|---|---|---|
ASCII(string) | Returns ASCII value of first character | ASCII('A') → 65 |
CHAR(n) | Returns character for ASCII value | CHAR(65) → 'A' |
NCHAR(n) | Returns Unicode character | NCHAR(8364) → '€' |
UNICODE(string) | Returns Unicode value | UNICODE('€') → 8364 |
String Operations
| Function | Description | Example |
|---|---|---|
CONCAT(s1, s2) | Concatenates strings | CONCAT('A', 'B') → 'AB' |
LENGTH(string) | Returns string length | LENGTH('Hello') → 5 |
LEFT(string, n) | Returns leftmost n characters | LEFT('Hello', 2) → 'He' |
RIGHT(string, n) | Returns rightmost n characters | RIGHT('Hello', 2) → 'lo' |
SUBSTRING(s, start, len) | Extracts substring | SUBSTRING('Hello', 2, 3) → 'ell' |
SUBSTR_BEFORE(s, pattern) | Returns substring before pattern | SUBSTR_BEFORE('a-b', '-') → 'a' |
SUBSTR_AFTER(s, pattern) | Returns substring after pattern | SUBSTR_AFTER('a-b', '-') → 'b' |
Case Conversion
| Function | Description | Example |
|---|---|---|
LOWER(string) / LCASE(string) | Converts to lowercase | LOWER('ABC') → 'abc' |
UPPER(string) / UCASE(string) | Converts to uppercase | UPPER('abc') → 'ABC' |
INITCAP(string) | Capitalizes first letter of each word | INITCAP('hello world') → 'Hello World' |
Trimming and Padding
| Function | Description | Example |
|---|---|---|
TRIM(string) | Removes leading/trailing spaces | TRIM(' AB ') → 'AB' |
LTRIM(string [, chars]) | Removes leading characters | LTRIM('00123', '0') → '123' |
RTRIM(string [, chars]) | Removes trailing characters | RTRIM('123 ') → '123' |
LPAD(s, n [, pad]) | Left-pads to length n | LPAD('5', 3, '0') → '005' |
RPAD(s, n [, pad]) | Right-pads to length n | RPAD('5', 3, '0') → '500' |
Search and Replace
| Function | Description | Example |
|---|---|---|
LOCATE(search, string) | Returns position of substring | LOCATE('ll', 'Hello') → 3 |
REPLACE(s, old, new) | Replaces occurrences | REPLACE('abc', 'b', 'x') → 'axc' |
REVERSE(string) | Reverses string | REVERSE('abc') → 'cba' |
---
Numeric Functions
Basic Operations
| Function | Description | Example |
|---|---|---|
ABS(n) | Absolute value | ABS(-5) → 5 |
SIGN(n) | Sign (-1, 0, 1) | SIGN(-5) → -1 |
MOD(n, m) | Modulo | MOD(10, 3) → 1 |
POWER(n, m) | n raised to power m | POWER(2, 3) → 8 |
SQRT(n) | Square root | SQRT(16) → 4 |
EXP(n) | e raised to power n | EXP(1) → 2.718... |
Rounding
| Function | Description | Example |
|---|---|---|
CEIL(n) / CEILING(n) | Rounds up | CEIL(4.2) → 5 |
FLOOR(n) | Rounds down | FLOOR(4.8) → 4 |
ROUND(n [, d]) | Rounds to d decimal places | ROUND(4.567, 2) → 4.57 |
TRUNC(n [, d]) | Truncates to d decimal places | TRUNC(4.567, 2) → 4.56 |
Logarithmic
| Function | Description | Example |
|---|---|---|
LN(n) | Natural logarithm | LN(2.718) → 1.0 |
LOG(base, n) | Logarithm with base | LOG(10, 100) → 2 |
Trigonometric
| Function | Description |
|---|---|
SIN(n) | Sine (radians) |
COS(n) | Cosine (radians) |
TAN(n) | Tangent (radians) |
ASIN(n) | Arc sine |
ACOS(n) | Arc cosine |
ATAN(n) | Arc tangent |
ATAN2(y, x) | Arc tangent of y/x |
SINH(n) | Hyperbolic sine |
COSH(n) | Hyperbolic cosine |
TANH(n) | Hyperbolic tangent |
Bitwise Operations
| Function | Description | Example |
|---|---|---|
BITAND(a, b) | Bitwise AND | BITAND(12, 10) → 8 |
BITOR(a, b) | Bitwise OR | BITOR(12, 10) → 14 |
BITXOR(a, b) | Bitwise XOR | BITXOR(12, 10) → 6 |
BITNOT(n) | Bitwise NOT | BITNOT(0) → -1 |
BITCOUNT(n) | Count set bits | BITCOUNT(7) → 3 |
BITSET(n, pos) | Set bit at position | BITSET(0, 3) → 8 |
BITUNSET(n, pos) | Unset bit at position | BITUNSET(8, 3) → 0 |
Random
| Function | Description |
|---|---|
RAND() | Random number 0-1 |
RAND(seed) | Seeded random |
---
Date and Time Functions
Current Date/Time
| Function | Description | Format |
|---|---|---|
CURRENT_DATE | Current local date | YYYY-MM-DD |
CURRENT_TIME | Current local time | HH:MI:SS |
CURRENT_TIMESTAMP | Current local datetime | YYYY-MM-DD HH:MI:SS.FF |
CURRENT_UTCDATE | Current UTC date | YYYY-MM-DD |
CURRENT_UTCTIME | Current UTC time | HH:MI:SS |
CURRENT_UTCTIMESTAMP | Current UTC datetime | YYYY-MM-DD HH:MI:SS.FF |
NOW() | Same as CURRENT_TIMESTAMP |
Date Extraction
| Function | Description | Example |
|---|---|---|
YEAR(date) | Extract year | YEAR('2024-03-15') → 2024 |
MONTH(date) | Extract month (1-12) | MONTH('2024-03-15') → 3 |
DAYOFMONTH(date) | Extract day (1-31) | DAYOFMONTH('2024-03-15') → 15 |
DAYOFYEAR(date) | Day of year (1-366) | DAYOFYEAR('2024-03-15') → 75 |
DAYNAME(date) | Name of day | DAYNAME('2024-03-15') → 'FRIDAY' |
WEEKDAY(date) | Day of week (0-6, Mon=0) | WEEKDAY('2024-03-15') → 4 |
WEEK(date) | Week number (1-53) | WEEK('2024-03-15') → 11 |
QUARTER(date) | Quarter (1-4) | QUARTER('2024-03-15') → 1 |
HOUR(time) | Extract hour (0-23) | HOUR('14:30:00') → 14 |
MINUTE(time) | Extract minute (0-59) | MINUTE('14:30:00') → 30 |
SECOND(time) | Extract second (0-59) | SECOND('14:30:45') → 45 |
EXTRACT Function
EXTRACT(<part> FROM <datetime>)Parts: YEAR, MONTH, DAY, HOUR, MINUTE, SECOND
Date Arithmetic
| Function | Description | Example |
|---|---|---|
ADD_DAYS(date, n) | Add n days | ADD_DAYS('2024-01-01', 30) |
ADD_MONTHS(date, n) | Add n months | ADD_MONTHS('2024-01-01', 3) |
ADD_YEARS(date, n) | Add n years | ADD_YEARS('2024-01-01', 1) |
ADD_SECONDS(ts, n) | Add n seconds | ADD_SECONDS(NOW(), 3600) |
DAYS_BETWEEN(d1, d2) | Days between dates | DAYS_BETWEEN('2024-01-01', '2024-03-01') → 60 |
MONTHS_BETWEEN(d1, d2) | Months between dates | MONTHS_BETWEEN('2024-01-15', '2024-04-15') → 3 |
SECONDS_BETWEEN(t1, t2) | Seconds between times | |
NANO100_BETWEEN(t1, t2) | 100-nanosecond intervals | |
WORKDAYS_BETWEEN(d1, d2 [, locale]) | Working days between |
Date Utilities
| Function | Description |
|---|---|
LAST_DAY(date) | Last day of month |
NEXT_DAY(date) | Next occurrence of day |
ISOWEEK(date) | ISO week number |
UTCTOLOCAL(ts, tz) | Convert UTC to local |
LOCALTOUTC(ts, tz) | Convert local to UTC |
---
Conversion Functions
Type Conversion
| Function | Description |
|---|---|
CAST(expr AS type) | Convert to type |
TO_BIGINT(value) | Convert to BIGINT |
TO_INTEGER(value) / TO_INT(value) | Convert to INTEGER |
TO_SMALLINT(value) | Convert to SMALLINT |
TO_TINYINT(value) | Convert to TINYINT |
TO_DOUBLE(value) | Convert to DOUBLE |
TO_REAL(value) | Convert to REAL |
TO_DECIMAL(value [, p, s]) | Convert to DECIMAL |
TO_SMALLDECIMAL(value) | Convert to SMALLDECIMAL |
String Conversion
| Function | Description |
|---|---|
TO_VARCHAR(value [, format]) | Convert to VARCHAR |
TO_NVARCHAR(value [, format]) | Convert to NVARCHAR |
TO_ALPHANUM(value) | Convert to ALPHANUM |
TO_FIXEDCHAR(value, n) | Convert to fixed-length char |
Date/Time Conversion
| Function | Description |
|---|---|
TO_DATE(string [, format]) | Convert to DATE |
TO_TIME(string [, format]) | Convert to TIME |
TO_TIMESTAMP(string [, format]) | Convert to TIMESTAMP |
TO_SECONDDATE(string [, format]) | Convert to SECONDDATE |
TO_DATS(date) | Convert to SAP date (YYYYMMDD) |
TO_TIMS(time) | Convert to SAP time (HHMMSS) |
Binary Conversion
| Function | Description |
|---|---|
TO_BINARY(value) | Convert to BINARY |
TO_BLOB(value) | Convert to BLOB |
TO_CLOB(value) | Convert to CLOB |
TO_NCLOB(value) | Convert to NCLOB |
HEXTOBIN(hex) | Hex string to binary |
BINTOHEX(bin) | Binary to hex string |
STRTOBIN(string, encoding) | String to binary |
BINTOSTR(binary, encoding) | Binary to string |
Date/Time Format Codes
| Code | Description |
|---|---|
YYYY | 4-digit year |
YY | 2-digit year |
MM | Month (01-12) |
MON | Month abbreviation |
DD | Day (01-31) |
DY | Day abbreviation |
HH / HH12 | Hour (01-12) |
HH24 | Hour (00-23) |
MI | Minute (00-59) |
SS | Second (00-59) |
FF | Fractional seconds |
AM / PM | Meridian indicator |
---
Aggregate Functions
| Function | Description |
|---|---|
COUNT(*) | Count all rows |
COUNT(column) | Count non-NULL values |
COUNT(DISTINCT column) | Count distinct values |
SUM(column) | Sum of values |
AVG(column) | Average of values |
MIN(column) | Minimum value |
MAX(column) | Maximum value |
MEDIAN(column) | Median value |
STDDEV(column) | Standard deviation |
VAR(column) | Variance |
STRING_AGG(column [, delimiter]) | Concatenate strings |
---
Window Functions
Syntax
<function>(<args>) OVER (
[PARTITION BY <columns>]
[ORDER BY <columns> [ASC|DESC]]
[<frame_clause>]
)Frame Clause
ROWS BETWEEN <start> AND <end>
RANGE BETWEEN <start> AND <end>Frame bounds:
UNBOUNDED PRECEDINGn PRECEDINGCURRENT ROWn FOLLOWINGUNBOUNDED FOLLOWING
Ranking Functions
| Function | Description |
|---|---|
ROW_NUMBER() | Unique sequential number |
RANK() | Rank with gaps for ties |
DENSE_RANK() | Rank without gaps |
NTILE(n) | Distribute into n buckets |
PERCENT_RANK() | Relative rank (0-1) |
CUME_DIST() | Cumulative distribution |
Navigation Functions
| Function | Description |
|---|---|
LEAD(col [, n [, default]]) | Value from n rows ahead |
LAG(col [, n [, default]]) | Value from n rows behind |
FIRST_VALUE(col) | First value in window |
LAST_VALUE(col) | Last value in window |
NTH_VALUE(col, n) | Nth value in window |
---
NULL Handling Functions
| Function | Description |
|---|---|
COALESCE(v1, v2, ...) | First non-NULL value |
IFNULL(value, default) | Return default if NULL |
NULLIF(v1, v2) | NULL if v1 = v2 |
NVL(value, default) | Same as IFNULL |
---
Miscellaneous Functions
| Function | Description |
|---|---|
CURRENT_SCHEMA | Current schema name |
CURRENT_USER | Current user name |
SESSION_USER | Session user name |
SESSION_CONTEXT(key) | Session context value |
RECORD_COUNT(table_var) | Row count of table variable |
CARDINALITY(array) | Array length |
SYSUUID | Generate UUID |
HASH_MD5(value) | MD5 hash |
HASH_SHA256(value) | SHA-256 hash |
SESSION_CONTEXT Keys
| Key | Description |
|---|---|
'CLIENT' | SAP client (mandant) |
'APPLICATIONUSER' | Application user name |
'LOCALE' | Session locale |
'LOCALE_SAP' | SAP locale setting |
Note: Available keys may vary by HANA version and configuration. Custom session variables can be set with SET SESSION.Example:
SELECT SESSION_CONTEXT('CLIENT') AS client,
SESSION_CONTEXT('APPLICATIONUSER') AS app_user
FROM DUMMY;---
SAP-Specific Functions
TO_DATS
Convert DATE to SAP date format (YYYYMMDD string):
SELECT TO_DATS(CURRENT_DATE) FROM DUMMY;
-- Returns: '20241123'
-- Extract year-month portion
SELECT SUBSTRING(TO_DATS(CURRENT_DATE), 1, 6) FROM DUMMY;
-- Returns: '202411'TO_TIMS
Convert TIME to SAP time format (HHMMSS string):
SELECT TO_TIMS(CURRENT_TIME) FROM DUMMY;
-- Returns: '143022'CONVERT_CURRENCY
Currency conversion using SAP exchange rate tables:
CONVERT_CURRENCY(
AMOUNT => <decimal_value>,
SOURCE_UNIT => <source_currency>,
TARGET_UNIT => <target_currency>,
SCHEMA => <schema_name>,
REFERENCE_DATE => <date>,
CLIENT => <client_number>,
CONVERSION_TYPE => <type>
)Example:
SELECT CONVERT_CURRENCY(
AMOUNT => 1000.00,
SOURCE_UNIT => 'USD',
SCHEMA => 'SAPABAP1',
TARGET_UNIT => 'EUR',
REFERENCE_DATE => CURRENT_DATE,
CLIENT => '100',
CONVERSION_TYPE => 'EURX'
) AS converted_amount
FROM DUMMY;CONVERT_UNIT
Unit of measure conversion using SAP UOM tables:
CONVERT_UNIT(
QUANTITY => <decimal_value>,
SOURCE_UNIT => <source_uom>,
TARGET_UNIT => <target_uom>,
SCHEMA => <schema_name>,
CLIENT => <client_number>
)Example:
-- Convert 1000 grams to kilograms
SELECT CONVERT_UNIT(
QUANTITY => 1000.00,
SOURCE_UNIT => 'G',
SCHEMA => 'SAPABAP1',
TARGET_UNIT => 'KG',
CLIENT => '100'
) AS converted_quantity
FROM DUMMY;
-- Returns: 1.00
-- Convert length units
SELECT CONVERT_UNIT(
QUANTITY => 100.00,
SOURCE_UNIT => 'CM',
SCHEMA => 'SAPABAP1',
TARGET_UNIT => 'M',
CLIENT => '100'
) AS meters
FROM DUMMY;
-- Returns: 1.00Note: Requires UOM conversion factors configured in SAP T006* tables.
---
SQLScript Libraries
Available since HANA 2.0 SPS03:
| Library | Purpose |
|---|---|
SQLSCRIPT_STRING | String manipulation (e.g., TABLE_SUMMARY) |
SQLSCRIPT_PRINT | Debug output |
SQLSCRIPT_SYNC | Synchronization |
SQLSCRIPT_CACHE | Caching utilities |
Usage:
USING SQLSCRIPT_STRING AS str_lib;
lv_summary = str_lib:TABLE_SUMMARY(:lt_data);SQLScript Data Types Reference
Numeric Types
| Type | Description | Range |
|---|---|---|
TINYINT | 8-bit integer | 0 to 255 |
SMALLINT | 16-bit integer | -32,768 to 32,767 |
INTEGER / INT | 32-bit integer | -2,147,483,648 to 2,147,483,647 |
BIGINT | 64-bit integer | -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 |
DECIMAL(p,s) | Fixed-point decimal | p = precision (1-34), s = scale (0-p) |
DOUBLE | 64-bit floating point | 15-digit precision |
REAL | 32-bit floating point | 7-digit precision |
Usage Examples
-- Decimal with precision and scale
DECLARE lv_price DECIMAL(15,2) := 12345.67;
-- Integer types
DECLARE lv_count INTEGER := 0;
DECLARE lv_id BIGINT;
-- Floating point
DECLARE lv_percentage REAL := 0.95;
DECLARE lv_measurement DOUBLE := 123.456789;Character Types
| Type | Description | Max Length |
|---|---|---|
VARCHAR(n) | Variable-length ASCII string | 5,000 bytes |
NVARCHAR(n) | Variable-length Unicode string | 5,000 characters |
ALPHANUM(n) | Alphanumeric string | 127 characters |
CLOB | Character large object | 2 GB |
NCLOB | Unicode character large object | 2 GB |
NLOB | National character large object | 2 GB |
Usage Examples
-- String variables
DECLARE lv_name NVARCHAR(100) := 'John Doe';
DECLARE lv_description VARCHAR(500);
DECLARE lv_code ALPHANUM(10) := 'ABC123';
-- Large objects
DECLARE lt_document CLOB;String Operations
-- Concatenation
lv_full_name = lv_first_name || ' ' || lv_last_name;
-- Common string functions
lv_upper = UPPER(:lv_string);
lv_length = LENGTH(:lv_string);
lv_trimmed = TRIM(:lv_string);
lv_substring = SUBSTRING(:lv_string, 1, 10);Date/Time Types
| Type | Format | Range |
|---|---|---|
DATE | 'YYYY-MM-DD' | 0001-01-01 to 9999-12-31 |
TIME | 'HH:MI:SS' | 00:00:00 to 23:59:59 |
TIMESTAMP | 'YYYY-MM-DD HH:MI:SS.FF' | Up to 7 fractional digits |
SECONDDATE | 'YYYY-MM-DD HH:MI:SS' | Same as TIMESTAMP without fractions |
Usage Examples
-- Date/time declarations
DECLARE lv_order_date DATE := CURRENT_DATE;
DECLARE lv_created_time TIMESTAMP := CURRENT_TIMESTAMP;
DECLARE lv_start_time TIME;
-- Date literals
lv_date = DATE '2025-11-26';
lv_time = TIME '14:30:00';
lv_timestamp = TIMESTAMP '2025-11-26 14:30:00.123';
-- Date operations
lv_future_date = ADD_DAYS(:lv_date, 30);
lv_days_between = DAYS_BETWEEN(:lv_date1, :lv_date2);Binary Types
| Type | Description | Max Length |
|---|---|---|
VARBINARY(n) | Variable-length binary | 5,000 bytes |
BLOB | Binary large object | 2 GB |
Usage Examples
-- Binary data
DECLARE lv_image VARBINARY(1000);
DECLARE lb_file BLOB;
-- Hexadecimal literals
lv_binary = X'48656C6C6F'; -- 'Hello' in hexType Conversion
CAST Function
-- Explicit type conversion
lv_string = CAST(:lv_number AS VARCHAR(20));
lv_date = CAST(:lv_string AS DATE);
lv_decimal = CAST(:lv_number AS DECIMAL(10,2));TO_ Functions
-- String to number
lv_integer = TO_INTEGER(:lv_string);
lv_decimal = TO_DECIMAL(:lv_string, '999999.99');
-- String to date/time
lv_date = TO_DATE(:lv_string, 'YYYY-MM-DD');
lv_timestamp = TO_TIMESTAMP(:lv_string, 'YYYY-MM-DD HH24:MI:SS');
-- Number/date to string
lv_string = TO_VARCHAR(:lv_number);
lv_string_date = TO_VARCHAR(:lv_date, 'YYYY-MM-DD');
-- ABAP date/time conversion
lv_abap_date = TO_DATS(:lv_date);
lv_abap_time = TO_TIMS(:lv_time);NULL Handling
Functions for NULL Values
-- COALESCE: Return first non-NULL value
lv_result = COALESCE(:lv_value1, :lv_value2, 'DEFAULT');
-- IFNULL: Return default if NULL
lv_result = IFNULL(:lv_value, 'DEFAULT');
-- NULLIF: Return NULL if values equal
lv_null_result = NULLIF(:lv_value1, :lv_value2);NULL Comparisons
-- Check for NULL
IF :lv_value IS NULL THEN
-- Handle NULL
END IF;
-- Cannot use = NULL, must use IS NULL
-- Wrong: WHERE column = NULL
-- Right: WHERE column IS NULLSpecial Considerations
Character Set Considerations
VARCHARstores ASCII (1 byte per character)NVARCHARstores Unicode (UTF-8, variable bytes per character)- Use
NVARCHARfor multilingual data ALPHANUMremoves leading/trailing spaces automatically
Performance Considerations
- Use smallest appropriate data type for better performance
DECIMALis preferred overFLOATfor precise calculationsVARCHAR/NVARCHARwith defined length performs better than CLOB- Date types store more efficiently than string representations
Storage Engine Differences
- Column Store: Optimized for analytical queries, compression
- Row Store: Optimized for transactional queries
- Choose appropriate storage type based on usage pattern
Best Practices
1. Use appropriate data types - Choose the smallest type that fits your needs 2. Prefer specific over generic - Use DECIMAL instead of DOUBLE for money 3. Consider Unicode - Use NVARCHAR for any text that might contain non-ASCII 4. Handle NULLs explicitly - Don't assume values are non-NULL 5. Use functions for conversion - Prefer TO_ functions over CAST for dates/times 6. Document assumptions - Comment on data type choices and constraints
SQLScript Exception Handling Reference
Table of Contents
- Overview
- EXIT HANDLER
- Purpose
- Syntax
- Condition Values
- Examples
- CONTINUE HANDLER
- Purpose
- Syntax
- When to Use
- Examples
- CONDITION Declaration
- Purpose
- Syntax
- Common Error Codes
- Examples
- SIGNAL Statement
- Purpose
- Syntax
- User-defined Error Codes
- Examples
- RESIGNAL Statement
- Purpose
- Syntax
- When to Use
- Examples
- Error Information Access
- SQL_ERROR_CODE
- SQL_ERROR_MESSAGE
- Examples
- Handler Precedence
- Handler Selection Rules
- Examples
- Nested Handlers
- Handler Scoping
- Examples
- Best Practices
- Error Handling Strategy
- Performance Considerations
- Common Patterns
Overview
SAP HANA SQLScript provides four primary mechanisms for exception handling: 1. EXIT HANDLER - Handle exceptions and suspend execution 2. CONTINUE HANDLER - Handle exceptions and continue execution 3. CONDITION - Define named conditions for error codes 4. SIGNAL/RESIGNAL - Throw and re-throw exceptions
---
EXIT HANDLER
Purpose
EXIT HANDLER catches exceptions, suspends procedure execution, and performs specified recovery actions.
Syntax
DECLARE EXIT HANDLER FOR <condition_value>
<statement>;
-- Or with block
DECLARE EXIT HANDLER FOR <condition_value>
BEGIN
<statements>
END;Condition Values
| Condition | Description |
|---|---|
SQLEXCEPTION | Catches any SQL exception |
SQL_ERROR_CODE <number> | Catches specific error code |
<condition_name> | Catches user-defined condition |
Placement Rule
Important: EXIT HANDLER must be declared after all other DECLARE statements but before any procedural code begins.
BEGIN
-- 1. Variable declarations
DECLARE lv_count INTEGER;
-- 2. Condition declarations
DECLARE my_error CONDITION FOR SQL_ERROR_CODE 301;
-- 3. EXIT HANDLER declarations (must be last DECLARE)
DECLARE EXIT HANDLER FOR SQLEXCEPTION
SELECT ::SQL_ERROR_CODE, ::SQL_ERROR_MESSAGE FROM DUMMY;
-- 4. Procedural code starts here
INSERT INTO "TABLE" VALUES (1, 'test');
END;Error Information Access
| Variable | Type | Description |
|---|---|---|
::SQL_ERROR_CODE | INTEGER | Numeric error code of caught exception |
::SQL_ERROR_MESSAGE | NVARCHAR | Error message text |
Basic Example
CREATE PROCEDURE safe_insert (
IN iv_id INTEGER,
IN iv_name NVARCHAR(100)
)
LANGUAGE SQLSCRIPT AS
BEGIN
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
-- Log error to table
INSERT INTO "ERROR_LOG" (
error_code,
error_message,
created_at
) VALUES (
::SQL_ERROR_CODE,
::SQL_ERROR_MESSAGE,
CURRENT_TIMESTAMP
);
-- Return error info
SELECT ::SQL_ERROR_CODE AS err_code,
::SQL_ERROR_MESSAGE AS err_msg
FROM DUMMY;
END;
-- Main logic that may throw exception
INSERT INTO "CUSTOMERS" (id, name) VALUES (:iv_id, :iv_name);
END;Handling Specific Error Codes
CREATE PROCEDURE handle_duplicate (IN iv_id INTEGER)
LANGUAGE SQLSCRIPT AS
BEGIN
-- Handle only unique constraint violations
DECLARE EXIT HANDLER FOR SQL_ERROR_CODE 301
BEGIN
SELECT 'Record already exists with ID: ' || :iv_id AS message
FROM DUMMY;
END;
INSERT INTO "MYTABLE" (id) VALUES (:iv_id);
END;Multiple Handlers
CREATE PROCEDURE multi_handler_example ()
LANGUAGE SQLSCRIPT AS
BEGIN
DECLARE duplicate_key CONDITION FOR SQL_ERROR_CODE 301;
DECLARE no_data CONDITION FOR SQL_ERROR_CODE 1299;
-- Specific handler for duplicates
DECLARE EXIT HANDLER FOR duplicate_key
SELECT 'Duplicate key error' AS error FROM DUMMY;
-- Specific handler for no data
DECLARE EXIT HANDLER FOR no_data
SELECT 'No data found' AS error FROM DUMMY;
-- Generic handler for all other errors
DECLARE EXIT HANDLER FOR SQLEXCEPTION
SELECT 'Unexpected error: ' || ::SQL_ERROR_MESSAGE AS error FROM DUMMY;
-- Procedural code
INSERT INTO "TABLE1" VALUES (1);
SELECT * FROM "TABLE2" WHERE id = 999;
END;---
CONDITION
Purpose
CONDITIONS allow you to assign user-friendly names to SQL error codes for cleaner, more readable code.
Note: CONDITION declaration is optional. You can use SQL_ERROR_CODE <number> directly in EXIT HANDLER declarations. However, named conditions improve code readability and maintainability.Syntax
DECLARE <condition_name> CONDITION FOR SQL_ERROR_CODE <number>;Example
CREATE PROCEDURE condition_example ()
LANGUAGE SQLSCRIPT AS
BEGIN
-- Define named conditions
DECLARE unique_violation CONDITION FOR SQL_ERROR_CODE 301;
DECLARE invalid_table CONDITION FOR SQL_ERROR_CODE 259;
DECLARE permission_denied CONDITION FOR SQL_ERROR_CODE 258;
-- Use in EXIT HANDLER
DECLARE EXIT HANDLER FOR unique_violation
SELECT 'Cannot insert duplicate record' AS message FROM DUMMY;
DECLARE EXIT HANDLER FOR invalid_table
SELECT 'Table does not exist' AS message FROM DUMMY;
DECLARE EXIT HANDLER FOR permission_denied
SELECT 'Access denied to object' AS message FROM DUMMY;
-- Procedural code
INSERT INTO "PROTECTED_TABLE" VALUES (1, 'test');
END;---
SIGNAL
Purpose
SIGNAL explicitly throws an exception with a user-defined error code (range: 10000-19999).
Syntax
-- Using condition name
SIGNAL <condition_name>;
SIGNAL <condition_name> SET MESSAGE_TEXT = '<message>';
-- Using error code directly
SIGNAL SQL_ERROR_CODE <number> SET MESSAGE_TEXT = '<message>';User-Defined Error Code Range
| Range | Usage |
|---|---|
| 10000 - 19999 | User-defined exceptions |
Example
CREATE PROCEDURE validate_input (IN iv_amount DECIMAL(15,2))
LANGUAGE SQLSCRIPT AS
BEGIN
-- Define custom error condition
DECLARE invalid_amount CONDITION FOR SQL_ERROR_CODE 10001;
DECLARE EXIT HANDLER FOR invalid_amount
SELECT 'Validation failed: ' || ::SQL_ERROR_MESSAGE AS error FROM DUMMY;
-- Validate input
IF :iv_amount < 0 THEN
SIGNAL invalid_amount SET MESSAGE_TEXT = 'Amount cannot be negative';
END IF;
IF :iv_amount > 1000000 THEN
SIGNAL invalid_amount SET MESSAGE_TEXT = 'Amount exceeds maximum limit';
END IF;
-- Continue with valid input
INSERT INTO "TRANSACTIONS" (amount) VALUES (:iv_amount);
END;SIGNAL Without Condition
-- Throw exception with error code directly
IF :lv_count = 0 THEN
SIGNAL SQL_ERROR_CODE 10002 SET MESSAGE_TEXT = 'No records found for processing';
END IF;---
RESIGNAL
Purpose
RESIGNAL re-throws an exception from within an EXIT HANDLER, allowing the exception to propagate to the caller.
Syntax
-- Re-throw current exception
RESIGNAL;
-- Re-throw with different condition
RESIGNAL <condition_name>;
-- Re-throw with modified message
RESIGNAL SET MESSAGE_TEXT = '<new_message>';Restriction
Important: RESIGNAL can only be used within an EXIT HANDLER block.
Example: Logging and Re-throwing
CREATE PROCEDURE process_with_logging ()
LANGUAGE SQLSCRIPT AS
BEGIN
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
-- Log the error first
INSERT INTO "ERROR_LOG" (
procedure_name,
error_code,
error_message,
logged_at
) VALUES (
'process_with_logging',
::SQL_ERROR_CODE,
::SQL_ERROR_MESSAGE,
CURRENT_TIMESTAMP
);
-- Re-throw to caller
RESIGNAL;
END;
-- Risky operation
DELETE FROM "IMPORTANT_TABLE" WHERE status = 'OLD';
END;Example: Wrapping Exceptions
CREATE PROCEDURE wrap_exception ()
LANGUAGE SQLSCRIPT AS
BEGIN
DECLARE business_error CONDITION FOR SQL_ERROR_CODE 10100;
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
-- Wrap technical error as business error
RESIGNAL business_error
SET MESSAGE_TEXT = 'Business operation failed: ' || ::SQL_ERROR_MESSAGE;
END;
-- Technical operation
UPDATE "ACCOUNTS" SET balance = balance - 100 WHERE id = 1;
END;---
Common SQL Error Codes
| Code | Description |
|---|---|
| 258 | Insufficient privilege |
| 259 | Invalid table name |
| 260 | Invalid column name |
| 261 | Invalid index name |
| 301 | Unique constraint violation |
| 339 | Foreign key constraint violation |
| 362 | NOT NULL constraint violation |
| 1299 | No data found |
| 1304 | Resource busy |
| 10000-19999 | User-defined errors |
---
Complete Error Handling Pattern
CREATE PROCEDURE complete_error_handling (
IN iv_customer_id INTEGER,
IN iv_amount DECIMAL(15,2),
OUT ov_status NVARCHAR(20),
OUT ov_message NVARCHAR(500)
)
LANGUAGE SQLSCRIPT AS
BEGIN
-- Define conditions
DECLARE duplicate_key CONDITION FOR SQL_ERROR_CODE 301;
DECLARE fk_violation CONDITION FOR SQL_ERROR_CODE 339;
DECLARE invalid_input CONDITION FOR SQL_ERROR_CODE 10001;
-- Handle duplicate key
DECLARE EXIT HANDLER FOR duplicate_key
BEGIN
ov_status = 'ERROR';
ov_message = 'Transaction already exists for this customer';
END;
-- Handle foreign key violation
DECLARE EXIT HANDLER FOR fk_violation
BEGIN
ov_status = 'ERROR';
ov_message = 'Customer ID does not exist';
END;
-- Handle validation errors
DECLARE EXIT HANDLER FOR invalid_input
BEGIN
ov_status = 'ERROR';
ov_message = ::SQL_ERROR_MESSAGE;
END;
-- Handle all other errors
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
-- Log unexpected errors
INSERT INTO "ERROR_LOG" VALUES (
::SQL_ERROR_CODE,
::SQL_ERROR_MESSAGE,
CURRENT_TIMESTAMP
);
ov_status = 'ERROR';
ov_message = 'Unexpected error occurred. Please contact support.';
END;
-- Input validation
IF :iv_amount <= 0 THEN
SIGNAL invalid_input SET MESSAGE_TEXT = 'Amount must be positive';
END IF;
IF :iv_customer_id IS NULL THEN
SIGNAL invalid_input SET MESSAGE_TEXT = 'Customer ID is required';
END IF;
-- Main business logic
INSERT INTO "TRANSACTIONS" (customer_id, amount, created_at)
VALUES (:iv_customer_id, :iv_amount, CURRENT_TIMESTAMP);
-- Success
ov_status = 'SUCCESS';
ov_message = 'Transaction recorded successfully';
END;---
Best Practices
1. Always Handle SQLEXCEPTION
-- Catch-all handler prevents unhandled exceptions
DECLARE EXIT HANDLER FOR SQLEXCEPTION
-- Log and/or notify2. Use Named Conditions
-- Good: Clear intent
DECLARE duplicate_key CONDITION FOR SQL_ERROR_CODE 301;
DECLARE EXIT HANDLER FOR duplicate_key ...
-- Avoid: Magic numbers
DECLARE EXIT HANDLER FOR SQL_ERROR_CODE 301 ...3. Log Before RESIGNAL
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
-- Log first
INSERT INTO error_log VALUES (...);
-- Then re-throw
RESIGNAL;
END;4. Use User-Defined Codes for Business Logic
-- Reserve 10000-19999 for application-specific errors
DECLARE validation_error CONDITION FOR SQL_ERROR_CODE 10001;
DECLARE business_rule_error CONDITION FOR SQL_ERROR_CODE 10002;5. Provide Meaningful Messages
SIGNAL validation_error
SET MESSAGE_TEXT = 'Order quantity (' || :lv_qty || ') exceeds available stock (' || :lv_stock || ')';SQLScript Glossary
Comprehensive glossary of SQLScript and SAP HANA database terminology.
---
A
AMDP (ABAP Managed Database Procedure)
A technology that allows SQLScript procedures to be written and managed within ABAP classes. Requires the IF_AMDP_MARKER_HDB interface and NetWeaver 7.40 SP05+.
Anonymous Block
A SQLScript code block executed immediately without being stored in the database. Uses DO BEGIN ... END; syntax. Useful for testing and ad-hoc operations.
Array
A one-dimensional ordered collection of elements of the same type. Declared with ARRAY keyword. Uses 1-based indexing. Cannot be returned from procedures.
---
B
Binding
The process of assigning host variables to SQL statements at runtime. In SQLScript, variables are bound using the colon prefix (:variable_name).
Block Statement
A compound statement enclosed in BEGIN ... END that groups multiple statements. Can contain declarations, executable statements, and exception handlers.
---
C
Calculation Engine (CE)
SAP HANA's internal execution engine optimized for analytical processing. CE functions (e.g., CE_CALC, CE_JOIN) provide direct access to this engine but are deprecated in favor of SQL-based approaches.
Code-to-Data Paradigm
The fundamental principle of pushing computation to where data resides (in the database) rather than moving data to the application layer. This minimizes data transfer and leverages HANA's in-memory processing.
Column Store
SAP HANA's primary storage format where data is stored by columns rather than rows. Optimized for analytical queries, aggregations, and compression. Most tables should use column store.
Condition
A named association with a specific SQL error code. Declared with DECLARE condition_name CONDITION FOR SQL_ERROR_CODE number;. Used with EXIT HANDLER for targeted error handling.
Cursor
A database object that allows row-by-row processing of query results. Pattern: Declare → Open → Fetch → Close. Use sparingly as cursors bypass the optimizer.
---
D
DDL (Data Definition Language)
SQL statements that define database structure: CREATE, ALTER, DROP, TRUNCATE. DDL operations are auto-committed in HANA.
Declarative Logic
SQL-based code that describes what result is wanted without specifying how to compute it. Converted to data flow graphs and executed in parallel. Preferred over imperative logic.
Default Schema
The schema used when object names are not fully qualified. Set with DEFAULT SCHEMA clause in procedures or session-level SET SCHEMA.
DML (Data Manipulation Language)
SQL statements that manipulate data: SELECT, INSERT, UPDATE, DELETE, MERGE. DML operations respect transaction boundaries.
DUMMY
A single-row, single-column system table used for evaluating expressions without needing actual table data. SELECT :variable FROM DUMMY;
Dynamic SQL
SQL statements constructed and executed at runtime using EXECUTE IMMEDIATE. Avoid when possible as it prevents optimization and poses security risks.
---
E
Engine Mixing
The anti-pattern of combining Row Store and Column Store operations in a single query, or mixing CE functions with SQL. Causes performance degradation due to data format conversion.
EXIT HANDLER
An exception handler that executes when a specified condition occurs, then exits the current block. Declared with DECLARE EXIT HANDLER FOR condition_value statement;.
---
F
Function (UDF)
User-Defined Function. Two types:
- Scalar UDF: Returns a single value
- Table UDF: Returns a table (must be read-only)
---
H
HDI (HANA Deployment Infrastructure)
A container-based deployment model for SAP HANA artifacts. Provides isolated schema namespaces and supports version-controlled deployments via .hdbtable, .hdbprocedure files.
Host Variable
A variable in SQLScript that holds values passed to or from SQL statements. Referenced with colon prefix (:variable).
---
I
Imperative Logic
Procedural code using control structures (IF, WHILE, FOR, LOOP) that specifies how to compute results step by step. Executes sequentially and prevents parallel processing. Use only when necessary.
Implicit Declaration
Creating a table variable without explicit DECLARE by assigning a SELECT result: lt_result = SELECT * FROM table;
INVOKER
SQL Security mode where the procedure executes with the privileges of the calling user. Compare with DEFINER mode.
---
L
L-Value
An expression that can appear on the left side of an assignment (can be assigned to). In SQLScript: scalar variables, table variables.
Lateral Join
A join where the right side can reference columns from the left side. Uses CROSS APPLY or OUTER APPLY syntax. Enables correlated subquery-like patterns in FROM clause.
---
N
NULL
The absence of a value. Handled with COALESCE, IFNULL, NULLIF functions. Uninitialized variables default to NULL.
---
O
OUTER APPLY
A lateral join that returns all rows from the left side even when the right side returns no rows (similar to LEFT JOIN). Compare with CROSS APPLY.
---
P
Parallel Execution
The ability of SAP HANA to execute multiple operations simultaneously. Declarative SQL enables parallel execution; imperative logic prevents it.
Plan Visualizer
SAP HANA tool for analyzing query execution plans. Shows operator breakdown, data flow, and identifies performance bottlenecks.
Pragma
A compiler directive that influences code generation without changing logic. Example: /*#RESULT_CACHE*/ for result caching.
Procedure
A stored database object containing SQLScript logic with optional input/output parameters. Created with CREATE PROCEDURE and invoked with CALL.
---
R
READS SQL DATA
Procedure option indicating read-only operation (no DML changes). Required for procedures called from SQL expressions and table functions.
RESIGNAL
Statement that re-throws the current exception from within an exception handler, optionally with a modified message.
Row Store
SAP HANA storage format where data is stored by rows. Used for OLTP-style operations requiring frequent single-row access. Less common than Column Store.
R-Value
An expression that can appear on the right side of an assignment (provides a value). In SQLScript: literals, expressions, function results.
---
S
Scalar Type
A data type that holds a single value (INTEGER, VARCHAR, DATE, etc.) as opposed to table types or arrays.
SIGNAL
Statement that throws a user-defined exception with a custom error code (10000-19999) and message. SIGNAL condition SET MESSAGE_TEXT = 'message';
SQL Security
Determines execution privileges for procedures:
- DEFINER: Runs with owner's privileges
- INVOKER: Runs with caller's privileges
SQLEXCEPTION
A generic condition that matches any SQL exception. Used with EXIT HANDLER: DECLARE EXIT HANDLER FOR SQLEXCEPTION ...
SQLScript
SAP HANA's procedural extension to SQL. Combines declarative SQL with imperative control structures for complex database logic.
---
T
Table Type
A user-defined type defining a table structure. Created with CREATE TYPE name AS TABLE (...). Used for procedure parameters and structured data.
Table Variable
A variable that holds a table structure and data. Can be implicitly or explicitly declared. Referenced with colon prefix in SQL context.
Transaction
A logical unit of work comprising one or more database operations. Bounded by COMMIT or ROLLBACK. Note: DDL auto-commits.
---
U
UDF (User-Defined Function)
See Function.
UNION ALL
Set operation combining results without removing duplicates. Faster than UNION which eliminates duplicates.
---
V
Variable Scope
The visibility of a variable within code:
- Block scope: Variables declared in a block are visible only within that block
- Procedure scope: Parameters visible throughout the procedure
---
W
Window Function
A function that performs calculations across a set of rows related to the current row, without collapsing results. Examples: ROW_NUMBER, RANK, LAG, LEAD, SUM OVER.
---
Common Abbreviations
| Abbreviation | Full Form |
|---|---|
| AMDP | ABAP Managed Database Procedure |
| CE | Calculation Engine |
| CDS | Core Data Services |
| DDL | Data Definition Language |
| DML | Data Manipulation Language |
| HDI | HANA Deployment Infrastructure |
| HANA | High-performance ANalytic Appliance |
| UDF | User-Defined Function |
| XSA | Extended Application Services, Advanced |
---
Related Terms
For function-specific terminology, see built-in-functions.md. For AMDP-specific terminology, see amdp-integration.md. For data type details, see data-types.md.
SQLScript Performance Optimization Guide
Table of Contents
- Core Principle: Code-to-Data Paradigm
- Top Performance Optimization Tips
- 1. Reduce Data Volume Early
- 2. Prefer Declarative Over Imperative
- 3. Avoid Engine Mixing
- 4. Use UNION ALL Instead of UNION
- 5. Minimize Dynamic SQL
- 6. Position Imperative Logic Last
- Execution Plan Analysis
- Understanding the Plan
- Common Plan Issues
- Optimization Techniques
- Advanced Optimization Techniques
- Query Hints
- Partitioning Strategies
- Materialized Views
- Join Optimization
- Memory Management
- Memory Allocation
- Memory Leaks Prevention
- Large Dataset Handling
- Parallel Execution
- Parallelism in Declarative Logic
- Limitations of Imperative Code
- Performance Monitoring
- Expensive Statements Trace
- Performance Tools
- Key Metrics
- Common Performance Anti-Patterns
- Row-by-Row Processing
- Cursor Overuse
- Unnecessary Joins
- Suboptimal Data Types
- Benchmarking Best Practices
Core Principle: Code-to-Data Paradigm
SAP HANA's fundamental performance philosophy is push computation to the database, not pull data to the application. This leverages:
- In-memory processing
- Columnar storage compression
- Parallel query execution
- Database optimizer capabilities
---
Top Performance Optimization Tips
1. Reduce Data Volume Early
Problem: Processing large datasets consumes memory and slows execution.
Solution: Filter rows and select only required columns as early as possible.
-- GOOD: Filter and project early
lt_filtered = SELECT customer_id, order_total
FROM "ORDERS"
WHERE status = 'COMPLETED'
AND order_date >= ADD_DAYS(CURRENT_DATE, -30);
lt_result = SELECT f.customer_id, f.order_total, c.name
FROM :lt_filtered AS f
JOIN "CUSTOMERS" AS c ON f.customer_id = c.id;
-- BAD: Join first, filter later
lt_result = SELECT o.customer_id, o.order_total, c.name
FROM "ORDERS" AS o
JOIN "CUSTOMERS" AS c ON o.customer_id = c.id
WHERE o.status = 'COMPLETED'
AND o.order_date >= ADD_DAYS(CURRENT_DATE, -30);Best Practices:
- Select only columns you need (avoid
SELECT *) - Apply WHERE filters as early as possible
- Pre-aggregate before joining
- Consider NULL-heavy columns separately
---
2. Prefer Declarative Over Imperative Logic
Problem: Imperative constructs (loops, cursors, IF statements) prevent parallel execution.
Solution: Use set-based SQL operations whenever possible.
-- GOOD: Declarative set-based operation
lt_updated = SELECT id,
amount * 1.1 AS new_amount,
CASE WHEN amount > 1000 THEN 'HIGH'
WHEN amount > 100 THEN 'MEDIUM'
ELSE 'LOW'
END AS tier
FROM "TRANSACTIONS";
-- BAD: Imperative row-by-row processing
FOR row AS cursor_transactions DO
IF row.amount > 1000 THEN
UPDATE "TRANSACTIONS" SET tier = 'HIGH' WHERE id = row.id;
ELSEIF row.amount > 100 THEN
UPDATE "TRANSACTIONS" SET tier = 'MEDIUM' WHERE id = row.id;
ELSE
UPDATE "TRANSACTIONS" SET tier = 'LOW' WHERE id = row.id;
END IF;
END FOR;Impact:
- Declarative: HANA optimizer creates parallel execution plan
- Imperative: Forces sequential processing, bypasses optimizer
---
3. Avoid Engine Mixing
Problem: HANA uses specialized engines for different operations. Mixing them causes data conversion overhead.
| Engine | Purpose | Triggered By |
|---|---|---|
| Column Engine | Columnar storage operations | Queries on column tables, most SQLScript |
| Row Engine | Row-store table operations | Queries on row tables, transactional operations |
| Calculation Engine | Complex expressions, CE functions | CE_* functions, certain calculation views |
Engine Selection: HANA automatically selects the execution engine based on:
- Table storage type (column vs row store)
- Query patterns and operations used
- Optimizer cost estimation
Solution: Keep operations within same engine type.
-- BAD: Mixing Row Store and Column Store
SELECT c.* FROM "COLUMN_STORE_TABLE" c
JOIN "ROW_STORE_TABLE" r ON c.id = r.id;
-- BETTER: Convert to same store type or redesign
-- Or at minimum, be aware of the conversion costAvoid mixing:
- Row Store tables with Column Store tables
- SQL operations with Calculation Engine (CE) functions
- SQLScript CE functions in performance-critical paths
---
4. Optimize Set Operations
Problem: UNION, INTERSECT, EXCEPT don't utilize Column Engine efficiently.
Solution: Replace with JOIN operations where possible, or use UNION ALL.
-- GOOD: UNION ALL (no duplicate removal)
SELECT id, name, 'A' AS source FROM table_a
UNION ALL
SELECT id, name, 'B' AS source FROM table_b;
-- SLOWER: UNION (removes duplicates)
SELECT id, name FROM table_a
UNION
SELECT id, name FROM table_b;
-- ALTERNATIVE: Use JOIN instead of INTERSECT
-- Instead of: SELECT id FROM a INTERSECT SELECT id FROM b
SELECT DISTINCT a.id
FROM table_a AS a
INNER JOIN table_b AS b ON a.id = b.id;---
5. Eliminate Dynamic SQL
Problem: Dynamic SQL requires re-optimization on every execution.
Solution: Use static SQL with parameters.
-- BAD: Dynamic SQL
lv_sql = 'SELECT * FROM "' || :lv_table || '" WHERE status = ''' || :lv_status || '''';
EXECUTE IMMEDIATE :lv_sql;
-- GOOD: Static SQL with parameters
SELECT * FROM "ORDERS" WHERE status = :lv_status;Additional issues with dynamic SQL:
- SQL injection vulnerabilities
- No query plan caching
- Harder to debug and maintain
---
6. Position Imperative Logic Last
Problem: Imperative statements block parallel processing of subsequent code.
Solution: Structure procedures with declarative logic first, imperative at the end.
CREATE PROCEDURE optimized_processing ()
LANGUAGE SQLSCRIPT AS
BEGIN
-- 1. Declarative operations FIRST (run in parallel)
lt_data = SELECT * FROM "SOURCE_TABLE" WHERE active = 1;
lt_aggregated = SELECT category, SUM(amount) AS total
FROM :lt_data GROUP BY category;
lt_joined = SELECT a.*, b.name
FROM :lt_aggregated AS a
JOIN "CATEGORIES" AS b ON a.category = b.id;
-- 2. Final imperative operations LAST
FOR row AS (SELECT * FROM :lt_joined) DO
IF row.total > 10000 THEN
INSERT INTO "ALERTS" VALUES (row.category, row.total, CURRENT_TIMESTAMP);
END IF;
END FOR;
END;---
Query Optimization Techniques
Use Appropriate Indexes
-- Create index for frequently filtered columns
CREATE INDEX idx_orders_status ON "ORDERS" (status);
CREATE INDEX idx_orders_date ON "ORDERS" (order_date);
-- Composite index for multi-column filters
CREATE INDEX idx_orders_status_date ON "ORDERS" (status, order_date);Leverage Partitioning
-- Partition large tables by date
CREATE COLUMN TABLE "TRANSACTIONS" (
id INTEGER,
trans_date DATE,
amount DECIMAL(15,2)
) PARTITION BY RANGE (trans_date) (
PARTITION '2023' <= VALUES < '2024',
PARTITION '2024' <= VALUES < '2025',
PARTITION OTHERS
);Use Query Hints
-- Hint to use specific execution strategy
SELECT /*+ USE_OLAP_PLAN */ * FROM "LARGE_TABLE";
-- Hint to disable parallelism (when needed)
SELECT /*+ NO_PARALLEL */ * FROM "SMALL_TABLE";---
Table Variable Best Practices
Avoid Scalar Variables in Parallel Sections
-- BAD: Scalar variables break parallelism
DECLARE lv_count INTEGER;
SELECT COUNT(*) INTO lv_count FROM "TABLE1";
-- Subsequent operations must wait
-- BETTER: Use table variables
lt_stats = SELECT COUNT(*) AS cnt FROM "TABLE1";
-- Can continue parallel processingUse Table Variable Operators
-- Efficient in-memory operations
:lt_data.INSERT((:lv_id, :lv_name));
:lt_data.UPDATE((:lv_new_name), 1);
:lt_data.DELETE(1);---
Monitoring and Analysis Tools
Plan Visualizer
Analyze execution plans to identify bottlenecks:
1. Open SQL Console in HANA Studio/Web IDE 2. Execute query with "Visualize Plan" 3. Look for:
- Full table scans (add indexes)
- Engine conversions (avoid mixing)
- Large intermediate results (filter earlier)
Expensive Statement Trace
Enable to capture slow queries:
ALTER SYSTEM ALTER CONFIGURATION ('indexserver.ini', 'SYSTEM')
SET ('expensive_statement', 'enable') = 'true';
ALTER SYSTEM ALTER CONFIGURATION ('indexserver.ini', 'SYSTEM')
SET ('expensive_statement', 'threshold_duration') = '1000000'; -- 1 second in microsecondsSQL Analyzer
Get optimization recommendations:
-- In SAP HANA Studio
EXPLAIN PLAN FOR <your_query>;
SELECT * FROM "EXPLAIN_CALL_PLANS";---
Memory Management
Control Result Set Size
-- Limit results for testing/debugging
SELECT TOP 1000 * FROM "LARGE_TABLE";
-- Use LIMIT for pagination
SELECT * FROM "TABLE" ORDER BY id LIMIT 100 OFFSET 200;Release Resources
-- Explicitly close cursors
CLOSE my_cursor;
-- Use smaller table variables when possible
lt_small = SELECT id, name FROM :lt_large; -- Only needed columns---
Anti-Patterns to Avoid
| Anti-Pattern | Problem | Solution |
|---|---|---|
SELECT * | Retrieves unnecessary columns | List specific columns |
| Cursor loops for updates | Row-by-row processing | Set-based UPDATE |
| DISTINCT on large sets | Memory intensive | Filter earlier or redesign |
| Correlated subqueries | N+1 query pattern | Use JOIN |
| Multiple sequential queries | No parallelism | Combine into single query |
| String concatenation in loops | Memory fragmentation | Use STRING_AGG |
---
Performance Checklist
Before deploying SQLScript:
- [ ] Columns explicitly listed (no
SELECT *) - [ ] WHERE filters applied early
- [ ] Set-based operations preferred over loops
- [ ] No dynamic SQL unless absolutely necessary
- [ ] Declarative statements before imperative
- [ ] Appropriate indexes exist
- [ ] Execution plan reviewed
- [ ] No engine mixing issues
- [ ] Table variables used efficiently
- [ ] Resource cleanup in place
---
Benchmarking Template
DO
BEGIN
DECLARE lv_start TIMESTAMP;
DECLARE lv_end TIMESTAMP;
DECLARE lv_duration BIGINT;
lv_start = CURRENT_TIMESTAMP;
-- Your code here
lt_result = SELECT * FROM "LARGE_TABLE" WHERE condition;
lv_end = CURRENT_TIMESTAMP;
lv_duration = NANO100_BETWEEN(:lv_start, :lv_end) / 10000; -- milliseconds
SELECT :lv_duration AS execution_time_ms FROM DUMMY;
END;SQLScript Skill Reference Guide
This guide provides quick navigation to all reference documentation in the sap-sqlscript skill.
Reference Files Overview
| File | Description | Lines | Use When |
|---|---|---|---|
| syntax-reference.md | Complete SQLScript syntax patterns | ~564 | Looking up CREATE PROCEDURE, function syntax, control flow |
| built-in-functions.md | All built-in function categories | ~518 | Finding string, date, numeric, aggregate, window functions |
| data-types.md | Data type documentation | ~187 | Choosing correct data types, type conversion |
| exception-handling.md | Error handling patterns | ~491 | Implementing EXIT HANDLER, SIGNAL, RESIGNAL |
| amdp-integration.md | AMDP implementation guide | ~527 | Creating AMDP classes, method types, ABAP integration |
| performance-guide.md | Optimization techniques | ~406 | Optimizing procedures, avoiding anti-patterns |
| advanced-features.md | Advanced SQLScript features | ~846 | Lateral joins, JSON, query hints, CE functions |
| troubleshooting.md | Common errors and solutions | ~540 | Debugging errors, resolving common issues |
---
Quick Navigation by Topic
Getting Started
- Syntax basics: syntax-reference.md - Procedure, function, anonymous block syntax
- Data types: data-types.md - All supported types and conversion functions
- Built-in functions: built-in-functions.md - String, date, numeric functions
Core Development
- Stored Procedures: syntax-reference.md - CREATE PROCEDURE patterns
- User-Defined Functions: syntax-reference.md - Scalar and table UDFs
- Variables & Table Types: syntax-reference.md - Declaration patterns
- Control Structures: syntax-reference.md - IF, WHILE, FOR, LOOP
- Cursors: syntax-reference.md - DECLARE, OPEN, FETCH, CLOSE
Error Handling
- EXIT HANDLER: exception-handling.md - Basic and advanced patterns
- CONDITION: exception-handling.md - Named conditions
- SIGNAL/RESIGNAL: exception-handling.md - User-defined exceptions
- Error codes: exception-handling.md - Common SQL error codes
- Error logging: exception-handling.md - Logging patterns
ABAP Integration
- AMDP Classes: amdp-integration.md - Class structure and interface
- AMDP Methods: amdp-integration.md - Procedures, functions, CDS
- Data Type Mapping: amdp-integration.md - ABAP to SQLScript types
- AMDP Restrictions: amdp-integration.md - Limitations and workarounds
- Debugging AMDP: amdp-integration.md - Eclipse ADT debugging
Performance Optimization
- Code-to-Data: performance-guide.md - Fundamental paradigm
- Declarative vs Imperative: performance-guide.md - When to use each
- Engine Mixing: performance-guide.md - Avoiding Row/Column store issues
- Cursor Performance: performance-guide.md - When cursors are acceptable
- Memory Management: performance-guide.md - Large result sets
- Index Strategies: performance-guide.md - Index usage in SQLScript
Functions Reference
- String Functions: built-in-functions.md - SUBSTRING, CONCAT, TRIM, etc.
- Numeric Functions: built-in-functions.md - ROUND, ABS, MOD, etc.
- Date/Time Functions: built-in-functions.md - ADD_DAYS, EXTRACT, etc.
- Conversion Functions: built-in-functions.md - CAST, TO_VARCHAR, etc.
- Aggregate Functions: built-in-functions.md - SUM, COUNT, AVG, etc.
- Window Functions: built-in-functions.md - ROW_NUMBER, RANK, LAG, LEAD
- NULL Handling: built-in-functions.md - COALESCE, IFNULL, NULLIF
Advanced Features
- Lateral Joins: advanced-features.md - CROSS APPLY, OUTER APPLY
- JSON Support: advanced-features.md - JSON_VALUE, JSON_TABLE
- Query Hints: advanced-features.md - INDEX, LOOKUPS, NO_ROW_LOCK
- Currency Conversion: advanced-features.md - CONVERT_CURRENCY
- Unit Conversion: advanced-features.md - CONVERT_UNIT
- CE Functions: advanced-features.md - Calculation Engine functions
- Array Functions: advanced-features.md - Array aggregation
- Pragmas: advanced-features.md - Compiler directives
Troubleshooting
- Common Errors: troubleshooting.md - Error messages and solutions
- Invalid Column/Table: troubleshooting.md - Name resolution issues
- Type Conversion: troubleshooting.md - Implicit/explicit conversion
- Performance Issues: troubleshooting.md - Memory and timeout errors
- Security Errors: troubleshooting.md - Privilege and authorization
- AMDP Errors: troubleshooting.md - AMDP-specific issues
- Debugging Strategies: troubleshooting.md - Step-by-step debugging
---
Common Use Case Mapping
| I want to... | Reference File |
|---|---|
| Create a stored procedure | syntax-reference.md |
| Create a table function | syntax-reference.md |
| Handle errors in my procedure | exception-handling.md |
| Create an AMDP class | amdp-integration.md |
| Optimize slow procedure | performance-guide.md |
| Work with JSON data | advanced-features.md |
| Convert currency values | advanced-features.md |
| Fix a specific error | troubleshooting.md |
| Find a specific function | built-in-functions.md |
| Choose the right data type | data-types.md |
---
Search Patterns
Use these patterns to search within reference files:
# Find all function signatures
grep -E "^[A-Z_]+\(" references/*.md
# Find all error codes
grep -E "SQL_ERROR_CODE|error code" references/*.md
# Find all examples
grep -A5 "Example:" references/*.md
# Find AMDP patterns
grep -E "BY DATABASE|AMDP" references/*.md---
Version Information
- SAP HANA Platform: 2.0 SPS08
- SAP HANA Cloud: QRC 1/2026
- AMDP Support: NetWeaver 7.40 SP05+
- Last Updated: 2025-12-27
---
Related Skills
For comprehensive SAP development, combine this skill with:
- sap-abap - ABAP programming patterns for AMDP context
- sap-cap-capire - CAP framework database procedures integration
- sap-hana-cli - HANA CLI for procedure deployment and testing
- sap-abap-cds - CDS views that consume SQLScript procedures
- sap-btp-cloud-platform - BTP deployment of HANA artifacts
SQLScript Complete Syntax Reference
Table of Contents
- Procedure Syntax
- CREATE PROCEDURE
- Parameter Modes
- Procedure Body
- DROP PROCEDURE
- Function Syntax
- CREATE Scalar Function
- CREATE Table Function
- DROP FUNCTION
- Anonymous Block Syntax
- Variable Declaration
- Scalar Variables
- Table Variables
- Array Variables
- Control Flow Statements
- IF Statement
- CASE Statement
- WHILE Loop
- FOR Loop
- LOOP Statement
- Cursor Operations
- DECLARE CURSOR
- OPEN Cursor
- FETCH Cursor
- CLOSE Cursor
- Exception Handling
- DECLARE EXIT HANDLER
- DECLARE CONDITION
- SIGNAL Statement
- RESIGNAL Statement
- Table Type Definition
- Assignment Statements
- Simple Assignment
- SELECT INTO
- DDL Statements
- CREATE TABLE
- DROP TABLE
- CREATE VIEW
- DROP VIEW
- Built-in Functions
- Aggregate Functions
- Window Functions
- String Functions
- Date/Time Functions
- Conversion Functions
- Special Constructs
- DUMMY Table
- SESSION_USER
- CURRENT_SCHEMA
Procedure Syntax
CREATE PROCEDURE
CREATE [OR REPLACE] PROCEDURE <schema_name>.<procedure_name>
(
[IN <parameter_name> <sql_type> [DEFAULT <default_value>]],
[OUT <parameter_name> <sql_type>],
[INOUT <parameter_name> <sql_type>]
)
LANGUAGE SQLSCRIPT
[SQL SECURITY {DEFINER | INVOKER}]
[DEFAULT SCHEMA <schema_name>]
[READS SQL DATA]
[WITH RESULT VIEW <view_name>]
[DETERMINISTIC]
AS
BEGIN
[SEQUENTIAL EXECUTION]
<procedure_body>
END;Note: SEQUENTIAL EXECUTION forces the procedure to execute sequentially without parallelism. This is rarely needed but can be useful for procedures that rely on side effects or specific ordering guarantees.Parameter Modes
| Mode | Description |
|---|---|
IN | Input parameter (read-only within procedure) |
OUT | Output parameter (write-only, initial value undefined) |
INOUT | Input and output (read/write) |
Security Modes
| Mode | Description |
|---|---|
SQL SECURITY DEFINER | Execute with privileges of procedure owner |
SQL SECURITY INVOKER | Execute with privileges of caller |
---
Function Syntax
Scalar User-Defined Function
CREATE [OR REPLACE] FUNCTION <schema_name>.<function_name>
(
<parameter_name> <sql_type> [DEFAULT <value>],
...
)
RETURNS <return_type>
LANGUAGE SQLSCRIPT
[SQL SECURITY {DEFINER | INVOKER}]
[DEFAULT SCHEMA <schema_name>]
[DETERMINISTIC]
AS
BEGIN
DECLARE result <return_type>;
-- function logic
RETURN result;
END;Table User-Defined Function
CREATE [OR REPLACE] FUNCTION <schema_name>.<function_name>
(
<parameter_name> <sql_type> [DEFAULT <value>],
...
)
RETURNS TABLE (
<column_name> <sql_type>,
...
)
LANGUAGE SQLSCRIPT
SQL SECURITY INVOKER
READS SQL DATA
[DEFAULT SCHEMA <schema_name>]
AS
BEGIN
RETURN SELECT <columns> FROM <source>;
END;---
Anonymous Block Syntax
DO [(<parameter_clause>)]
BEGIN [SEQUENTIAL EXECUTION]
<block_body>
END;With Parameters
DO (
IN iv_input INTEGER => 100,
OUT ov_output INTEGER => ?
)
BEGIN
ov_output = :iv_input * 2;
END;---
Variable Declaration Syntax
Scalar Variables
DECLARE <variable_name> <sql_type> [:= <initial_value>];
DECLARE <var1>, <var2> <sql_type>; -- Multiple variables same typeConstant Variables
DECLARE <constant_name> CONSTANT <sql_type> := <value>;Table Variables
-- Inline definition
DECLARE <table_var> TABLE (
<column_name> <sql_type> [NOT NULL],
...
);
-- From existing table structure
DECLARE <table_var> TABLE LIKE <table_name>;
DECLARE <table_var> TABLE LIKE :<other_table_var>;
-- From table type
DECLARE <table_var> <table_type_name>;Array Variables
DECLARE <array_name> <sql_type> ARRAY;
DECLARE <array_name> <sql_type> ARRAY := ARRAY(<value1>, <value2>, ...);---
Assignment Syntax
Scalar Assignment
<variable> := <expression>;
<variable> = <expression>; -- Alternative syntaxSELECT INTO
SELECT <column> INTO <variable> FROM <table> [WHERE ...];
SELECT <col1>, <col2> INTO <var1>, <var2> FROM <table>;Table Variable Assignment
<table_var> = SELECT <columns> FROM <table>;
<table_var> = :<other_table_var>;---
Control Flow Syntax
IF-THEN-ELSE
IF <condition> THEN
<statements>
[ELSEIF <condition> THEN
<statements>]
[ELSE
<statements>]
END IF;CASE Expression
-- Simple CASE
CASE <expression>
WHEN <value1> THEN <result1>
WHEN <value2> THEN <result2>
ELSE <default_result>
END
-- Searched CASE
CASE
WHEN <condition1> THEN <result1>
WHEN <condition2> THEN <result2>
ELSE <default_result>
ENDWHILE Loop
WHILE <condition> DO
<statements>
END WHILE;DO n TIMES Loop
Repeat a block a fixed number of times:
DO <n> TIMES
BEGIN
<statements>
END;Example:
DO 10 TIMES
BEGIN
INSERT INTO "LOG_TABLE" (message) VALUES ('Iteration');
END;FOR Loop
-- Numeric range (inclusive on both ends)
FOR <var> IN [REVERSE] <start>..<end> DO
<statements>
END FOR;
-- Cursor iteration
FOR <row_var> AS <cursor_name> DO
<statements using row_var.column_name>
END FOR;
-- Inline cursor
FOR <row_var> AS (SELECT <columns> FROM <table>) DO
<statements>
END FOR;Range Semantics: The numeric FOR loop range is inclusive on both bounds. FOR i IN 1..5 iterates with i = 1, 2, 3, 4, 5 (five iterations total).LOOP with EXIT
LOOP
<statements>
IF <condition> THEN
BREAK; -- or LEAVE
END IF;
[CONTINUE;] -- Skip to next iteration
END LOOP;---
Cursor Syntax
Declaration
DECLARE CURSOR <cursor_name> FOR
<select_statement>;
-- With parameters
DECLARE CURSOR <cursor_name> (<param> <type>) FOR
SELECT * FROM <table> WHERE col = :<param>;Operations
OPEN <cursor_name>;
OPEN <cursor_name> (<param_value>);
FETCH <cursor_name> INTO <var1>, <var2>, ...;
CLOSE <cursor_name>;Cursor Attributes
| Attribute | Type | Description | Typical Usage |
|---|---|---|---|
<cursor>::ISCLOSED | BOOLEAN | TRUE if cursor is closed | Check before OPEN to avoid "already open" error |
<cursor>::NOTFOUND | BOOLEAN | TRUE if FETCH found no row | Loop termination condition after FETCH |
<cursor>::ROWCOUNT | INTEGER | Number of rows fetched so far | Progress tracking, batch processing limits |
Usage Example:
WHILE NOT cur::NOTFOUND DO -- Check NOTFOUND to exit loop
FETCH cur INTO lv_var;
IF cur::ROWCOUNT > 1000 THEN -- Limit processing
BREAK;
END IF;
END WHILE;---
Table Type Syntax
CREATE TYPE
CREATE TYPE <schema_name>.<type_name> AS TABLE (
<column_name> <sql_type> [NOT NULL],
...
);DROP TYPE
DROP TYPE <schema_name>.<type_name> [CASCADE];---
Exception Handling Syntax
EXIT HANDLER
DECLARE EXIT HANDLER FOR <condition>
<statement>;
DECLARE EXIT HANDLER FOR <condition>
BEGIN
<statements>
END;CONTINUE HANDLER
CONTINUE HANDLER catches exceptions and continues execution of the procedure (unlike EXIT HANDLER which suspends execution):
DECLARE CONTINUE HANDLER FOR <condition>
<statement>;
DECLARE CONTINUE HANDLER FOR <condition>
BEGIN
<statements>
END;Note: Use CONTINUE HANDLER when you want to log errors and continue processing. See references/exception-handling.md for detailed comparison of EXIT vs CONTINUE handlers.Condition Declaration
DECLARE <condition_name> CONDITION FOR SQL_ERROR_CODE <number>;SIGNAL
SIGNAL <condition_name>;
SIGNAL <condition_name> SET MESSAGE_TEXT = '<message>';
SIGNAL SQL_ERROR_CODE <number> SET MESSAGE_TEXT = '<message>';RESIGNAL
RESIGNAL;
RESIGNAL <condition_name>;
RESIGNAL SET MESSAGE_TEXT = '<message>';---
Table Variable Operators
INSERT
:<table_var>.INSERT((<value1>, <value2>, ...));
:<table_var>.INSERT(:<other_table_var>);
:<table_var>.INSERT(:<other_table_var>, <row_number>);UPDATE
:<table_var>.UPDATE((<new_value1>, <new_value2>, ...), <row_number>);DELETE
:<table_var>.DELETE(<row_number>);
:<table_var>.DELETE(<start_row>, <count>);SEARCH
<position> = :<table_var>.SEARCH((<column_name>, <search_value>), <start_position>);---
UNNEST Function
Convert array to table:
UNNEST(<array1> [, <array2>, ...]) [WITH ORDINALITY]
AS <table_alias> (<col1> [, <col2>, ...] [, <ordinality_col>])Example:
DECLARE arr INTEGER ARRAY := ARRAY(10, 20, 30);
lt_result = SELECT * FROM UNNEST(:arr) AS t(value);---
Dynamic SQL Syntax
EXECUTE IMMEDIATE
EXECUTE IMMEDIATE <sql_string>;
EXECUTE IMMEDIATE <sql_string> INTO <variable>;
EXECUTE IMMEDIATE <sql_string> USING <param1>, <param2>, ...;EXEC (Procedure Call)
EXEC <sql_string>;Warning: Avoid dynamic SQL for performance and security reasons.
---
Operators
Arithmetic
| Operator | Description |
|---|---|
+ | Addition |
- | Subtraction |
* | Multiplication |
/ | Division |
% | Modulo |
String
| Operator | Description |
|---|---|
| `\ | \ |
Comparison
| Operator | Description |
|---|---|
= | Equal |
!=, <> | Not equal |
< | Less than |
> | Greater than |
<= | Less than or equal |
>= | Greater than or equal |
BETWEEN | Range check |
IN | Set membership |
LIKE | Pattern matching |
IS NULL | NULL check |
IS NOT NULL | Not NULL check |
Logical
| Operator | Description |
|---|---|
AND | Logical AND |
OR | Logical OR |
NOT | Logical NOT |
---
Comments
-- Single line comment
/* Multi-line
comment */
/**
* Documentation comment
*/