SAP HANA SQL Script Reference en
SAP HANA SQL Script Reference en
Content
What is SQLScript? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
3.1
3.2
Datatype Extension. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
4.1
Scalar Datatypes. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
4.2
Table Types. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
CREATE TYPE. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
DROP TYPE. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
Logic Container. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
5.1
Procedures. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
CREATE PROCEDURE. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
DROP PROCEDURE. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
ALTER PROCEDURE. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
ALTER PROCEDURE RECOMPILE. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
Procedure Calls. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .29
Procedure Parameters. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .33
Procedure Metadata. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
5.2
5.3
6.1
6.2
6.3
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
6.4
6.5
6.6
6.7
7.1
7.2
7.3
Control Structures. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
Conditionals. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
While Loop. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
For Loop. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
Break and Continue. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
7.4
Cursors. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
Define Cursor. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
Open Cursor. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
Close Cursor. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
Fetch Query Results of a Cursor. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
Attributes of a Cursor. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
Looping over Result Sets. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
7.5
Autonomous Transaction. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
7.6
7.7
Dynamic SQL. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
EXEC. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
EXECUTE IMMEDIATE. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
APPLY_FILTER. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
7.8
7.9
ARRAY. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
DECLARE ARRAY-TYPED VARIABLE. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
SET AN ELEMENT OF AN ARRAY. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
RETURN AN ELEMENT OF AN ARRAY. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
UNNEST. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
ARRAY_AGG . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99
TRIM_ARRAY. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100
CARDINALITY. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
CONCATENATE TWO ARRAYS. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
7.10
7.11
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
7.12
7.13
8.1
8.2
8.3
10
11
11.1
11.2
::ROWCOUNT. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133
12
Supportability. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .135
12.1
M_ACTIVE_PROCEDURES. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135
13
13.1
13.2
13.3
13.4
13.5
13.6
13.7
Avoid Mixing Calculation Engine Plan Operators and SQL Queries. . . . . . . . . . . . . . . . . . . . . . . . . 141
13.8
13.9
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
14
14.1
14.2
14.3
15
Appendix. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .152
15.1
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
SQLScript is a collection of extensions to the Structured Query Language (SQL). The extensions are:
Data extension, which allows the definition of table types without corresponding tables.
Functional extension, which allows the definition of (side-effect free) functions which can be used to
express and encapsulate complex data flows.
Procedural extension, which provides imperative constructs executed in the context of the database
process.
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
This document uses BNF (Backus Naur Form) which is the notation technique used to define programming
languages. BNF describes the syntax of a grammar using a set of production rules using a set of symbols.
Symbols used in BNF
Table 1:
Symbol
Description
<>
Angle brackets are used to surround the name of a syntactic element (BNF non-terminal) of the
SQL language.
::=
The definition operator is used to provide definitions of the element appeared on the left side of
the operator in a production rule.
[]
Square brackets are used to indicate optional elements in a formula. Optional elements may be
specified or omitted.
{}
Braces group elements in a formula. Repetitive elements (zero or more elements) can be speci
fied within brace symbols.
The alternative operator indicates that the portion of the formula following the bar is an alterna
tive to the portion preceding the bar.
...
The ellipsis indicates that the element may be repeated any number of times. If ellipsis appears
after grouped elements, the grouped elements enclosed with braces are repeated. If ellipsis ap
pears after a single element, only that element is repeated.
!!
Introduces normal English text. This is used when the definition of a syntactic element is not ex
pressed in BNF.
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
What is SQLScript?
The motivation for SQLScript is to embed data-intensive application logic into the database. As of today,
applications only offload very limited functionality into the database using SQL, most of the application logic is
normally executed in an application server. This has the effect that data to be operated upon needs to be
copied from the database into the application server and vice versa. When executing data intensive logic, this
copying of data can be very expensive in terms of processor and data transfer time. Moreover, when using an
imperative language like ABAP or JAVA for processing data, developers tend to write algorithms which follow a
one tuple at a time semantics (for example looping over rows in a table). However, these algorithms are hard
to optimize and parallelize compared to declarative set-oriented languages such as SQL.
The SAP HANA database is optimized for modern technology trends and takes advantage of modern
hardware, for example, by having data residing in main-memory and allowing massive-parallelization on multicore CPUs. The goal of the SAP HANA database is to optimally support application requirements by leveraging
such hardware. To this end, the SAP HANA database exposes a very sophisticated interface to the application
consisting of many languages. The expressiveness of these languages far exceeds that attainable with
OpenSQL. The set of SQL extensions for the SAP HANA database that allow developers to push data intensive
logic into the database is called SQLScript. Conceptually SQLScript is related to stored procedures as defined
in the SQL standard, but SQLScript is designed to provide superior optimization possibilities. SQLScript
should be used in cases where other modeling constructs of SAP HANA, for example analytic views or
attribute views are not sufficient. For more information on how to best exploit the different view types, see
"Exploit Underlying Engine".
The set of SQL extensions are the key to avoid massive data copies to the application server and for leveraging
sophisticated parallel execution strategies of the database. SQLScript addresses the following problems:
Decomposing an SQL query can only be done using views. However when decomposing complex queries
using views, all intermediate results are visible and must be explicitly typed. Moreover SQL views cannot
be parameterized which limits their reuse. In particular they can only be used like tables and embedded
into other SQL statements.
SQL queries do not have features to express business logic (for example a complex currency conversion).
As a consequence such a business logic cannot be pushed down into the database (even if it is mainly
based on standard aggregations like SUM(Sales), etc.).
An SQL query can only return one result at a time. As a consequence the computation of related result
sets must be split into separate, usually unrelated, queries.
As SQLScript encourages developers to implement algorithms using a set-oriented paradigm and not
using a one tuple at a time paradigm, imperative logic is required, for example by iterative approximation
algorithms. Thus it is possible to mix imperative constructs known from stored procedures with
declarative ones.
Related Information
Exploit Underlying Engine [page 140]
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
3.1
You can develop secure procedures using SQLScript in SAP HANA by observing the following
recommendations.
Using SQLScript, you can read and modify information in the database. In some cases, depending on the
commands and parameters you choose, you can create a situation in which data leakage or data tampering
can occur. To prevent this, SAP recommends using the following practices in all procedures.
Mark each parameter using the keywords IN or OUT. Avoid using the INOUT keyword.
Use the INVOKER keyword when you want the user to have the assigned privileges to start a procedure.
The default keyword, DEFINER, allows only the owner of the procedure to start it.
Mark read-only procedures using READS SQL DATA whenever it is possible. This ensures that the data
and the structure of the database are not altered.
Tip
Another advantage to using READS SQL DATA is that it optimizes performance.
Ensure that the types of parameters and variables are as specific as possible. Avoid using VARCHAR, for
example. By reducing the length of variables you can reduce the risk of injection attacks.
Perform validation on input parameters within the procedure.
Dynamic SQL
In SQLScript you can create dynamic SQL using one of the following commands: EXEC, EXECUTE IMMEDIATE,
and APPLY_FILTER. Although these commands allow the use of variables in SQLScript where they might not
be supported. In these situations you risk injection attacks unless you perform input validation within the
procedure. In some cases injection attacks can occur by way of data from another database table.
To avoid potential vulnerability from injection attacks, consider using the following methods instead of
dynamic SQL:
Use static SQL statements. For example, use the static statement, SELECT instead of EXECUTE
IMMEDIATE and passing the values in the WHERE clause.
Use server-side JavaScript to write this procedure instead of using SQLScript.
Perform validation on input parameters within the procedure using either SQLScript or server-side
JavaScript.
Escape Code
You might need to use some SQL statements that are not supported in SQLScript, for example, the GRANT
statement. In other cases you might want to use the Data Definition Language (DDL) in which some <name>
elements, but not <value> elements, come from user input or another data source. The CREATE TABLE
statement is an example of where this situation can occur. In these cases you can use dynamic SQL to create
an escape from the procedure in the code.
10
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
To avoid potential vulnerability from injection attacks, consider using the folowing methods instead of escape
code:
Use server-side JavaScript to write this procedure instead of using SQLScript.
Perform validation on input parameters within the procedure using either SQLScript or server-side
JavaScript.
Related Information
SAP HANA Security Guide
SAP HANA SQL and System Views Reference
3.2
To better understand the features of SQLScript, and their impact on execution, it can be helpful to understand
how SQLScript is processed in the SAP HANA database.
When a user defines a new procedure, for example using the CREATE PROCEDURE statement, the SAP HANA
database query compiler processes the statement in a similar way to an SQL statement. A step by step
analysis of the process flow follows below:
Parse the statement - Detect and report simple syntactic errors.
Check the statements semantic correctness - Derive types for variables and check their use is consistent.
Optimize the code - Optimization distinguishes between declarative logic shown in the upper branch and
imperative logic shown in the lower branch. We discuss how the SAP HANA database recognizes them
below.
When the procedure starts, the invoke activity can be divided into two phases:
1. Compilation
Code generation - For declarative logic the calculation models are created to represent the dataflow
defined by the SQLScript code. It is optimized further by the calculation engine, when it is instantiated.
For imperative logic the code blocks are translated into L nodes.
The calculation models generated in the previous step are combined into a stacked calculation model.
2. Execution - The execution commences with binding actual parameters to the calculation models. When
the calculation models are instantiated they can be optimized based on concrete input provided.
Optimizations include predicate or projection embedding in the database. Finally the instantiated
calculation model is executed using any of the available parts of the SAP HANA database.
With SQLScript one can implement applications both using imperative orchestration logic and (functional)
declarative logic, and this is also reflected in the way SQLScript processing works for both coding styles.
Imperative logic is executed sequentially and declarative logic is executed by exploiting the internal
architecture of the SAP HANA database utilizing its potential for parallelism.
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
11
3.2.1 Orchestration-Logic
Orchestration logic is used to implement data flow and control flow logic using imperative language constructs
such as loops and conditionals. The orchestration logic can also execute declarative logic that is defined in the
functional extension by calling the corresponding procedures. In order to achieve an efficient execution on
both levels, the statements are transformed into a dataflow graph to the maximum extent possible. The
compilation step extracts data-flow oriented snippets out of the orchestration logic and maps them to dataflow constructs. The calculation engine serves as execution engine of the resulting dataflow graph. Since the
language L is used as intermediate language for translating SQLScript into a calculation model, the range of
mappings may span the full spectrum from a single internal L-node for a complete SQLScript script in its
simplest form, up to a fully resolved data-flow graph without any imperative code left. Typically, the dataflow
graph provides more opportunities for optimization and thus better performance.
To transform the application logic into a complex data flow graph two prerequisites have to be fulfilled:
All data flow operations have to be side-effect free, that is they must not change any global state either in
the database or in the application logic.
All control flows can be transformed into a static dataflow graph.
In SQLScript the optimizer will transform a sequence of assignments of SQL query result sets to table
variables into parallelizable dataflow constructs. The imperative logic is usually represented as a single node in
the dataflow graph, and thus it will be executed sequentially.
3.2.1.1
12
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
Related Information
ins_msg_proc [page 152]
3.2.2 Declarative-Logic
Declarative logic is used for efficient execution of data-intensive computations. This logic is internally
represented as data flows which can be executed in parallel. As a consequence, operations in a dataflow graph
have to be free of side effects. This means they must not change any global state either in the database or in
the application. The first condition is ensured by only allowing changes on the dataset that is passed as input
to the operator. The second condition is achieved by only allowing a limited subset of language features to
express the logic of the operator. Given these prerequisites, the following kinds of operators are available:
SQL SELECT Statement
Custom operators provided by SAP
Logically each operator represents a node in the data flow graph. Custom operators have to be manually
implemented by SAP.
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
13
Datatype Extension
Besides the built-in scalar SQL datatypes, SQLScript allows you to use and define user-defined types for
tabular values.
4.1
Scalar Datatypes
The SQLScript type system is based on the SQL-92 type system. It supports the following primitive data types:
Table 2:
Numeric types
VARCHAR NVARCHAR
Datetime Types
Binary Types
VARBINARY
Spatial Types
ST_GEOMETRY
ALPHANUM
Note
This is the same as for SQL statements, excluding the TEXT and SHORTTEXT types.
See SAP HANA SQL and System Views Reference, Data Types section, for further details on scalar types.
4.2
Table Types
SQLScript's datatype extension also allows the definition of table types. These table types are used to define
parameters for a procedure that represent tabular results.
14
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
Syntax
CREATE TYPE <type_name> AS TABLE (<column_list_definition>)
Syntax Elements
<type_name> ::= [<schema_name>.]<identifier>
Identifies the table type to be created and, optionally, in which schema the creation should take place.
<column_list_definition> ::= <column_elem>[{, <column_elem>}...]
<column_elem> ::= <column_name> <data_type>[<column_store_data_type>]
[<ddic_data_type>]
<column_name> ::= <identifier>
Defines a table column.
<data_type> ::= DATE | TIME | SECONDDATE | TIMESTAMP | TINYINT | SMALLINT |
INTEGER | BIGINT | SMALLDECIMAL | DECIMAL
| REAL | DOUBLE | VARCHAR | NVARCHAR | ALPHANUM | SHORTTEXT |
VARBINARY | BLOB | CLOB | NCLOB | TEXT
<column_store_data_type> ::= CS_ALPHANUM | CS_INT | CS_FIXED | CS_FLOAT |
CS_DOUBLE | CS_DECIMAL_FLOAT | CS_FIXED(p-s, s)
| CS_SDFLOAT | CS_STRING | CS_UNITEDECFLOAT |
CS_DATE | CS_TIME | CS_FIXEDSTRING | CS_RAW
| CS_DAYDATE | CS_SECONDTIME | CS_LONGDATE |
CS_SECONDDATE
<ddic_data_type> ::= DDIC_ACCP | DDIC_ALNM | DDIC_CHAR | DDIC_CDAY | DDIC_CLNT
| DDIC_CUKY | DDIC_CURR | DDIC_D16D
| DDIC_D34D | DDIC_D16R | DDIC_D34R | DDIC_D16S | DDIC_D34S
| DDIC_DATS | DDIC_DAY | DDIC_DEC
| DDIC_FLTP | DDIC_GUID | DDIC_INT1 | DDIC_INT2 | DDIC_INT4
| DDIC_INT8 | DDIC_LANG | DDIC_LCHR
| DDIC_MIN | DDIC_MON | DDIC_LRAW | DDIC_NUMC | DDIC_PREC
| DDIC_QUAN | DDIC_RAW | DDIC_RSTR
| DDIC_SEC | DDIC_SRST | DDIC_SSTR | DDIC_STRG | DDIC_STXT
| DDIC_TIMS | DDIC_UNIT | DDIC_UTCM
| DDIC_UTCL | DDIC_UTCS | DDIC_TEXT | DDIC_VARC | DDIC_WEEK
The available data types. For more information on data types, see Scalar Datatypes [page 14]
Description
The CREATE TYPE statement creates a user-defined type.
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
15
The syntax for defining table types follows the SQL syntax for defining new tables. The table type is specified
using a list of attribute names and primitive data types. For each table type, attributes must have unique
names.
Example
You create a table type called tt_publishers.
CREATE TYPE tt_publishers AS TABLE (
publisher INTEGER,
name VARCHAR(50),
price DECIMAL,
cnt INTEGER);
You create a table type called tt_years.
CREATE TYPE tt_years AS TABLE (
year VARCHAR(4),
price DECIMAL,
cnt INTEGER);
Syntax
DROP TYPE <type_name> [<drop_option>]
Syntax Elements
<type_name> ::= [<schema_name>.]<identifier>
The identifier of the table type to be dropped, with optional schema name.
<drop_option> ::= CASCADE | RESTRICT
When <drop_option> is not specified, a non-cascaded drop will be performed. This will drop only the specified
type, dependent objects of the type will be invalidated but not dropped.
The invalidated objects can be revalidated when an object with the same schema and object name is created.
16
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
Description
The DROP TYPE statement removes a user-defined table type.
Example
You create a table type called my_type.
CREATE TYPE my_type AS TABLE ( column_a DOUBLE );
You drop the my_type table type.
DROP TYPE my_type;
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
17
Logic Container
In SQLScript there are two different logic containers, Procedure and User Defined Function. The User Defined
Function container is separated into Scalar User Defined Function and Table User Defined Function.
The following sections provide an overview of the syntactical language description for both containers.
5.1
Procedures
Procedures allows you to describe a sequence of data transformations on data passed as input and database
tables.
Data transformations can be implemented as queries that follow the SAP HANA database SQL syntax by
calling other procedures. Read-only procedures can only call other read-only procedures.
The use of procedures has some advantages compared to using SQL:
The calculation and transformations described in procedures can be parameterized and reused in other
procedures.
The user is able to use and express knowledge about relationships in the data; related computations can
share common sub-expressions, and related results can be returned using multiple output parameters.
It is easy to define common sub-expressions. The query optimizer decides if a materialization strategy
(which avoids recomputation of expressions) or other optimizing rewrites are best to apply. In any case, it
eases the task to detect common sub-expressions and improves the readability of the SQLScript code.
Scalar variables or imperative language features are also available and can be used if they are required.
Syntax
CREATE PROCEDURE <proc_name> [(<parameter_clause>)] [LANGUAGE <lang>] [SQL
SECURITY <mode>] [DEFAULT SCHEMA <default_schema_name>]
[READS SQL DATA ] AS
BEGIN [SEQUENTIAL EXECUTION]
<procedure_body>
END
18
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
Syntax Elements
<proc_name> ::= [<schema_name>.]<identifier>
The identifier of the procedure to be created, with optional schema name.
<parameter_clause> ::= <parameter> [{,<parameter>}...]
The input and output parameters of the procedure.
<parameter> ::= [<param_inout>] <param_name> <param_type>
A procedure parameter with associated data type.
<param_inout> ::= IN|OUT|INOUT
Default: IN
Each parameter is marked using the keywords IN/OUT/INOUT. Input and output parameters must be
explicitly typed (i.e. no un-typed tables are supported).
<param_name> ::= <identifier>
The variable name for a parameter.
<param_type> ::= <sql_type> | <table_type> | <table_type_definition>
The input and output parameters of a procedure can have any of the primitive SQL types or a table type.
INOUT parameters can only be of scalar type.
<sql_type> ::= DATE | TIME| TIMESTAMP | SECONDDATE | TINYINT | SMALLINT |
INTEGER | BIGINT | DECIMAL | SMALLDECIMAL | REAL | DOUBLE
| VARCHAR | NVARCHAR | ALPHANUM | VARBINARY | CLOB | NCLOB | BLOB
The data type of the variable. For more information on data types see Data Types in the SAP HANA SQL and
System Views Reference.
<table_type> ::= <identifier>
A table type previously defined with the CREATE TYPE command, see CREATE TYPE [page 15].
<table_type_defintion>
::= TABLE (<column_list_definition>)
<column_list_definition> ::= <column_elem>[{, <column_elem>}...]
<column_elem> ::= <column_name> <data_type>
<column_name> ::= <identifier>
A table type implicitly defined within the signature.
LANGUAGE <lang>
<lang> ::= SQLSCRIPT | R
Default: SQLSCRIPT
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
19
Defines the programming language used in the procedure. It is good practice to define the language in all
procedure definitions.
SQL SECURITY <mode>
<mode> ::= DEFINER | INVOKER
Default: DEFINER
Specifies the security mode of the procedure.
DEFINER
Specifies that the execution of the procedure is performed with the privileges of the definer of the procedure.
INVOKER
Specifies that the execution of the procedure is performed with the privileges of the invoker of the procedure.
DEFAULT SCHEMA <default_schema_name>
<default_schema_name> ::= <unicode_name>
Specifies the schema for unqualified objects in the procedure body. If nothing is specified, then the
current_schema of the session is used.
READS SQL DATA
Marks the procedure as being read-only, side-effect free i.e. the procedure does not make modifications to the
database data or its structure. This means that the procedure does not contain DDL or DML statements, and
that the procedure only calls other read-only procedures. The advantage of using this parameter is that certain
optimizations are available for read-only procedures.
SEQUENTIAL EXECUTION
This statement will force sequential execution of the procedure logic. No parallelism takes place.
<procedure_body> ::= [<proc_decl_list>]
[<proc_handler_list>]
<proc_stmt_list>
Defines the main body of the procedure according to the programming language selected.
<proc_decl_list> ::= <proc_decl> [{<proc_decl>}]
<proc_decl> ::= DECLARE {<proc_variable>|<proc_table_variable>|<proc_cursor>|
<proc_condition>} ;
<proc_table_variable> ::= <variable_name_list> {<table_type_definition>|
<table_type>}
<proc_variable>::= <variable_name_list> [CONSTANT] {<sql_type>|<array_datatype>}
[NOT NULL][<proc_default>]
<variable_name_list>
::= <variable_name>[{, <variable_name}...]
<column_list_elements> ::= (<column_definition>[{,<column_definition>}...])
<array_datatype>
::= <sql_type> ARRAY [ = <array_constructor> ]
<array_constructor>
::= ARRAY (<expression> [ { , <expression> }...] )
<proc_default> ::= (DEFAULT | '=' ) <value>|<expression>
<value>
!!= An element of the type specified by <type> or an expression
<proc_cursor> ::= CURSOR <cursor_name> [ ( proc_cursor_param_list ) ] FOR
<subquery> ;
<proc_cursor_param_list> ::= <proc_cursor_param> [{,<proc_cursor_param>}...]
<variable_name>
::= <identifier>
<cursor_name>
::= <identifier>
20
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
<proc_cursor_param>
::= <param_name> <datatype>
<proc_condition>
::= <variable_name> CONDITION | <variable_name> CONDITION
FOR <sql_error_code>
Condition handler declaration.
<proc_handler_list> ::= <proc_handler> [{, <proc_handler>}...]
<proc_handler>::= DECLARE EXIT HANDLER FOR <proc_condition_value_list>
<proc_stmt> ;
Declares exception handlers to catch SQL exceptions.
<proc_condition_value_list> ::= <proc_condition_value>
{,<proc_condition_value>}...]
One or more condition values.
<proc_condition_value> ::= SQLEXCEPTION
| <sql_error_code> | <condition_name>
You can use a specific error code number or condition name declared on condition variable.
<proc_stmt_list> ::= {<proc_stmt>}...
<proc_stmt> ::= <proc_block>
| <proc_assign>
| <proc_single_assign>
| <proc_multi_assign>
| <proc_if>
| <proc_loop>
| <proc_while>
| <proc_for>
| <proc_foreach>
| <proc_exit>
| <proc_continue>
| <proc_signal>
| <proc_resignal>
| <proc_sql>
| <proc_open>
| <proc_fetch>
| <proc_close>
| <proc_call>
| <proc_exec>
| <proc_return>
Procedure body statements.
<proc_block> ::= BEGIN <proc_block_option>
[<proc_decl_list>]
[<proc_handler_list>]
<proc_stmt_list>
END ;
<proc_block_option> ::= [SEQUENTIAL EXECUTION ]| [AUTONOMOUS TRANSACTION] |
[PARALLEL EXECUTION]
Sections of your procedures can be nested using BEGIN and END terminals.
<proc_assign> ::= <variable_name> = { <expression> | <array_function> } ;
| <variable_name> '[' <expression> ']' = <expression> ;
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
21
Assign values to variables. An <expression> can be either a simple expression, such as a character, a date, or a
number, or it can be a scalar function or a scalar user-defined function.
<array_function> = ARRAY_AGG
( :<table_variable>.<column_name> [ ORDER BY
<sort_spec_list> ] )
| CARDINALITY ( :<array_variable_name>)
| TRIM_ARRAY ( :<array_variable_name> ,
<array_variable_name>)
| ARRAY ( <array_variable_name_list> )
<table_variable>
::= <identifier>
<column_name>
::= <identifier>
<array_variable_name> ::= <identifier>
The ARRAY_AGG function returns the array by aggregating the set of elements in the specified column of the
table variable. Elements can optionally be ordered.
The CARDINALITY function returns the number of the elements in the array, <array_variable_name>.
The TRIM_ARRAY function returns the new array by removing the given number of elements,
<numeric_value_expression>, from the end of the array, <array_value_expression>.
The ARRAY function returns an array whose elements are specified in the list <array_variable_name>. For
more information see the chapter ARRAY [page 95].
<proc_single_assign> ::=
|
|
|
<variable_name>
<variable_name>
<variable_name>
<variable_name>
=
=
=
=
<subquery>
<proc_ce_call>
<proc_apply_filter>
<unnest_function>
22
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
For more information about the CE-Operators, see Calculation Engine Plan Operators [page 108].
<proc_apply_filter> ::= APPLY_FILTER ( {<table_name> | :<table_variable>},
<variable_name> ) ;
APPLY_FILTER defines a dynamic WHERE condition <variable_name> that will be applied during runtime. For
more information about see the chapter APPLY_FILTER [page 87].
<unnest_function> ::= UNNEST ( <variable_name_list> ) [ WITH ORDINALITY ]
[<as_col_names>] ;
<variable_name_list> ::= :<variable_name> [{, :<variable_name>}...]
The UNNEST function returns a table including a row for each element of the specified array.
WITH ORDINALTIY
Appends an ordinal column to the return values.
<as_col_names>
::= AS [table_name] ( <column_name_list> )
<column_name_list> ::= <column_name>[{, <column_name>}...]
<column_name>
::= <identifier>
Specifies the column names of the return table.
<proc_if> ::= IF <condition> THEN [SEQUENTIAL EXECUTION][<proc_decl_list>]
[<proc_handler_list>] <proc_stmt_list>
[<proc_elsif_list>]
[<proc_else>]
END IF ;
<proc_elsif_list> ::= ELSEIF <condition> THEN [SEQUENTIAL EXECUTION]
[<proc_decl_list>] [<proc_handler_list>] <proc_stmt_list>
<proc_else> ::= ELSE [SEQUENTIAL EXECUTION][<proc_decl_list>]
[<proc_handler_list>] <proc_stmt_list>
You use IF - THEN - ELSE IF to control execution flow with conditionals.
<proc_loop> ::= LOOP [SEQUENTIAL EXECUTION][<proc_decl_list>]
[<proc_handler_list>] <proc_stmt_list> END LOOP ;
You use loop to repeatedly execute a set of statements.
<proc_while> ::= WHILE <condition> DO [SEQUENTIAL EXECUTION][<proc_decl_list>]
[<proc_handler_list>] <proc_stmt_list> END WHILE ;
You use while to repeatedly call a set of trigger statements while a condition is true.
<proc_for> ::= FOR <column_name> IN [ REVERSE ] <expression> [...] <expression>
DO [SEQUENTIAL EXECUTION][<proc_decl_list>] [<proc_handler_list>]
<proc_stmt_list>
END FOR ;
You use FOR - IN loops to iterate over a set of data.
<proc_foreach> ::= FOR <column_name> AS <column_name> [<open_param_list>] DO
[SEQUENTIAL EXECUTION][<proc_decl_list>] [<proc_handler_list>]
<proc_stmt_list>
END FOR ;
<open_param_list> ::= ( <expression> [ { , <expression> }...] )
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
23
You use FOR - EACH loops to iterate over all elements in a set of data.
<proc_exit>
::= BREAK ;
Terminates a loop.
<proc_continue> ::= CONTINUE ;
Skips a current loop iteration and continues with the next value.
<proc_signal>
::=
You use the SIGNAL statement to explicitly raise an exception from within your trigger procedures.
<proc_resignal> ::= RESIGNAL [<signal_value>] [<set_signal_info>] ;
You use the RESIGNAL statement to raise an exception on the action statement in an exception handler. If an
error code is not specified, RESIGNAL will throw the caught exception.
<signal_value>
::= <signal_name> | <sql_error_code>
<signal_name>
::= <identifier>
<sql_error_code> ::= <unsigned_integer>
You can SIGNAL or RESIGNAL a signal name or an SQL error code.
<set_signal_info> ::= SET MESSAGE_TEXT = '<message_string>'
<message_string> ::= <any_character>
You use SET MESSAGE_TEXT to deliver an error message to users when specified error is thrown during
procedure execution.
<proc_sql> ::=
<subquery>
| <select_into_stmt>
| <insert_stmt>
| <delete_stmt>
| <update_stmt>
| <replace_stmt>
| <call_stmt>
| <create_table>
| <drop_table>
| <truncate_statement>
For information on <insert_stmt>, see INSERT in the SAP HANA SQL and System Views Reference.
For information on <delete_stmt>, see DELETE in the SAP HANA SQL and System Views Reference.
For information on <update_stmt>, see UPDATE in the SAP HANA SQL and System Views Reference.
For information on <replace_stmt> and <upsert_stmt>, see REPLACE and UPSERT in the SAP HANA SQL
and System Views Reference.
For information on <truncate_stmt>, see TRUNCATE in the SAP HANA SQL and System Views Reference.
<select_into_stmt> ::= SELECT <select_list> INTO <var_name_list>
<from_clause >
[<where_clause>]
[<group_by_clause>]
[<having_clause>]
[{<set_operator> <subquery>, ... }]
[<order_by_clause>]
24
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
[<limit>] ;
<var_name_list> ::= <var_name>[{, <var_name>}...]
<var_name>
::= <identifier>
<var_name> is a scalar variable. You can assign selected item value to this scalar variable.
<proc_open> ::= OPEN <cursor_name> [ <open_param_list>] ;
<proc_fetch> ::= FETCH <cursor_name> INTO <column_name_list> ;
<proc_close> ::= CLOSE <cursor_name> ;
Cursor operations
<proc_call> ::= CALL <proc_name> (<param_list>) ;
Calling a procedure; for more information, see CALL - Internal Procedure Call [page 31]
<proc_exec>
Description
The CREATE PROCEDURE statement creates a procedure using the specified programming language <lang>.
Example
Example - Creating a Procedure
You create an SQLScript procedure with the following definition.
CREATE PROCEDURE orchestrationProc
LANGUAGE SQLSCRIPT AS
BEGIN
DECLARE v_id BIGINT;
DECLARE v_name VARCHAR(30);
DECLARE v_pmnt BIGINT;
DECLARE v_msg VARCHAR(200);
DECLARE CURSOR c_cursor1 (p_payment BIGINT) FOR
SELECT id, name, payment FROM control_tab
WHERE payment > :p_payment ORDER BY id ASC;
CALL init_proc();
OPEN c_cursor1(250000);
FETCH c_cursor1 INTO v_id, v_name, v_pmnt; v_msg = :v_name || ' (id '
|| :v_id || ') earns ' || :v_pmnt || ' $.';
CALL ins_msg_proc(:v_msg);
CLOSE c_cursor1;
END;
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
25
The procedure features a number of imperative constructs including the use of a cursor (with associated
state) and local scalar variables with assignments.
Syntax
Syntax Elements
Description
Drops a procedure created using CREATE PROCEDURE from the database catalog.
26
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
Examples
You drop a procedure called my_proc from the database using a non-cascaded drop.
DROP PROCEDURE my_proc;
Note
Please note that the following properties cannot be changed with ALTER PROCEDURE:
procedure owner
security mode (INVOKER, DEFINER)
procedure type (table function, scalar function, procedure)
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
27
Note
Note that you need the ALTER privilege for the object you want to change.
Syntax
Syntax Elements
Description
The ALTER PROCEDURE RECOMPILE statement manually triggers a recompilation of a procedure by
generating an updated execution plan. For production code a procedure should be compiled without the WITH
PLAN option to avoid overhead during compilation and execution of the procedure.
28
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
Example
You trigger the recompilation of the my_proc procedure to produce debugging information.
ALTER PROCEDURE my_proc RECOMPILE WITH PLAN;
Recommendation
SAP recommends that you use parameterized CALL statements for better performance. The advantages
follow.
The parameterized query compiles only once, thereby reducing the compile time.
A stored query string in the SQL plan cache is more generic and a precompiled query plan can be
reused for the same procedure call with different input parameters.
By not using query parameters for the CALL statement, the system triggers a new query plan
generation.
5.1.5.1
CALL
Syntax
CALL <proc_name> (<param_list>) [WITH OVERVIEW]
Syntax Elements
<proc_name> ::= [<schema_name>.]<identifier>
The identifier of the procedure to be called, with optional schema name.
<param_list> ::= <proc_param>[{, <proc_param>}...]
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
29
Description
Calls a procedure defined with CREATE PROCEDURE [page 18].
CALL conceptually returns a list of result sets with one entry for every tabular result. An iterator can be used to
iterate over these results sets. For each result set you can iterate over the result table in the same way as for
query results. SQL statements that are not assigned to any table variable in the procedure body will be added
as result sets at the end of the list of result sets. The type of the result structures will be determined during
compilation time but will not be visible in the signature of the procedure.
CALL when executed by the client the syntax behaves in a way consistent with the SQL standard semantics,
for example, Java clients can call a procedure using a JDBC CallableStatement. Scalar output variables will be
a scalar value that can be retrieved from the callable statement directly.
Note
Unquoted identifiers are implicitly treated as upper case. Quoting identifiers will respect capitalization and
allow for using white spaces which are normally not allowed in SQL identifiers.
Examples
For the examples, consider the following procedure signature:
CREATE PROCEDURE proc(
IN value integer,IN currency nvarchar(10),OUT outTable typeTable,
30
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
AS
BEGIN
END;
5.1.5.2
Syntax:
CALL <proc_name > (<param_list>)
Syntax Elements:
<param_list> ::= <param>[{, <param>}...]
Specifies procedure parameters.
<param>::= <in_table_param> | <in_scalar_param> |<out_scalar_param> |
<out_table_param>
Parmeters can be of table or scalar type.
<in_table_param> ::= <read_variable_identifier>|<sql_identifier>
<in_scalar_param>::=<read_variable_identifier>|<scalar_value>|<expression>
<in_param> ::= :<identifier>
Specifies a procedure input parameter.
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
31
Note
Please note the use of a colon in-front of the identifier name.
<out_param> ::= <identifier>
Specifies a procedure input parameter.
Description:
For an internal procedure, where one procedure calls another procedure, all existing variables of the caller or
literals are passed to the IN parameters of the callee and new variables of the caller are bound to the OUT
parameters of the callee. That is to say, the result is implicitly bound to the variable that is given in the function
call.
Example:
CALL addDiscount (:lt_expensive_books, lt_on_sale);
When procedure addDiscount is called, the variable <:lt_expensive_books> is assigned to the function
and the variable <lt_on_sales> is bound by this function call.
Related Information
CALL
5.1.5.3
You can call a procedure passing named parameters by using the token =>.
For example:
CALL myproc (i => 2)
When you use named parameters you can ignore the order of the parameters in the procedure signature. Run
the following commands and you can try some examples below.
create type mytab_t as table (i int);
create table mytab (i int);
insert into mytab values (0);
insert into mytab values (1);
insert into mytab values (2);
insert into mytab values (3);
insert into mytab values (4);
insert into mytab values (5);
create procedure myproc (in intab mytab_t,in i int, out outtab mytab_t) as
begin
outtab = select i from :intab where i > :i;
end;
32
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
Description
IN
An input parameter
OUT
An output parameter
INOUT
Specifies a parameter that will both pass-in and return data to and from the procedure.
Note
This is only supported for Scalar values.
Related Information
Datatype Extension [page 14]
5.1.6.1
Scalar Parameters
Consider the following procedure:
CREATE PROCEDURE test_scalar (IN i INT, IN a VARCHAR)
AS
BEGIN
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
33
You can pass tables and views to the parameter of this function.
CALL test_table (tab1)
Note
Implicit binding of multiple values is currently not supported.
You should always use sql special identifiers when binding a value to a table variable.
CALL test_table ("tab1")
Do not use the following syntax:
CALL test_table ('tab')
5.1.6.2
In the signature you can define default values for input parameters by using the DEFAULT keyword:
IN <param_name> (<sql_type>|<table_type>|<table_type_definition>) DEFAULT
(<value>|<table_name>)
The usage of the default value will be illustrated in the next example. Therefore the following tables are needed:
CREATE
INSERT
CREATE
INSERT
34
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
The procedure in the example generates a FULLNAME by the given input table and delimiter. Whereby default
values are used for both input parameters:
CREATE PROCEDURE FULLNAME(
IN INTAB TABLE(FirstName NVARCHAR (20), LastName NVARCHAR (20)) DEFAULT NAMES,
IN delimiter VARCHAR(10) DEFAULT ', ',
OUT outtab TABLE(fullname NVarchar(50))
)
AS
BEGIN
outtab = SELECT lastname||:delimiter|| firstname AS FULLNAME FROM :intab;
END;
For the tabular input parameter INTAB the default table NAMES is defined and for the scalar input parameter
DELIMITER the , is defined as default. To use the default values in the signature, you need to pass in
parameters using Named Parameters. That means to call the procedure FULLNAME and using the default value
would be done as follows:
CALL FULLNAME (outtab=>?);
The result of that call is:
FULLNAME
-------DOE,JOHN
Now we want to pass a different table, i.e. MYNAMES but still want to use the default delimiter value, the call
looks then as follows:
CALL FULLNAME(INTAB=> MYNAMES, outtab => ?)
And the result shows that now the table MYNAMES was used:
FULLNAME
-------DOE,ALICE
Note
Please note that default values are not supported for output parameters.
Related Information
Call with Named Parameters [page 32]
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
35
5.1.6.3
For tabular IN and OUT parameter the keyword EMPTY can be used to define an empty input table as a default:
(IN|OUT) <param_name> (<table_type>|<table_type_definition>) DEFAULT EMPTY
Whereas the general default value handling is only supported for input parameters, the DEFAULT EMPTY is
supported for both, tabular IN and OUT parameters.
In the following example we use the DEFAULT EMPTY for the tabular output parameter to be able to declare a
procedure with an empty body.
CREATE PROCEDURE PROC_EMPTY (OUT OUTTAB TABLE(I INT) DEFAULT EMPTY)
AS
BEGIN
END;
Creating the procedure without DEFAULT EMPTY would lead to the error that OUTTAB is not assigned. The
procedure PROC_EMPTY can be called as usual and returns an empty result set:
call PROC_EMPTY (?);
The next example illustrate the usage for a tabular input parameter.
CREATE PROCEDURE CHECKINPUT (IN intab TABLE(I INT ) DEFAULT EMPTY,
OUT result NVARCHAR(20)
)
AS
BEGIN
IF IS_EMPTY(:intab) THEN
result = 'Input is empty';
ELSE
result = 'Input is not empty';
END IF;
END;
Calling the procedure without passing an input table
call CHECKINPUT(result=>?)
leads to the following result:
OUT(1)
----------------'Input is empty'
Note
Please note that DEFAULT EMPTY is so far only supported in a procedure signature.
36
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
Related Information
SAP HANA SQL and System Views Reference
5.1.7.1
SYS.PROCEDURES
Structure
Table 4:
Column name
Data type
Description
SCHEMA_NAME
NVARCHAR(256)
PROCEDURE_NAME
NVARCHAR(256)
PROCEDURE_OID
BIGINT
DEFAULT_SCHEMA_NAME
NVARCHAR(256)
INPUT_PARAMETER_COUNT
INTEGER
OUTPUT_PARAMETER_COUNT
INTEGER
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
37
Column name
Data type
Description
INOUT_PARAMETER_COUNT
INTEGER
RESULT_SET_COUNT
INTEGER
IS_UNICODE
VARCHAR(5)
DEFINITION
NCLOB
PROCEDURE_TYPE
VARCHAR(10)
READ_ONLY
VARCHAR(5)
IS_VALID
VARCHAR(5)
5.1.7.2
SYS. PROCEDURE_PARAMETERS
Structure
Table 5:
Column name
Data type
Description
SCHEMA_NAME
NVARCHAR(256)
PROCEDURE_NAME
NVARCHAR(256)
PROCEDURE_OID
BIGINT
PARAMETER_NAME
NVARCHAR(256)
Parameter name
DATA_TYPE_ID
SMALLINT
Data type ID
DATA_TYPE_NAME
VARCHAR(16)
LENGTH
INTEGER
Parameter length
38
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
Column name
Data type
Description
SCALE
INTEGER
POSITION
INTEGER
TABLE_TYPE_SCHEMA
NVARCHAR(256)
TABLE_TYPE_NAME
NVARCHAR(256)
PARAMETER_TYPE
VARCHAR(7)
HAS_DEFAULT_VALUE
VARCHAR(5)
IS_NULLABLE
VARCHAR(5)
5.1.7.3
SYS.OBJECT_DEPENDENCIES
Dependencies between objects, for example, views which refer to a specific table
Structure
Table 6:
Column name
Data type
Description
BASE_SCHEMA_NAME
NVARCHAR(256)
BASE_OBJECT_NAME
NVARCHAR(256)
BASE_OBJECT_TYPE
VARCHAR(32)
DEPENDENT_SCHEMA_NAME
NVARCHAR(256)
DEPENDENT_OBJECT_NAME
NVARCHAR(256)
DEPENDENT_OBJECT_TYPE
VARCHAR(32)
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
39
Column name
Data type
Description
DEPENDENCY_TYPE
INTEGER
0: NORMAL (default)
1: EXTERNAL_DIRECT (direct de
pendency between dependent ob
ject and base object)
2: EXTERNAL_INDIRECT (indirect
dependency between dependent
object und base object)
5: REFERENTIAL_DIRECT (foreign
key dependency between tables)
5.1.7.3.1
In this section we explore the ways in which you can query the OBJECT_DEPENDENCIES system view.
You create the following database objects and procedures.
CREATE SCHEMA deps;
CREATE TYPE mytab_t AS TABLE (id int, key_val int, val int);
CREATE TABLE mytab1 (id INT PRIMARY KEY, key_val int, val INT);
CREATE TABLE mytab2 (id INT PRIMARY key, key_val int, val INT);
CREATE PROCEDURE deps.get_tables(OUT outtab1 mytab_t, OUT outtab2 mytab_t)
LANGUAGE SQLSCRIPT READS SQL DATA AS
BEGIN
outtab1 = SELECT * FROM mytab1;
outtab2 = SELECT * FROM mytab2;
END;
CREATE PROCEDURE deps.my_proc (IN val INT, OUT outtab mytab_t) LANGUAGE
SQLSCRIPT READS SQL DATA
AS
BEGIN
CALL deps.get_tables(tab1, tab2);
IF :val > 1 THEN
outtab = SELECT * FROM :tab1;
ELSE
outtab = SELECT * FROM :tab2;
END IF;
END;
Object dependency examination
Firstly you will find all the (direct and indirect) base objects of the procedure DEPS.GET_TABLES. You execute
the following statement.
SELECT * FROM OBJECT_DEPENDENCIES WHERE dependent_object_name = 'GET_TABLES' and
dependent_schema_name = 'DEPS';
40
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
BASE_OB
JECT_NAME
BASE_OB
JECT_TYPE
DEPEND
ENT_SCHEMA
_NAME
DEPEND
cbfS5D
JECT_NAME
DEPEND
cbfS5D
JECT_TYPE
DEPEND
ENCY_TYPE
SYSTEM
MYTAB_T
TABLE
DEPS
GET_TABLES
PROCEDURE
SYSTEM
MYTAB1
TABLE
DEPS
GET_TABLES
PROCEDURE
SYSTEM
MYTAB2
TABLE
DEPS
GET_TABLES
PROCEDURE
DEPS
GET_TABLES
PROCEDURE
DEPS
GET_TABLES
PROCEDURE
Lets examine the DEPENDENCY_TYPE column in more detail. As you obtained the results in the table above
via a select on all the base objects of the procedure, the objects show include both persistent and transient
objects. You can distinguish between these object dependency types using the DEPENDENCY_TYPE column,
as shown below:
BASE_OB
JECT_NAME
BASE_OB
JECT_TYPE
DEPEND
ENT_SCHEMA
_NAME
DEPEND
cbfS5D
JECT_NAME
DEPEND
cbfS5D
JECT_TYPE
DEPEND
ENCY_TYPE
SYSTEM
MYTAB_T
TABLE
DEPS
MY_PROC
PROCEDURE
DEPS
GET_TABLES
PROCEDURE
DEPS
MY_PROC
PROCEDURE
Finally you find all the dependent objects that are using DEPS.MY_PROC. You execute the following statement.
SELECT * FROM OBJECT_DEPENDENCIES WHERE base_object_name = 'GET_TABLES' and
base_schema_name = 'DEPS' ;
The result obtained is as follows:
Table 9:
BASE_SCHEM
A_NAME
BASE_OB
JECT_NAME
BASE_OB
JECT_TYPE
DEPEND
ENT_SCHEMA
_NAME
DEPEND
cbfS5D
JECT_NAME
DEPEND
cbfS5D
JECT_TYPE
DEPEND
ENCY_TYPE
DEPS
GET_TABLES
PROCEDURE
DEPS
MY_PROC
PROCEDURE
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
41
5.2
There are two different kinds of user defined functions (UDF): Table User Defined Functions and Scalar User
Defined Functions. They are referred to as Table UDF and Scalar UDF in the following table. They differ by
input/output parameters, supported functions in the body, and the way they are consumed in SQL
statements.
Table 10:
Functions Calling
Input Parameter
Table UDF
Scalar UDF
Table types
Output
Supported functionality
Syntax
42
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
Syntax Elements
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
43
Note
Only SQLScript UDF can be defined.
SQL SECURITY <mode>
<mode> ::= DEFINER | INVOKER
Default: DEFINER (Table UDF) / INVOKER (Scalar UDF)
Specifies the security mode of the function.
DEFINER
Specifies that the execution of the function is performed with the privileges of the definer of the function.
INVOKER
Specifies that the execution of the function is performed with the privileges of the invoker of the function.
DEFAULT SCHEMA <default_schema_name>
<default_schema_name> ::= <unicode_name>
Specifies the schema for unqualified objects in the function body. If nothing is specified, then the
current_schema of the session is used.
<function_body>
::= <scalar_function_body>|<table_function_body>
<scalar_function_body> ::= [DECLARE <func_var>]
<proc_assign>
<table_function_body> ::= [<func_block_decl_list>]
[<func_handler_list>]
<func_stmt_list>
<func_return_statement>
Defines the main body of the table UDF and scalar UDF. As the function is flagged as read-only, neither DDL
nor DML statements (INSERT, UPDATE, and DELETE) are allowed in the function body. A scalar UDF does not
support table-typed variables as its input and table operations in the function body.
For the definition of <proc_assign>, see CREATE PROCEDURE [page 18].
<func_block_decl_list>
<func_var>
<array_datatype> } [NOT
<array_datatype>
<array_constructor>
<func_default>
<func_expr>
Defines one or more local variables with associated scalar type or array type.
44
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
An array type has <type> as its element type. An Array has a range from 1 to 2,147,483,647, which is the
limitation of underlying structure.
You can assign default values by specifying <expression>s. See Expressions in the SAP HANA SQL and
System Views Reference .
<func_handler_list> ::= <proc_handler_list>
See CREATE PROCEDURE [page 18].
<func_stmt_list> ::= <func_stmt>| <func_stmt_list> <func_stmt>
<func_stmt>
::= <proc_block>
| <proc_assign>
| <proc_single_assign>
| <proc_if>
| <proc_while>
| <proc_for>
| <proc_foreach>
| <proc_exit>
| <proc_signal>
| <proc_resignal>
| <proc_open>
| <proc_fetch>
| <proc_close>
For further information of the definitions in <func_stmt>, see CREATE PROCEDURE [page 18]..
<func_return_statement> ::= RETURN <function_return_expr>
<func_return_expr>
::= <table_variable> | <subquery>
A table function must contain a return statement.
Example
How to create a table function is shown in the following example:
CREATE FUNCTION scale (val INT)
RETURNS TABLE (a INT, b INT) LANGUAGE SQLSCRIPT AS
BEGIN
RETURN SELECT a, :val * b AS b FROM mytab;
END;
How to call the table function scale is shown in the following example:
SELECT * FROM scale(10);
SELECT * FROM scale(10) AS a, scale(10) AS b where a.a =
b.a
How to create a scalar function of name func_add_mul that takes two values of type double and returns two
values of type double is shown in the following example:
CREATE FUNCTION func_add_mul(x Double, y Double)
RETURNS result_add Double, result_mul Double
LANGUAGE SQLSCRIPT READS SQL DATA AS
BEGIN
result_add = :x + :y;
result_mul = :x * :y;
END;
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
45
In a query you can either use the scalar function in the projection list or in the where-clause. In the following
example the func_add_mul is used in the projection list:
CREATE TABLE TAB (a Double, b Double);
INSERT INTO TAB VALUES (1.0, 2.0);
INSERT INTO TAB VALUES (3.0, 4.0);
SELECT a, b, func_add_mul(a, b).result_add as ADD, func_add_mul(a,
b).result_mul as MUL FROM TAB ORDER BY a;
A
B
ADD
MUL
------------------1
2
3
2
3
4
7
12
Besides using the scalar function in a query you can also use a scalar function in scalar assignment, e.g.:
CREATE FUNCTION func_mul(input1 INT)
RETURNS output1 INT LANGUAGE SQLSCRIPT
AS
BEGIN
output1 = :input1 * :input1;
END;
CREATE FUNCTION func_mul_wrapper(input1 INT)
RETURNS output1 INT LANGUAGE SQLSCRIPT AS
BEGIN
output1 = func_mul(:input1);
END;
SELECT func_mul_wrapper(2) as RESULT FROM dummy;
RESULT
----------------4
Syntax
DROP FUNCTION <func_name> [<drop_option>]
Syntax Elements
<func_name> ::= [<schema_name>.]<identifier>
The name of the function to be dropped, with optional schema name.
<drop_option> ::= CASCADE | RESTRICT
When <drop_option> is not specified a non-cascaded drop will be performed. This will only drop the specified
function, dependent objects of the function will be invalidated but not dropped.
46
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
The invalidated objects can be revalidated when an object that has same schema and object name is created.
CASCADE
Drops the function and dependent objects.
RESTRICT
Drops the function only when dependent objects do not exist. If this drop option is used and a dependent
object exists an error will be thrown.
Description
Drops a function created using CREATE FUNCTION from the database catalog.
Examples
You drop a function called my_func from the database using a non-cascaded drop.
DROP FUNCTION my_func;
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
47
The function GET_FUNCTIONS should now be changed to return only valid functions. In order to do so, we will
use ALTER FUNCTION:
ALTER FUNCTION GET_FUNCTIONS
returns TABLE(schema_name NVARCHAR(256),
name
NVARCHAR(256))
AS
BEGIN
return SELECT schema_name
AS schema_name,
function_name AS name
FROM FUNCTIONS
WHERE IS_VALID = 'TRUE';
END;
Besides changing the function body, you can also change the default schema <default_schema_name>.
Note
Please note that the following properties cannot be changed with ALTER FUNCTION:
function owner
security mode (INVOKER, DEFINER)
function type (table function, scalar function, procedure)
parameter signature (parameter name, parameter type, default value)
If you need to change these properties you have to drop and re-create the procedure again by using DROP
FUNCTION and CREATE FUNCTION.
Note that if the default schema is not explicitly specified, it will be removed.
Note
Note that you need the ALTER privilege for the object you want to change.
Parameter
48
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
Function
Parameter
5.2.5.1
SYS.FUNCTIONS
Structure
Table 12:
Column name
Data type
Description
SCHEMA_NAME
NVARCHAR(256)
FUNCTION_NAME
NVARCHAR(256)
FUNCTION_OID
BIGINT
INPUT_PARAMETER_COUNT
INTEGER
RETURN_VALUE_COUNT
INTEGER
IS_UNICODE
VARCHAR(5)
DEFINITION
NCLOB
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
49
Column name
Data type
Description
FUNCTION_TYPE
VARCHAR(10)
IS_VALID
VARCHAR(5)
5.2.5.2
SYS.FUNCTIONS_PARAMETERS
Structure
Table 13:
Column name
Data type
Description
SCHEMA_NAME
NVARCHAR(256)
FUNCTION_NAME
NVARCHAR(256)
FUNCTION_OID
BIGINT
PARAMETER_NAME
NVARCHAR(256)
Parameter name
DATA_TYPE_ID
INTEGER
Data type ID
DATA_TYPE_NAME
VARCHAR(16)
LENGTH
INTEGER
Parameter length
SCALE
INTEGER
POSITION
INTEGER
TABLE_TYPE_SCHEMA
NVARCHAR(256)
TABLE_TYPE_NAME
NVARCHAR(256)
PARAMETER_TYPE
50
VARCHAR(7)
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
Column name
Data type
Description
HAS_DEFAULT_VALUE
VARCHAR(5)
IS_NULLABLE
VARCHAR(5)
The function in the example generates a FULLNAME by the given input table and delimiter. Whereby default
values are used for both input parameters:
CREATE FUNCTION FULLNAME(
IN INTAB TABLE(FirstName NVARCHAR (20), LastName NVARCHAR (20)) DEFAULT NAMES,
IN delimiter VARCHAR(10) DEFAULT ', ')
returns TABLE(fullname NVarchar(50))
AS
BEGIN
return SELECT lastname||:delimiter|| firstname AS FULLNAME FROM :intab;
END;
For the tabular input parameter INTAB the default table NAMES is defined and for the scalar input parameter
DELIMITER the , is defined as default.
That means to query the function FULLNAME and using the default value would be done as follows:
SELECT * FROM
FULLNAME();
FULLNAME(INTAB=> MYNAMES);
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
51
And the result shows that now the table MYNAMES was used:
FULLNAME
-------DOE,ALICE
In a scalar function, default values can also be used, as shown in the next example:
CREATE FUNCTION GET_FULLNAME(
firstname NVARCHAR(20),
lastName NVARCHAR(20),
delimiter NVARCHAR(10) DEFAULT ','
)
RETURNS fullname NVARCHAR(50)
AS
BEGIN
fullname = :lastname||:delimiter|| :firstname;
END;
Calling that function by using the default value of the variable delimiter would be the following:
SELECT GET__FULLNAME(firstname=>firstname, lastname=>lastname) AS FULLNAME FROM
NAMES;
Note
Please note that default values are not supported for output parameters.
Related Information
Call with Named Parameters [page 32]
5.3
Anonymous Block
Anonymous block is an executable DML statement which can contain imperative or declarative statements.
All SQLScript statements supported in procedures are also supported in anonymous blocks. Compared to
procedures, an anonymous block has no corresponding object created in the metadata catalog.
An anonymous block is defined and executed in a single step by using the following syntax:
Sample Code
DO
BEGIN [SEQUENTIAL EXECUTION]
<procedure_body>
END
52
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
Also contrary to a procedure, an anonymous block has neither parameters nor container-specific properties
(for example, language, security mode, and so on.) However the body of an anonymous block is similar to the
procedure body.
Example 1:
Sample Code
DO
BEGIN
DECLARE I INTEGER;
CREATE TABLE TAB1 (I INTEGER);
FOR I IN 1..10 DO
INSERT INTO TAB1 VALUES (:I);
END FOR;
END;
This example contains an anonymous block that creates a table and inserts values into that table.
Since an anonymous block does not have any parameters defined, the only way to return a result set is by
using SELECT statements, as shown in example 2.
Example 2:
Sample Code
DO
BEGIN
T1 = SELECT I, 10 AS J FROM TAB;
T2 = SELECT I, 20 AS K FROM TAB;
T3 = SELECT J, K FROM :T1, :T2 WHERE :T1.I = :T2.I;
SELECT * FROM :T3;
END
More examples using an anonymous block follow.
Example 3:
In this example, an anonymous block calls another procedure.
Sample Code
DO
BEGIN
T1 = SELECT * FROM TAB;
CALL PROC3(:T1, :T2);
SELECT * FROM :T2;
END
Example 4:
In this example, an anonymous block uses the exception handler.
Sample Code
DO
BEGIN
DECLARE I, J INTEGER;
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
53
END
BEGIN
DECLARE EXIT HANDLER FOR SQLEXCEPTION
IF ::SQL_ERROR_CODE = 288 THEN
DROP TABLE TAB;
CREATE TABLE TAB (I INTEGER PRIMARY KEY);
ELSE
RESIGNAL;
END IF;
CREATE TABLE TAB (I INTEGER PRIMARY KEY);
END;
FOR I in 1..3 DO
INSERT INTO TAB VALUES (:I);
END FOR;
IF :J <> 3 THEN
SIGNAL SQL_ERROR_CODE 10001;
END IF;
Example 5:
In this example, an anonymous block uses commit and rollback.
Sample Code
DO
BEGIN
CREATE TABLE TAB2 (K INT);
COMMIT;
DROP TABLE TAB;
CREATE TABLE TAB (J INT);
ROLLBACK;
DELETE FROM TAB;
END
54
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
Each table assignment in a procedure or table user defined function specifies a transformation of some data
by means of classical relational operators such as selection, projection. The result of the statement is then
bound to a variable which either is used as input by a subsequent statement data transformation or is one of
the output variables of the procedure. In order to describe the data flow of a procedure, statements bind new
variables that are referenced elsewhere in the body of the procedure.
This approach leads to data flows which are free of side effects. The declarative nature to define business logic
might require some deeper thought when specifying an algorithm, but it gives the SAP HANA database
freedom to optimize the data flow which may result in better performance.
The following example shows a simple procedure implemented in SQLScript. To better illustrate the high-level
concept, we have omitted some details.
CREATE PROCEDURE getOutput( IN cnt INTEGER, IN currency VARCHAR(3),
OUT output_pubs tt_publishers, OUT output_year tt_years)
LANGUAGE SQLSCRIPT READS SQL DATA AS
BEGIN
big_pub_ids = SELECT publisher AS pid FROM books
-- Query Q1 GROUP BY
publisher HAVING COUNT(isbn) > :cnt;
big_pub_books = SELECT title, name, publisher,
-- Query Q2 year, price
FROM :big_pub_ids, publishers, books
WHERE pub_id = pid AND pub_id = publisher
AND crcy = :currency;
output_pubs = SELECT publisher, name,
-- Query Q3
SUM(price) AS price, COUNT(title) AS cnt FROM :big_pub_books GROUP BY
publisher, name;
output_year = SELECT year, SUM(price) AS price,
-- Query Q4 COUNT(title)
AS cnt
FROM :big_pub_books GROUP BY year;
END;
This SQLScript example defines a read-only procedure that has 2 scalar input parameters and 2 output
parameters of type table. The first line contains an SQL query Q1, that identifies big publishers based on the
number of books they have published (using the input parameter cnt). Next, detailed information about these
publishers along with their corresponding books is determined in query Q2. Finally, this information is
aggregated in 2 different ways in queries Q3 (aggregated per publisher) and Q4 (aggregated per year)
respectively. The resulting tables constitute the output tables of the function.
A procedure in SQLScript that only uses declarative constructs can be completely translated into an acyclic
dataflow graph where each node represents a data transformation. The example above could be represented
as the dataflow graph shown in the following image. Similar to SQL queries, the graph is analyzed and
optimized before execution. It is also possible to call a procedure from within another procedure. In terms of
the dataflow graph, this type of nested procedure call can be seen as a sub-graph that consumes intermediate
results and returns its output to the subsequent nodes. For optimization, the sub-graph of the called
procedure is merged with the graph of the calling procedure, and the resulting graph is then optimized. The
optimization applies similar rules as an SQL optimizer uses for its logical optimization (for example filter
pushdown). Then the plan is translated into a physical plan which consists of physical database operations (for
example hash joins). The translation into a physical plan involves further optimizations using a cost model as
well as heuristics.
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
55
6.1
56
Table Parameter
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
Syntax
[IN|OUT] <param_name> {<table_type>|<table_type_definition>}
<table_type> ::= <identifier>
<table_type_definition> ::= TABLE(<column_list_elements>)
Description
Table parameters that are defined in the Signature are either input or output. They must be typed explicitly.
This can be done either by using a table type previously defined with the CREATE TYPE command or by writing
it directly in the signature without any previously defined table type.
Example
(IN inputVar TABLE(I INT),OUT outputVar TABLE (I INT, J DOUBLE))
Defines the tabular structure directly in the signature.
(IN inputVar tableType, OUT outputVar outputTableType)
Using previously defined tableType and outputTableType table types.
The advantage of previously defined table type is that it can be reused by other procedure and functions. The
disadvantage is that you must take care of its lifecycle.
The advantage of a table variable structure that you directly define in the signature is that you do not need to
take care of its lifecycle. In this case, the disadvantage is that it cannot be reused.
6.2
Local table variables are, as the name suggests, variables with a reference to tabular data structure. This data
structure originates from an SQL Query.
6.3
The type of a table variable in the body of procedure or table function is either derived from the SQL Query or it
can be declared explicitly.
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
57
If the table variable derived its type from the SQL Query, the SQLScript compiler determines the type from the
first assignments of that variable. This provides a great deal of flexibility. One disadvantage, however, is that it
also lead to many type conversions in the background. This is because sometimes the derived table type does
not match the typed table parameters at the signature. This can lead to additional conversion costs, which are
unnecessary.
Another disadvantage is the cost for unnecessary internal statement compilation to derive the types.
To avoid this unnecessary cost, you can declare the type of a table variable explicitly.
Signature
DECLARE <sql_identifier> [{,<sql_identifier> }...] [CONSTANT] {TABLE
(<column_list_definition>)|<table_type>} [ <proc_table_default> ]
<proc_table_default> ::= { DEFAULT | '=' } { <select_statement> | <proc_ce_call>
| <proc_apply_filter> | <unnest_function> }
Local table variables are declared using the DECLARE keyword. For the referenced type you can either use
previously declared table type or use the inplace type definition TABLE (<column_list_definition>). The
next example illustrate both variants:
Sample Code
DECLARE temp TABLE (n int);
DECLARE temp MY_TABLE_TYPE;
You can also directly assign a default value to a table variable by using the DEFAULT keyword or =. As a
default all statements are allowed all statements that are also supported for the typical table variable
assignment.
DECLARE temp MY_TABLE_TYPE = UNNEST (:arr) as (i);
DECLARE temp MY_TABLE_TYPE DEFAULT SELECT * FROM TABLE;
The table variable can be also flagged as read-only by using the CONSTANT keyword. The consequence is that
you cannot override the variable anymore. Note that in case the CONSTANT keyword is used the table variable
should have default value since table variable cannot be NULL.
DECLARE temp CONSTANT TABLE(I INT) DEFAULT SELECT * FROM TABLE;
Description
Local table variables are declared using the DECLARE keyword. A table variable temp can be referenced by
using :temp. For more information, see Referencing Variables [page 60]. The <sql_identifier> must be
unique among all other scalar variables and table variables in the same code block. You can, however, use
names that are identical to the name of another variable in a different code block. Additionally, you can
reference these identifiers only in their local scope.
CREATE PROCEDURE exampleExplicit (OUT outTab TABLE(n int))
58
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
59
Variable scope
Derived Type
Explicitly Declared
in execution time.
used.
Note
The NOT NULL option is not supported (see also the information about scalar variable declaration).
6.4
Table variables are bound using the equality operator. This operator binds the result of a valid SELECT
statement on the right-hand side to an intermediate variable or an output parameter on the left-hand side.
Statements on the right hand side can refer to input parameters or intermediate result variables bound by
other statements. Cyclic dependencies that result from the intermediate result assignments or from calling
other functions are not allowed, that is to say recursion is not possible.
6.5
Referencing Variables
Bound variables are referenced by their name (for example, <var>). In the variable reference the variable
name is prefixed by <:> such as <:var>. The procedure or table function describe a dataflow graph using
their statements and the variables that connect the statements. The order in which statements are written in a
body can be different from the order in which statements are evaluated. In case a table variable is bound
multiple times, the order of these bindings is consistent with the order they appear in the body. Additionally,
statements are only evaluated if the variables that are bound by the statement are consumed by another
subsequent statement. Consequently, statements whose results are not consumed are removed during
optimization.
60
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
Example:
lt_expensive_books = SELECT title, price, crcy FROM :it_books
WHERE price > :minPrice AND crcy = :currency;
In this assignment, the variable <lt_expensive_books> is bound. The <:it_books> variable in the FROM
clause refers to an IN parameter of a table type. It would also be possible to consume variables of type table in
the FROM clause which were bound by an earlier statement. <:minPrice> and <:currency> refer to IN
parameters of a scalar type.
6.6
Syntax
SELECT * FROM <column_view> ( <named_parameter_list> );
Syntax Elements
<column_view> ::= <identifier>
The name of the column view.
<named_parameter_list> ::= <named_parameter> [{,<named_parameter>}}]
A list of parameters to be used with the column view.
<named_parameter> ::= <parameter_name> => <expression>
Defines the parameter used to refer to the given expression.
<parameter_name> ::= {PLACEHOLDER.<identifier> | HINT.<identifier> |
<identifier>}
The parameter name definition. PLACEHOLDER is used for place holder parameters and HINT for hint
parameters.
Description
Using column view parameter binding it is possible to pass parameters from a procedure/scripted calculation
view to a parameterized column view e.g. hierarchy view, graphical calculation view, scripted calculation view.
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
61
Examples:
Example 1 - Basic example
In the following example, assume you have the calculation view CALC_VIEW with placeholder parameters
"client" and "currency". You want to use this view in a procedure and bind the values of the parameters during
the execution of the procedure.
CREATE PROCEDURE my_proc_caller (IN in_client INT, IN in_currency INT, OUT
outtab mytab_t) LANGUAGE SQLSCRIPT READS SQL DATA AS
BEGIN
outtab = SELECT * FROM CALC_VIEW (PLACEHOLDER."$$client$$" => :in_client ,
PLACEHOLDER."$$currency$$" => :in_currency );
END;
Example 2 - Using a Hierarchical View
The following example assumes that you have a hierarchical column view "H_PROC" and you want to use this
view in a procedure. The procedure should return an extended expression that will be passed via a variable.
CREATE PROCEDURE "EXTEND_EXPRESSION"(
IN in_expr nvarchar(20),
OUT out_result "TTY_HIER_OUTPUT")
LANGUAGE SQLSCRIPT READS SQL DATA AS
BEGIN
DECLARE expr VARCHAR(256) = 'leaves(nodes())';
IF :in_expr <> '' THEN
expr = 'leaves(' || :in_expr || ')';
END IF;
out_result = SELECT query_node, result_node FROM h_proc ("expression"
=> :expr ) as h order by h.result_node;
END;
You call this procedure as follows.
CALL "EXTEND_EXPRESSION"('',?);
CALL "EXTEND_EXPRESSION"('subtree("B1")',?);
6.7
The SQLScript compiler combines statements to optimize code. Hints enable you to block or enforce the
inlining of table variables.
Note
Using a HINT needs to be considered carefully. In some cases, using a HINT could end up being more
expensive.
62
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
Block Statement-Inlining
The overall optimization guideline in SQLScript states that dependent statements are combined if possible.
For example, you have two table variable assignments as follows:
Sample Code
tab
tab2
Sample Code
select C from (select A,B,C from T where A = 1) where C=0;
There can be situations, however, when the combined statements lead to a non-optimal plan and as a result, to
less-than-optimal performance of the executed statement. In these situations it can help to block the
combination of specific statements. Therefore SAP has introduced a HINT called NO_INLINE. By placing that
HINT at the end of select statement, it blocks the combination (or inlining) of that statement into other
statements. An example of using this follows:
Sample Code
tab
tab2
By adding WITH HINT (NO_INLINE) to the table variable tab, you can block the combination of that
statement and ensure that the two statements are executed separately.
Enforce Statement-Inlining
Using the hint called INLINE helps in situations when you want to combine the statement of a nested
procedure into the outer procedure.
Currently statements that belong to nested procedure are not combined into the statements of the calling
procedures. In the following example, you have two procedures defined.
Sample Code
CREATE PROCEDURE procInner (OUT tab2 TABLE(I int))
LANGUAGE SQLSCRIPT READS SQL DATA
AS
BEGIN
tab2 = SELECT I FROM T;
END;
CREATE PROCEDURE procCaller (OUT table2 TABLE(I int))
LANGUAGE SQLSCRIPT READS SQL DATA
AS
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
63
BEGIN
call procInner (outTable);
table2 = select I from :outTable where I > 10;
END;
By executing the procedure, ProcCaller, the two table assignments are executed separately. If you want to
have both statements combined, you can do so by using WITH HINT (INLINE) at the statement of the
output table variable. Using this example, it would be written as follows:
Sample Code
CREATE PROCEDURE procInner (OUT tab2 TABLE(I int))
LANGUAGE SQLSCRIPT READS SQL DATA
AS
BEGIN
tab2 = SELECT I FROM T WITH HINT (INLINE);
END;
Now, if the procedure, ProcCaller, is executed, then the statement of table variable tab2 in ProcInner is
combined into the statement of the variable, tab, in the procedure, ProcCaller:
Sample Code
SELECT I FROM (SELECT I FROM T WITH HINT (INLINE)) where I > 10;
64
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
In this section we will focus on imperative language constructs such as loops and conditionals. The use of
imperative logic splits the logic among several dataflows. For additional information, see Orchestration-Logic
[page 12] and Declarative SQLScript Logic [page 55].
7.1
Syntax
DECLARE <sql_identifier> [CONSTANT] <type> [NOT NULL] [<proc_default>]
Syntax Elements
<proc_default> ::= (DEFAULT | '=' ) <value>|<expression>
Default value expression assignment.
<value>
Description
Local variables are declared using DECLARE keyword and they can optionally be initialized with their
declaration. By default scalar variables are initialized with NULL. A scalar variable var can be referenced the
same way as described above using :var.
Tip
If you want to access the value of the variable, then use :var in your code. If you want to assign a value to
the variable, then use var in your code.
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
65
Assignment is possible multiple times, overwriting the previous value stored in the scalar variable. Assignment
is performed using the = operator.
Recommendation
SAP recommends that you use only the = operator in defining scalar variables. (The := operator is still
available, however.)
Example
CREATE PROCEDURE proc (OUT z INT) LANGUAGE SQLSCRIPT READS SQL DATA
AS
BEGIN
DECLARE a int;
DECLARE b int = 0;
DECLARE c int DEFAULT 0;
end;
In the example you see the various ways of making declarations and assignments.
Note
Before the SAP HANA SPS 08 release, scalar UDF assignment to the scalar variable was not supported. If
you wanted to get the result value from a scalar UDF and consume it in a procedure, the scalar UDF had to
be used in a SELECT statement, even though this was expensive.
Now you can assign a scalar UDF to a scalar variable with 1 output or more than 1 output, as depicted in the
following code examples.
Consuming the result using an SQL statement:
DECLARE i INTEGER DEFAULT 0;
SELECT SUDF_ADD(:input1, :input2) into i from dummy;
Assign the scalar UDF to the scalar variable:
DECLARE i INTEGER DEFAULT 0;
i = SUDF_ADD(:input1, :input2);
Assign the scalar UDF with more than 1 output to scalar variables:
DECLARE i INTEGER DEFAULT 0;
DECLARE j NVARCHAR(5);
(i,j) = SUDF_EXPR(:input1);
DECLARE a INTEGER DEFAULT 0;
a = SUDF_EXPR(:input1).x;
66
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
7.2
SQLScript supports local variable declaration in a nested block. Local variables are only visible in the scope of
the block in which they are defined. It is also possible to define local variables inside LOOP / WHILE /FOR / IFELSE control structures.
Consider the following code:
CREATE PROCEDURE nested_block(OUT val INT) LANGUAGE SQLSCRIPT
READS SQL DATA AS
BEGIN
DECLARE a INT = 1;
BEGIN
DECLARE a INT = 2;
BEGIN
DECLARE a INT;
a = 3;
END;
val = a;
END;
END;
When you call this procedure the result is:
call nested_block(?)
--> OUT:[2]
From this result you can see that the inner most nested block value of 3 has not been passed to the val
variable. Now let's redefine the procedure without the inner most DECLARE statement:
DROP PROCEDURE nested_block;
CREATE PROCEDURE nested_block(OUT val INT) LANGUAGE SQLSCRIPT
READS SQL DATA AS
BEGIN
DECLARE a INT = 1;
BEGIN
DECLARE a INT = 2;
BEGIN
a = 3;
END;
val = a;
END;
END;
Now when you call this modified procedure the result is:
call nested_block(?)
--> OUT:[3]
From this result you can see that the innermost nested block has used the variable declared in the second level
nested block.
Local Variables in Control Structures
Conditionals
CREATE PROCEDURE nested_block_if(IN inval INT, OUT val INT) LANGUAGE SQLSCRIPT
READS SQL DATA AS
BEGIN
DECLARE a INT = 1;
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
67
DECLARE v INT = 0;
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
val = :a;
END;
v = 1 /(1-:inval);
IF :a = 1 THEN
DECLARE a INT = 2;
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
val = :a;
END;
v = 1 /(2-:inval);
IF :a = 2 THEN
DECLARE a INT = 3;
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
val = :a;
END;
v = 1 / (3-:inval);
END IF;
v = 1 / (4-:inval);
END IF;
v = 1 / (5-:inval);
END;
call nested_block_if(1, ?)
-->OUT:[1]
call nested_block_if(2, ?)
-->OUT:[2]
call nested_block_if(3, ?)
-->OUT:[3]
call nested_block_if(4, ?)
--> OUT:[2]
call nested_block_if(5, ?)
--> OUT:[1]
While Loop
CREATE PROCEDURE nested_block_while(OUT val INT) LANGUAGE SQLSCRIPT READS SQL
DATA AS
BEGIN
DECLARE v int = 2;
val = 0;
WHILE v > 0
DO
DECLARE a INT = 0;
a = :a + 1;
val = :val + :a;
v = :v - 1;
END WHILE;
END;
call nested_block_while(?)
--> OUT:[2]
For Loop
CREATE TABLE mytab1(a int);
CREATE TABLE mytab2(a int);
CREATE TABLE mytab3(a int);
INSERT INTO mytab1 VALUES(1);
INSERT INTO mytab2 VALUES(2);
INSERT INTO mytab3 VALUES(3);
CREATE PROCEDURE nested_block_for(IN inval INT, OUT val INT) LANGUAGE SQLSCRIPT
READS SQL DATA AS
BEGIN
DECLARE a1 int default 0;
68
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
Note
The example below uses tables and values created in the For Loop example above.
CREATE PROCEDURE nested_block_loop(IN inval INT, OUT val INT) LANGUAGE
SQLSCRIPT READS SQL DATA AS
BEGIN
DECLARE a1 int;
DECLARE a2 int;
DECLARE a3 int;
DECLARE v1 int default 1;
DECLARE v2 int default 1;
DECLARE v3 int default 1;
DECLARE CURSOR C FOR SELECT * FROM mytab1;
OPEN C;
FETCH C into a1;
CLOSE C;
LOOP
DECLARE CURSOR C FOR SELECT * FROM mytab2;
OPEN C;
FETCH C into a2;
CLOSE C;
LOOP
DECLARE CURSOR C FOR SELECT * FROM mytab3;
OPEN C;
FETCH C INTO a3;
CLOSE C;
IF :v2 = 1 THEN
BREAK;
END IF;
END LOOP;
IF :v1 = 1 THEN
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
69
BREAK;
END IF;
END LOOP;
IF :inval = 1 THEN
val = :a1;
ELSEIF :inval = 2 THEN
val = :a2;
ELSEIF :inval = 3 THEN
val = :a3;
END IF;
END;
call nested_block_loop(1, ?)
--> OUT:[1]
call nested_block_loop(2, ?)
--> OUT:[2]
call nested_block_loop(3, ?)
--> OUT:[3]
7.3
Control Structures
7.3.1 Conditionals
Syntax:
IF <bool_expr1>
THEN
<then_stmts1>
[{ELSEIF <bool_expr2>
THEN
<then_stmts2>}...]
[ELSE
<else_stmts3>]
END IF
Syntax elements:
<bool_expr1>
<bool_expr2>
<condition>
<comparison>
<null_check>
::=
::=
::=
::=
::=
<condition>
<condition>
<comparison> | <null_check>
<comp_val> <comparator> <comp_val>
<comp_val> IS [NOT] NULL
Note
NULL is the default value for all local variables.
See Example 2 for an example use of this comparison.
<comparator>
70
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
<comp_val>
::= <scalar_expression>|<scalar_udf>
<scalar_expression> ::=<scalar_value>[{operator}<scalar_value>]
<scalar_value> ::= <numeric_literal> | <exact_numeric_literal>|
<unsigned_numeric_literal>
<operator>::=+|-|/|*
Specifies the comparison value. This can be based on either scalar literals or scalar variables.
<then_stmts1> ::= <proc>
<then_stmts2> ::= <proc_stmts>
<else_stmts3> ::= <proc_stmts>
<proc_stmts> ::= !! SQLScript procedural statements
Defines procedural statements to be executed dependent on the preceding conditional expression.
Description:
The IF statement consists of a Boolean expression <bool_expr1>. If this expression evaluates to true then
the statements <then_stmts1> in the mandatory THEN block are executed. The IF statement ends with END
IF. The remaining parts are optional.
If the Boolean expression <bool_expr1> does not evaluate to true the ELSE-branch is evaluated. The
statements<else_stmts3> are executed without further checks. After an else branch no further ELSE branch
or ELSEIF branch is allowed.
Alternatively, when ELSEIF is used instead of ELSE a further Boolean expression <bool_expr2> is evaluated.
If it evaluates to true, the statements <then_stmts2> are executed. In this manner an arbitrary number of
ELSEIF clauses can be added.
This statement can be used to simulate the switch-case statement known from many programming
languages.
Examples:
Example 1
You use the IF statement to implementing the functionality of the SAP HANA database`s UPSERT statement.
CREATE PROCEDURE upsert_proc (IN v_isbn VARCHAR(20))
LANGUAGE SQLSCRIPT AS
BEGIN
DECLARE found INT = 1;
SELECT count(*) INTO found FROM books WHERE isbn = :v_isbn;
IF :found = 0
THEN
INSERT INTO books
VALUES (:v_isbn, 'In-Memory Data Management', 1, 1,
'2011', 42.75, 'EUR');
ELSE
UPDATE books SET price = 42.75 WHERE isbn =:v_isbn;
END IF;
END;
Example 2
You use the IF statement to check if variable :found is NULL.
SELECT count(*) INTO found FROM books WHERE isbn = :v_isbn;
IF :found IS NULL THEN
CALL ins_msg_proc('result of count(*) cannot be NULL');
ELSE
CALL ins_msg_proc('result of count(*) not NULL - as expected');
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
71
END IF;
Example 3
It is also possible to use a scalar UDF in the condition, as shown in the following example.
CREATE PROCEDURE proc (in input1 INTEGER, out output1 TYPE1)
AS
BEGIN
DECLARE i INTEGER DEFAULT :input1;
IF SUDF(:i) = 1 THEN
output1 = SELECT value FROM T1;
ELSEIF SUDF(:i) = 2 THEN
output1 = SELECT value FROM T2;
ELSE
output1 = SELECT value FROM T3;
END IF;
END;
Related Information
ins_msg_proc [page 152]
72
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
You use WHILE to increment the :v_index1 and :v_index2 variables using nested loops.
CREATE PROCEDURE procWHILE (OUT V_INDEX2 INTEGER) LANGUAGE SQLSCRIPT
READS SQL DATA
AS
BEGIN
DECLARE v_index1 INT = 0;
WHILE :v_index1 < 5 DO
v_index2 = 0;
WHILE :v_index2 < 5 DO
v_index2 = :v_index2 + 1;
END WHILE;
v_index1 = :v_index1 + 1;
END WHILE;
END;
Example 2
You can also use scalar UDF for the while condition as follows.
CREATE PROCEDURE proc (in input1 INTEGER, out output1 TYPE1)
AS
BEGIN
DECLARE i INTEGER DEFAULT :input1;
DECLARE cnt INTEGER DEFAULT 0;
WHILE SUDF(:i) > 0 DO
cnt = :cnt + 1;
i = :i - 1;
END WHILE;
output1 = SELECT value FROM T1 where id = :cnt ;
END;
Caution
No specific checks are performed to avoid infinite loops.
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
73
<signed_integer>
74
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
Syntax elements:
BREAK
Specifies that a loop should stop being processed.
CONTINUE
Specifies that a loop should stop processing the current iteration, and should immediately start processing the
next.
Description:
These statements provide internal control functionality for loops.
Example:
You defined the following loop sequence. If the loop value :x is less than 3 the iterations will be skipped. If :x is
5 then the loop will terminate.
CREATE PROCEDURE proc () LANGUAGE SQLSCRIPT
READS SQL DATA
AS
BEGIN
DECLARE x integer;
FOR x IN 0 .. 10 DO
IF :x < 3 THEN
CONTINUE;
END IF;
IF :x = 5 THEN
BREAK;
END IF;
END FOR;
END;
Related Information
ins_msg_proc [page 152]
7.4
Cursors
Cursors are used to fetch single rows from the result set returned by a query. When the cursor is declared it is
bound to a query. It is possible to parameterize the cursor query.
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
75
Note
Avoid using cursors when it is possible to express the same logic with SQL. You should do this as cursors
cannot be optimized the same way SQL can.
Example:
You create a cursor c_cursor1 to iterate over results from a SELECT on the books table. The cursor passes
one parameter v_isbn to the SELECT statement.
DECLARE CURSOR c_cursor1 (v_isbn VARCHAR(20)) FOR
SELECT isbn, title, price, crcy FROM books
WHERE isbn = :v_isbn ORDER BY isbn;
76
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
Related Information
SAP HANA SQL and System Views Reference
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
77
Description:
Closes a previously opened cursor and releases all associated state and resources. It is important to close all
cursors that were previously opened.
Example:
You close the cursor c_cursor1.
CLOSE c_cursor1;
Related Information
Attributes of a Cursor [page 79]
78
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
Description
c_cursor1::ISCLOSED
c_cursor1::NOTFOUND
c_cursor1::ROWCOUNT
Example:
The example below shows a complete procedure using the attributes of the cursor c_cursor1 to check if
fetching a set of results is possible.
CREATE PROCEDURE cursor_proc LANGUAGE SQLSCRIPT AS
BEGIN
DECLARE v_isbn
VARCHAR(20);
DECLARE v_title VARCHAR(20);
DECLARE v_price DOUBLE;
DECLARE v_crcy VARCHAR(20);
DECLARE CURSOR c_cursor1 (v_isbn VARCHAR(20)) FOR
SELECT isbn, title, price, crcy FROM books
WHERE isbn = :v_isbn ORDER BY isbn;
OPEN c_cursor1('978-3-86894-012-1');
IF c_cursor1::ISCLOSED THEN
CALL ins_msg_proc('WRONG: cursor not open');
ELSE
CALL ins_msg_proc('OK: cursor open');
END IF;
FETCH c_cursor1 INTO v_isbn, v_title, v_price, v_crcy;
IF c_cursor1::NOTFOUND THEN
CALL ins_msg_proc('WRONG: cursor contains no valid data');
ELSE
CALL ins_msg_proc('OK: cursor contains valid data');
END IF;
CLOSE c_cursor1;
END
Related Information
ins_msg_proc [page 152]
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
79
Tip
As this loop method takes care of opening and closing cursors, resource leaks can be avoided.
Consequently this loop is preferred to opening and closing a cursor explicitly and using other loop-variants.
Within the loop body, the attributes of the row that the cursor currently iterates over can be accessed like an
attribute of the cursor. Assuming <row_var> isa_row and the iterated data contains a column test, then the
value of this column can be accessed using a_row.test.
Example:
The example below demonstrates using a FOR-loop to loop over the results from c_cursor1 .
CREATE PROCEDURE foreach_proc() LANGUAGE SQLSCRIPT AS
BEGIN
DECLARE v_isbn
VARCHAR(20) = '';
80
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
Related Information
ins_msg_proc [page 152]
7.5
Autonomous Transaction
Syntax:
<proc_bloc> :: = BEGIN AUTONOMOUS TRANSACTION
[<proc_decl_list>]
[<proc_handler_list>]
[<proc_stmt_list>]
END;
Description:
The autonomous transaction is independent from the main procedure. Changes made and committed by an
autonomous transaction can be stored in persistency regardless of commit/rollback of the main procedure
transaction. The end of the autonomous transaction block has an implicit commit.
BEGIN AUTONOMOUS TRANSACTION
(some updates) (1)
COMMIT;
(some updates) (2)
ROLLBACK;
(some updates) (3)
END;
The examples show how commit and rollback work inside the autonomous transaction block. The first updates
(1) are committed, whereby the updates made in step (2) are completely rolled back. And the last updates (3)
are committed by the implicit commit at the end of the autonomous block.
CREATE PROCEDURE PROC1( IN p INT , OUT outtab TABLE (A INT)) LANGUAGE SQLSCRIPT
AS
BEGIN
DECLARE errCode INT;
DECLARE errMsg VARCHAR(5000);
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN AUTONOMOUS TRANSACTION
errCode= ::SQL_ERROR_CODE;
errMsg= ::SQL_ERROR_MESSAGE ;
INSERT INTO ERR_TABLE (PARAMETER,SQL_ERROR_CODE, SQL_ERROR_MESSAGE)
VALUES ( :p, :errCode, :errMsg);
END;
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
81
END
In the example above, an autonomous transaction is used to keep the error code in the ERR_TABLE stored in
persistency.
If the exception handler block were not an autonomous transaction, then every insert would be rolled back
because they were all made in the main transaction. In this case the result of the ERR_TABLE is as shown in the
following example.
P |SQL_ERROR_CODE| SQL_ERROR_MESSAGE
-------------------------------------------0 |
304
| division by zero undefined:
at function /()
Note
You have to be cautious if you access a table both before and inside an autonomous transaction started in a
nested procedure (e.g. TRUNCATE, update the same row), because this can lead to a deadlock situation.
One solution to avoid this is to commit the changes before entering the autonomous transaction in the
nested procedure.
82
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
7.6
Sample Code
CREATE PROCEDURE PROC1() AS
BEGIN
UPDATE B_TAB SET V = 3 WHERE ID = 1;
COMMIT;
UPDATE B_TAB SET V = 4 WHERE ID = 1;
ROLLBACK;
END;
In this example, the B_TAB table has one row before the PROC1 procedure is executed:
Table 16:
V
ID
After you execute the PROC1 procedure, the B_TAB table is updated as follows:
Table 17:
V
ID
This means only the first update in the procedure affected the B_TAB table. The second update does not affect
the B_TAB table because it was rolled back.
The following graphic provides more detail about the transactional behavior. With the first COMMIT command,
transaction tx1 is committed and the update on the B_TAB table is written to persistence. As a result of the
COMMIT, a new transaction starts, tx2.
By triggering ROLLBACK, all changes done in transaction tx2 are reverted. In Example 1, the second update is
reverted. Additionally after the rollback is performed, a new transaction starts, tx3.
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
83
The transaction boundary is not tied to the procedure block. This means that if a nested procedure contains a
COMMIT/ROLLBACK, then all statements of the top-level procedure are affected.
Example 2:
Sample Code
CREATE PROCEDURE PROC2()
BEGIN
UPDATE B_TAB SET V =
COMMIT;
END;
CREATE PROCEDURE PROC1()
BEGIN
UPDATE A_TAB SET V =
CALL PROC2();
UPDATE A_TAB SET V =
ROLLBACK;
END;
AS
3 WHERE ID = 1;
AS
2 WHERE ID = 1;
3 WHERE ID = 1;
In Example 2, the PROC1 procedure calls the PROC2procedure. The COMMIT in PROC2 commits all changes
done in the tx1 transaction (see the following graphic). This includes the first update statement in the PROC1
procedure as well as the update statement in the PROC2 procedure. With COMMIT a new transaction starts
implicitly, tx2.
Therefore the ROLLBACK command in PROC1 only affects the previous update statement; all other updates
were committed with the tx1 transaction.
84
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
Note
If you used DSQL in the past to execute these commands (for example, EXEC COMMIT,
EXEC ROLLBACK), SAP recommends that you replace all occurrences with the native commands
COMMIT/ROLLBACK because they are more secure.
The COMMIT/ROLLBACK commands are not supported in Scalar UDF or in Table UDF.
7.7
Dynamic SQL
Dynamic SQL allows you to construct an SQL statement during the execution time of a procedure. While
dynamic SQL allows you to use variables where they might not be supported in SQLScript and also provides
more flexibility in creating SQL statements, it does have the disadvantage of an additional cost at runtime:
Opportunities for optimizations are limited.
The statement is potentially recompiled every time the statement is executed.
You cannot use SQLScript variables in the SQL statement.
You cannot bind the result of a dynamic SQL statement to a SQLScript variable.
You must be very careful to avoid SQL injection bugs that might harm the integrity or security of the
database.
Note
You should avoid dynamic SQL wherever possible as it can have a negative impact on security or
performance.
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
85
7.7.1 EXEC
Syntax:
EXEC '<sql-statement>'
Description:
EXEC executes the SQL statement passed in a string argument.
Example:
You use dynamic SQL to insert a string into the message_box table.
v_sql1 = 'Third message from Dynamic SQL';
EXEC 'INSERT INTO message_box VALUES (''' || :v_sql1 || ''')';
86
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
7.7.3 APPLY_FILTER
Syntax
<variable_name> = APPLY_FILTER(<table_or_table_variable>,
<filter_variable_name>);
Syntax Elements
<variable_name> ::= <identifier>
The variable where the result of the APPLY_FILTER function will be stored.
<table_or_table_variable> ::= <table_name> | <table_variable>
You can use APPLY_FILTER with persistent tables and table variables.
<table_name> :: = <identifier>
The name of the table that is to be filtered.
<table_variable> ::= :<identifier>
The name of the table variable to be filtered.
<filter_variable_name> ::= <string_literal>
The filter command to be applied.
Note
The following constructs are not supported in the filter string <filter_variable_name>:
sub-queries, for example: CALL GET_PROCEDURE_NAME(' PROCEDURE_NAME in (SELECT
object_name FROM SYS.OBJECTS), ?);
fully-qualified column names, for example: CALL GET_PROCEDURE_NAME('
PROCEDURE.PROCEDURE_NAME = 'DSO', ?);
Description
The APPLY_FILTER function applies a dynamic filter on a table or table variable. Logically it can be considered
a partial dynamic sql statement. The advantage of the function is that you can assign it to a table variable and
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
87
will not block sql inlining. Despite this all other disadvantages of a full dynamic sql yields also for the
APPLY_FILTER.
Examples
Example 1 - Apply a filter on a persistent table
You create the following procedure
CREATE PROCEDURE GET_PROCEDURE_NAME (IN filter NVARCHAR(100), OUT procedures
outtype) AS
BEGIN
temp_procedures = APPLY_FILTER(SYS.PROCEDURES,:filter);
procedures = SELECT SCHEMA_NAME, PROCEDURE_NAME FROM :temp_procedures;
END;
You call the procedure with two different filter variables.
CALL GET_PROCEDURE_NAME(' PROCEDURE_NAME like ''TREX%''', ?);
CALL GET_PROCEDURE_NAME(' SCHEMA_NAME = ''SYS''', ?);
Example 2 - Using a table variable
CREATE TYPE outtype AS TABLE (SCHEMA_NAME NVARCHAR(256), PROCEDURE_NAME
NVARCHAR(256));
CREATE PROCEDURE GET_PROCEDURE_NAME (IN filter NVARCHAR(100), OUT procedures
outtype)
AS
BEGIN
temp_procedures = SELECT SCHEMA_NAME, PROCEDURE_NAME FROM SYS.PROCEDURES;
procedures = APPLY_FILTER(:temp_procedures,:filter);
END;
7.8
Exception Handling
Exception handling is a method for handling exception and completion conditions in an SQLScript procedure.
88
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
| <condition_name>
For example the following exit handler catches all SQLEXCEPTION and returns the information that an
exception was thrown:
DECLARE EXIT HANDLER FOR SQLEXCEPTION SELECT 'EXCEPTION was thrown' AS ERROR
FROM dummy;
For getting the error code and the error message the two system variables ::SQL_ERROR_CODE
and ::SQL_ERROR_MESSAGE can be used as it is shown in the next example:
CREATE PROCEDURE MYPROC (IN in_var INTEGER, OUT outtab TABLE(I INTEGER) ) AS
BEGIN
DECLARE EXIT HANDLER FOR SQLEXCEPTION
SELECT ::SQL_ERROR_CODE, ::SQL_ERROR_MESSAGE FROM DUMMY;
outtab = SELECT 1/:in_var as I FROM dummy;
END;
By setting <in_var> = 0 the result of the procedure execution would be:
::SQL_ERROR_CODE
::SQL_ERROR_MESSAGE
304
Division by zero undefined: the right-hand value of the division cannot be zero
at function /() (please check lines: 6)
Besides defining an exit handler for an arbitrary SQLEXCEPTION you can also define it for a specific error code
number by using the keyword SQL_ERROR_CODE followed by an SQL error code number.
For example if only the division-by-zero error should be handled the exception handler looks as follows:
DECLARE EXIT HANDLER FOR SQL_ERROR_CODE 304
SELECT ::SQL_ERROR_CODE, ::SQL_ERROR_MESSAGE FROM DUMMY;
Please note that only the SQL (code strings starting with ERR_SQL_*) and SQLScript (code strings starting
with ERR_SQLSCRIPT_*) error codes are supported in the exit handler. You can use the system view
M_ERROR_CODES to get more information about the error codes.
Instead of using an error code the exit handler can be also defined for a condition.
DECLARE EXIT HANDLER FOR MY_COND
SELECT ::SQL_ERROR_CODE, ::SQL_ERROR_MESSAGE FROM DUMMY;
How a condition is declared will be explained in section DECLARE CONDITION [page 90].
If you want to do more in the exit handler you have to use a block by using BEGINEND. For instance preparing
some additional information and inserting the error into a table:
DECLARE EXIT HANDLER FOR SQL_ERROR_CODE 304
BEGIN
DECLARE procedure_name NVARCHAR(500) =
::CURRENT_OBJECT_SCHEMA || '.' ||::CURRENT_OBJECT_NAME;
DECLARE parameters NVARCHAR(255) =
'IN_VAR = '||:in_var;
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
89
Note
Please notice in the example above that in case of an unhandled exception the transaction will be rolled
back. Thus the new row in the table LOG_TABLE is gone as well. To avoid this you can use an autonomous
transaction. You will find more information in Autonomous Transaction [page 81].
Besides declaring a condition for an already existing SQL error code, you can also declare a user-defined
condition. Either define it with or without a user-defined error code.
Considering you would need a user-defined condition for an invalid procedure input you have to declare it as in
the following example:
DECLARE invalid_input CONDITION;
Optional you can also associate a user-defined error code, e.g. 10000:
DECLARE invalid_input CONDITION FOR SQL_ERROR_CODE 10000;
Note
Please note the user-defined error codes must be within the range of 10000 to 19999.
90
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
How to signal and/or resignal a user-defined condition will be handled in the section SIGNAL and RESIGNAL
[page 91].
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
91
'
In case of calling the procedures with invalid input arguments you receive the following error message:
user-defined error: [10000] "MYSCHEMA"."GET_CUSTOMERS": line 9 col 3 (at pos
373): [10000] (range 3) user-defined error exception: START_DATE = 2011-03-03 >
END_DATE = 2010-03-03
How to handle the exception and continue with procedure execution will be explained in section DECLARE
EXIT HANDLER FOR A NESTED BLOCK.
The RESIGNAL statement is used to pass on the exception that is handled in the exit handler.
RESIGNAL [<user_defined_condition > | SQL_ERROR_CODE <int_const> ] [SET
MESSAGE_TEXT = '<message_string>']
Besides pass on the original exception by simple using RESIGNAL you can also change some information
before pass it on. Please note that the RESIGNAL statement can only be used in the exit handler.
Using RESIGNAL statement without changing the related information of an exception is done as follows:
CREATE PROCEDURE MYPROC (IN in_var INTEGER, OUT outtab TABLE(I INTEGER) ) AS
BEGIN
DECLARE EXIT HANDLER FOR SQLEXCEPTION
RESIGNAL;
outtab = SELECT 1/:in_var as I FROM dummy;
END;
In case of <in_var> = 0 the raised error would be the original SQL error code and message text.
To change the error message of an SQL error can be done by using SET MESSAGE _TEXT:
CREATE PROCEDURE MY (IN in_var INTEGER, OUT outtab TABLE(I INTEGER) )
AS
BEGIN
DECLARE EXIT HANDLER FOR SQLEXCEPTION
RESIGNAL SET MESSAGE_TEXT = 'for the input parameter in_var = '||
:in_var || ' exception was raised ';
outtab = SELECT 1/:in_var as I FROM dummy;
END;
The original SQL error message will be now replaced by the new one:
[304]: division by zero undefined: [304] "SYSTEM"."MY": line 4 col 10 (at pos
131): [304] (range 3) division by zero undefined exception: for the input
parameter in_var = 0 exception was raised
92
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
The original error message you can get via the system variable ::SQL_ERROR_MESSAGE, e.g. this is useful if
you still want to keep the original message, but like to add additional information:
CREATE PROCEDURE MY (IN in_var INTEGER, OUT outtab TABLE(I INTEGER) )
AS
BEGIN
DECLARE EXIT HANDLER FOR SQLEXCEPTION
RESIGNAL SET MESSAGE_TEXT = 'for the input parameter in_var = '||
:in_var || ' exception was raised '
|| ::SQL_ERROR_MESSAGE;
outtab = SELECT 1/:in_var as I FROM dummy;
END;
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
93
BEGIN
DECLARE myVar INT;
DECLARE EXIT HANDLER FOR SQL_ERROR_CODE 1299
BEGIN
SELECT 0 INTO myVar FROM DUMMY;
SELECT ::SQL_ERROR_CODE, ::SQL_ERROR_MESSAGE FROM DUMMY;
SELECT :myVar FROM DUMMY;
END;
SELECT I INTO myVar FROM MYTAB; --NO_DATA_FOUND exception
SELECT 'NeverReached_noContinueOnErrorSemantics' FROM DUMMY;
END;
CALL MYPROC;
Signal an exception
The SIGNAL statement can be used to explicitly raise an exception from within your procedures.
Note
The error code used must be within the user-defined range of 10000 to 19999.
94
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
Resignal an exception
The RESIGNAL statement raises an exception on the action statement in exception handler. If error code is not
specified, RESIGNAL will throw the caught exception.
CREATE TABLE MYTAB (I INTEGER PRIMARY KEY);
CREATE PROCEDURE MYPROC AS
BEGIN
DECLARE MYCOND CONDITION FOR SQL_ERROR_CODE 10001;
DECLARE EXIT HANDLER FOR MYCOND RESIGNAL;
INSERT INTO MYTAB VALUES (1);
SIGNAL MYCOND SET MESSAGE_TEXT = 'my error';
-- will not be reached
END;
CALL MYPROC;
7.9
ARRAY
An array is an indexed collection of elements of a single data type. In the following section we explore the
varying ways to define and use arrays in SQLScript.
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
95
The array constructor returns an array containing elements specified in the list of value expressions. The
following example illustrates an array constructor that contains the numbers 1, 2 and 3:
DECLARE array_int INTEGER ARRAY = ARRAY(1, 2, 3);
Besides using scalar constants you can also use scalar variables or parameters instead, as shown in the next
example.
CREATE PROCEDURE ARRAYPROC (IN a NVARCHAR(20), IN b NVARCHAR(20))
AS
BEGIN
DECLARE arrayNvarchar NVARCHAR(20) ARRAY;
arrayNvarchar = ARRAY(:a,:b);
END;
Note
Note you cannot use TEXT or SHORTTEXT as the array type.
96
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
Note
The array starts with the index 1.
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
97
For example, the following copies the value of the second element of array arr to variable var. Since the array
elements are of type NVARCHAR(15) the variable var has to have the same type:
DECLARE var NVARCHAR(15);
var = :arr[2];
Please note that you have to use : before the array variable if you read from the variable.
Instead of assigning the array element to a scalar variable it is possible to directly use the array element in the
SQL expression as well. For example, using the value of an array element as an index for another array.
DO
BEGIN
DECLARE arr TINYINT ARRAY = ARRAY(1,2,3);
DECLARE index_array INTEGER ARRAY = ARRAY(1,2);
DECLARE value TINYINT;
arr[:index_array[1]] = :arr[:index_array[2]];
value = :arr[:index_array[1]];
select :value from dummy;
END;
7.9.4 UNNEST
The UNNEST function converts one or many arrays into a table. The result table includes a row for each
element of the specified array. The result of the UNNEST function needs to be assigned to a table variable. The
syntax is:
<variable_name> = UNNEST(:<array_variable> [ {, :<array_variable>} ...] )[WITH
ORDINALITY] [AS ((<column_name> [ {, <column_name>} ])) ]
For example, the following statements convert the array id of type INTEGER and the array name of type
VARCHAR(10) into a table and assign it to the tabular output parameter rst:
CREATE PROCEDURE ARRAY_UNNEST_SIMPLE(OUT rst TTYPE)
READS SQL DATA
AS
BEGIN
DECLARE arr_id INTEGER ARRAY = ARRAY (1,2);
DECLARE arr_name VARCHAR(10) ARRAY = ARRAY('name1', 'name2', 'name3');
rst = UNNEST(:arr_id, :arr_name);
END;
For multiple arrays, the number of rows will be equal to the largest cardinality among the cardinalities of the
arrays. In the returned table, the cells that are not corresponding to any elements of the arrays are filled with
NULL values. The example above would result in the following tabular output of rst:
:ARR_ID :ARR_NAME
------------------1
name1
2
name2
?
name3
98
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
Furthermore the returned columns of the table can also be explicitly named be using the AS clause. In the
following example, the column names for :ARR_ID and :ARR_NAME are changed to ID and NAME.
rst = UNNEST(:arr_id, :arr_name) AS (ID, NAME);
The result is:
ID
NAME
------------------1
name1
2
name2
?
name3
As an additional option an ordinal column can be specified by using the WITH ORDINALITY clause.
The ordinal column will then be appended to the returned table. An alias for the ordinal column needs to be
explicitly specified. The next example illustrates the usage. SEQ is used as an alias for the ordinal column:
CREATE PROCEDURE ARRAY_UNNEST(OUT rst TABLE(AMOUNT INTEGER, SEQ INTEGER))
LANGUAGE SQLSCRIPT READS SQL DATA AS
BEGIN
DECLARE amount
INTEGER
ARRAY = ARRAY(10, 20);
rst = UNNEST(:amount) WITH ORDINALITY AS ( "AMOUNT", "SEQ");
END;
The result of calling this procedure is as follows:
AMOUNT SEQ
---------------10
1
20
2
Note
The UNNEST function cannot be referenced directly in a FROM clause of a SELECT statement.
7.9.5 ARRAY_AGG
The ARRAY_AGG function converts a column of a table variable into an array.
<array_variable_name> = ARRAY_AGG ( :<table_variable_name>.<column_name> [ORDER
BY { <expression> [ {, <expression>} ] [ ASC | DESC ] [ NULLS FIRST | NULLS
LAST ] , ... } ] )
In the following example the column A of table variable tab is aggregated into array id:
DECLARE id NVARCHAR(10) ARRAY;
DECLARE tab TABLE (A NVARCHAR(10), B INTEGER);
tab = SELECT A , B FROM tab1;
id = ARRAY_AGG(:tab.A);
The type of the array needs to have the same type as the column.
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
99
Optionally the ORDER BY clause can be used to determine the order of the elements in the array. If it is not
specified, the array elements are ordered non-deterministic. In the following example all elements of array id
are sorted descending by column B.
id
Additionally it is also possible to define where NULL values should appear in the result set. By default NULL
values are returned first for ascending ordering, and last for descending ordering. You can override this
behavior using NULLS FIRST or NULLS LAST to explicitly specify NULL value ordering. The next example
shows how the default behavior for the descending ordering can be overwritten by using NULLS FIRST:
CREATE COLUMN TABLE CTAB (A NVARCHAR(10));
INSERT INTO CTAB VALUES ('A1');
INSERT INTO CTAB VALUES (NULL);
INSERT INTO CTAB VALUES ('A2');
INSERT INTO CTAB VALUES (NULL);
DO
BEGIN
DECLARE id NVARCHAR(10) ARRAY;
tab = SELECT A FROM ctab;
id = ARRAY_AGG(:tab.A ORDER BY A DESC NULLS FIRST);
tab2 = UNNEST(:id) AS (A);
SELECT * FROM :tab2;
END;
Note
ARRAY_AGG function does not support using value expressions instead of table variables.
7.9.6 TRIM_ARRAY
The TRIM_ARRAY function removes elements from the end of an array. TRIM_ARRAY returns a new array with
a <trim_quantity> number of elements removed from the end of the array <array_variable>.
TRIM_ARRAY(:<array_variable>, <trim_quantity>)
<array_variable> ::= <identifier>
<trim_quantity> ::= <unsigned_integer>
For example, removing the last 2 elements of array array_id:
CREATE PROCEDURE ARRAY_TRIM(OUT rst TABLE (ID INTEGER))
LANGUAGE SQLSCRIPT SQL SECURITY INVOKER AS
BEGIN
DECLARE array_id
Integer ARRAY := ARRAY(1, 2, 3, 4);
array_id = TRIM_ARRAY(:array_id, 2);
rst
= UNNEST(:array_id) as ("ID");
END;
The result of calling this procedure is as follows:
ID
---
100
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
1
2
7.9.7 CARDINALITY
The CARDINALITY function returns the highest index of a set element in the array <array_variable>. It
returns N (>= 0) if the index of the N-th element is the largest among the indices.
CARDINALITY(:<array_variable>)
For example, get the size for array <array_id>.
CREATE PROCEDURE CARDINALITY_2(OUT n INTEGER) AS
BEGIN
DECLARE array_id Integer ARRAY;
n = CARDINALITY(:array_id);
END;
The result is n=0 because there is no element in the array. In the next example the cardinality is 20, as the
20th element is set. This implicitly sets the elements 1-19 to NULL:
CREATE PROCEDURE CARDINALITY_3(OUT n INTEGER) AS
BEGIN
DECLARE array_id Integer ARRAY;
array_id[20] = NULL;
n = CARDINALITY(:array_id);
END;
The CARDINALITY function can also directly be used everywhere where expressions are supported, for
example in a condition:
CREATE PROCEDURE CARDINALITY_1(OUT n INTEGER) AS
BEGIN
DECLARE array_id Integer ARRAY := ARRAY(1, 2, 3);
If CARDINALITY(:array_id) > 0 THEN
n = 1 ;
ELSE
n = 0;
END IF;
END;
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
101
AS
BEGIN
intab.A[2] = 5;
outtab = select * from :intab;
END;
Reading from a certain cell of a table variable is done in similar way as shown in the next example. Note that for
the read access the : is needed in front of the table variable.
create procedure procTCA (
IN intab TABLE(A INTEGER, B VARCHAR(20)),
OUT outvar VARCHAR(20)
)
AS
BEGIN
outvar = :intab.B[100];
END;
For the <index> the same rules apply as for the array index. That means the <index> can have any value
from 1 to 2^31 and that SQL Expression and Scalar User Defined Functions (Scalar UDF) that return a number
also can be used as an index. Furthermore instead of using a constant scalar values it is also possible to use a
scalar variable of type INTEGER as <index>.
Restrictions:
102
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
7.11
To determine whether a table or table variable is empty you can use the predicate IS_EMPTY:
IS_EMPTY( <table_name> | <table_variable> )
IS_EMPTY takes as an argument a <table_name> or a <table_variable>. It returns true if the table or
table variable is empty and false otherwise.
You can use IS_EMPTY in conditions like in if-statements or while loops. For instance in the next example
IS_EMPTY is used in an if-statement:
CREATE PROCEDURE PROC_IS_EMPTY ( IN tabvar TABLE(ID INTEGER),
OUT outtab TABLE(ID INTEGER)
)
AS
BEGIN
IF IS_EMPTY(:tabvar) THEN
RETURN;
END IF;
CALL INTERNAL_LOGIC (:tabvar, outtab);
END;
Besides that you can also use it in scalar variable assignments. But note since SQLScript does not support
BOOLEAN as scalar type, you need to assign the result of the value to a variable of type INTEGER, if needed.
That means true will be converted to 1 and false will be converted to 0.
Note
Note that the IS_EMPTY cannot be used in SQL queries or in expressions.
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
103
Example:
create table mytab(myval varchar(20));
insert into mytab values('Val1');
create procedure change_value(
in tabname varchar(20),
in field varchar(20),
in old_val varchar(20),
in new_val varchar(20)
) as
begin
declare sqlstr nclob;
sqlstr := 'UPDATE "' ||:tabname || '" SET ' || field || ' = ''' ||
new_val || ''' WHERE ' || field || ' = ''' || old_val || '''';
exec(:sqlstr);
end
The following values of input parameters can manipulate the dynamic SQL statement in an unintended way:
tabname: mytab" set myval = ' ' - field: myval = ' ' - new_val: ' - old_val: ' or 1 = 1 -This cannot happen if you validate and/or process the input values:
create procedure change_value(
in tabname varchar(20),
in field varchar(20),
in old_val varchar(20),
in new_val varchar(20)
) as
begin
declare sqlstr nclob;
declare mycond condition for sql_error_code 10001;
if is_sql_injection_safe(field) <> 1 then
signal mycond set message_text = 'Invalid field ' || field;
end if;
sqlstr := 'UPDATE "' || escape_double_quotes(:tabname) || '" SET ' ||
field || ' = ''' || escape_single_quotes(:new_val) || ''' WHERE ' || field
|| ' = ''' || escape_single_quotes(:old_val) || '''';
exec(:sqlstr);
end
Syntax IS_SQL_INJECTION_SAFE
IS_SQL_INJECTION_SAFE(<value>[, <max_tokens>])
Syntax Elements
<value> ::= <string>
String to be checked.
104
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
Description
Checks for possible SQL injection in a parameter which is to be used as a SQL identifier. Returns 1 if no
possible SQL injection is found, otherwise 0.
Example
The following code example shows that the function returns 0 if the number of tokens in the argument is
different from the expected number of a single token (default value).
SELECT IS_SQL_INJECTION_SAFE('tab,le') "safe" FROM DUMMY;
safe
------0
The following code example shows that the function returns 1 if the number of tokens in the argument matches
the expected number of 3 tokens.
SELECT IS_SQL_INJECTION_SAFE('CREATE STRUCTURED PRIVILEGE', 3) "safe" FROM DUMMY;
safe
------1
Syntax ESCAPE_SINGLE_QUOTES
ESCAPE_SINGLE_QUOTES(<value>)
Description
Escapes single quotes (apostrophes) in the given string <value>, ensuring a valid SQL string literal is used in
dynamic SQL statements to prevent SQL injections. Returns the input string with escaped single quotes.
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
105
Example
The following code example shows how the function escapes a single quote. The one single quote is escaped
with another single quote when passed to the function. The function then escapes the parameter content
Str'ing to Str''ing, which is returned from the SELECT.
SELECT ESCAPE_SINGLE_QUOTES('Str''ing') "string_literal" FROM DUMMY;
string_literal
--------------Str''ing
Syntax ESCAPE_DOUBLE_QUOTES
ESCAPE_DOUBLE_QUOTES(<value>)
Description
Escapes double quotes in the given string <value>, ensuring a valid SQL identifier is used in dynamic SQL
statements to prevent SQL injections. Returns the input string with escaped double quotes.
Example
The following code example shows that the function escapes the double quotes.
SELECT ESCAPE_DOUBLE_QUOTES('TAB"LE') "table_name" FROM DUMMY;
table_name
-------------TAB""LE
106
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
For example in the following procedure several UPDATE statements on different tables are parallelized:
CREATE COLUMN TABLE CTAB1(A INT);
CREATE COLUMN TABLE CTAB2(A INT);
CREATE COLUMN TABLE CTAB3(A INT);
CREATE COLUMN TABLE CTAB4(A INT);
CREATE COLUMN TABLE CTAB5(A INT);
CREATE PROCEDURE ParallelUpdate AS
BEGIN
BEGIN PARALLEL EXECUTION
UPDATE CTAB1 SET A = A + 1;
UPDATE CTAB2 SET A = A + 1;
UPDATE CTAB3 SET A = A + 1;
UPDATE CTAB4 SET A = A + 1;
UPDATE CTAB5 SET A = A + 1;
END;
END;
Please note that only DML statements on column store tables are supported within the parallel execution
block. In addition, the following restrictions apply:
Modification on tables with foreign key or triggers are not allowed
Updating the same table in different statements is not allowed
Reading/Writing the same table is not allowed
All other statements (i.e. CALL, variable declarations, table assignments ) are not supported within the
parallel execution block and are blocked.
In the next example several records from a table variable are inserted into different tables in parallel.
CREATE PROCEDURE ParallelInsert (IN intab TABLE (A INT, I INT)) AS
BEGIN
DECLARE tab TABLE(A INT);
tab = SELECT t.A AS A from TAB0 t
LEFT OUTER JOIN :intab s
ON s.A = t.A;
BEGIN PARALLEL EXECUTION
SELECT * FROM :tab s where
SELECT * FROM :tab s where
SELECT * FROM :tab s where
SELECT * FROM :tab s where
SELECT * FROM :tab s where
END;
END;
s.A
s.A
s.A
s.A
s.A
=
=
=
=
=
1
2
3
4
5
INTO
INTO
INTO
INTO
INTO
CTAB1;
CTAB2;
CTAB3;
CTAB4;
CTAB5;
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
107
Recommendation
SAP recommends that you use SQL rather than Calculation Engine Plan Operators with SQLScript.
The execution of Calculation Engine Plan Operators currently is bound to processing within the calculation
engine and does not allow a possibility to use alternative execution engines, such as L native execution. As
most Calculation Engine Plan Operators are converted internally and treated as SQL operations, the
conversion requires multiple layers of optimizations. This can be avoided by direct SQL use. Depending on
your system configuration and the version you use, mixing Calculation Engine Plan Operators and SQL can
lead to significant performance penalties when compared to to plain SQL implementation.
Table 18: Overview: Mapping between CE_* Operators and SQL
CE Operator
CE Syntax
SQL Equivalent
CE_COLUMN_TABLE
CE_COLUMN_TABLE(<table_nam
e>[,<attributes>])
CE_JOIN_VIEW
CE_JOIN_VIEW(<column_view_
name>[,<attributes>])
out =
CE_JOIN_VIEW("PRODUCT_SALE
S", ["PRODUCT_KEY",
product_sales;
"PRODUCT_TEXT", "SALES"]);
CE_OLAP_VIEW
CE_OLAP_VIEW
(<olap_view_name>[,<attrib
utes>])
out =
CE_OLAP_VIEW("OLAP_view",
dim1;
["DIM1", SUM("KF")]);
CE_CALC_VIEW
CE_CALC_VIEW(<calc_view_na
me>,[<attributes>])
out =
CE_CALC_VIEW("TESTCECTABLE
FROM "TESTCECTABLE";
108
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
CE Operator
CE Syntax
SQL Equivalent
CE_JOIN
CE_JOIN(<left_table>,<righ
t_table>,<join_attributes
>[<projection_list>])
SELECT [<projection_list>]
FROM
<left_table>,<right_table>
WHERE <join_attributes>
ot_pubs_books1 = CE_JOIN
(:lt_pubs, :it_books,
ot_pubs_books1 = SELECT
["PUBLISHER"]);
P.publisher AS publisher,
name, street,post_code,
city, country, isbn,
title, edition, year,
price, crcy FROM :lt_pubs
AS P, :it_books AS B WHERE
P.publisher = B.publisher;
CE_LEFT_OUTER_JOIN
CE_LEFT_OUTER_JOIN(<left_t
able>,<right_table>,<join_
attributes>[<projection_li
st>])
SELECT [<projection_list>]
FROM <left_table> LEFT
OUTER JOIN <right_table>
ON <join_attributes>
CE_RIGHT_OUTER_JOIN
CE_RIGHT_OUTER_JOIN(<left_
table>,<right_table>,<join
_attributes>[<projection_l
ist>])
SELECT [<projection_list>]
FROM <left_table> RIGHT
OUTER JOIN <right_table>
ON <join_attributes>
CE_PROJECTION
CE_PROJECTION(<table_varia
ble>,<projection_list>[,<f
ilter>])
SELECT <projection_list>
FROM <table_variable>
where [<filter>]
ot_books1 = CE_PROJECTION
(:it_books,
["TITLE","PRICE", "CRCY"
50');
CE_UNION_ALL
CE_UNION_ALL(<table_variab
le1>,<table_variable2>)
ot_all_books1 =
CE_UNION_ALL
SELECT * FROM
<table_variable1> UNION
ALL SELECT * FROM
<table_variable2>
(:lt_books, :it_audiobooks
ot_all_books2 = SELECT *
);
CE_CONVERSION
CE_CONVERSION(<table_varia
ble>,<conversion_params>,
[<rename_clause>])
SQL-Function
CONVERT_CURRENCY
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
109
CE Operator
CE Syntax
SQL Equivalent
CE_AGGREGATION
CE_AGGREGATION(<table_vari
able>,<aggregate_list>
[,<group_columns>])
SELECT <aggregate_list>
FROM <table_variable>
[GROUP BY <group_columns>]
ot_books1 = CE_AGGREGATION
(:it_books, [COUNT
("PUBLISHER") AS "CNT"],
["YEAR"]);
year;
CE_CALC(<expr>,
<result_type>)
SQL Function
CE_CALC
TEMP =
CE_PROJECTION(:table_var,
() AS "T_ID"
["ID" AS "KEY",
FROM :table_var
CE_CALC('rownum()',
INTEGER) AS "T_ID"] );
Calculation engine plan operators encapsulate data-transformation functions and can be used in the definition
of a procedure or a table user-defined function. They constitute a no longer recommended alternative to using
SQL statements. Their logic is directly implemented in the calculation engine, which is the execution
environments of SQLScript.
There are different categories of operators.
Data Source Access operators that bind a column table or a column view to a table variable.
Relational operators that allow a user to bypass the SQL processor during evaluation and to directly
interact with the calculation engine.
Special extensions that implement functions.
8.1
The data source access operators bind the column table or column view of a data source to a table variable for
reference by other built-in operators or statements in a SQLScript procedure.
8.1.1 CE_COLUMN_TABLE
Syntax:
CE_COLUMN_TABLE(<table_name> [<attributes>])
Syntax Elements:
<table_name>
110
::= [<schema_name>.]<identifier>
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
Identifies the table name of the column table, with optional schema name.
<attributes> ::= [ <attrib_name>[{, <attrib_name> }] ]
<attrib_name> ::= <string_literal>
Restricts the output to the specified attribute names.
Description:
The CE_COLUMN_TABLE operator provides access to an existing column table. It takes the name of the table
and returns its content bound to a variable. Optionally a list of attribute names can be provided to restrict the
output to the given attributes.
Note that many of the calculation engine operators provide a projection list for restricting the attributes
returned in the output. In the case of relational operators, the attributes may be renamed in the projection list.
The functions that provide data source access provide no renaming of attributes but just a simple projection.
Note
Calculation engine plan operators that reference identifiers must be enclosed with double-quotes and
capitalized, ensuring that the identifier's name is consistent with its internal representation.
If the identifiers have been declared without double-quotes in the CREATE TABLE statement (which is the
normal method), they are internally converted to upper-case letters. Identifiers in calculation engine plan
operators must match the internal representation, that is they must be upper case as well.
In contrast, if identifiers have been declared with double-quotes in the CREATE TABLE statement, they are
stored in a case-sensitive manner. Again, the identifiers in operators must match the internal
representation.
8.1.2 CE_JOIN_VIEW
Syntax:
CE_JOIN_VIEW(<column_view_name>[{,<attributes>,}...])
Syntax elements:
<column_view_name> ::= [<schema_name>.]<identifier>
Identifies the column view, with optional schema name.
<attributes> ::= [ <attrib_name>[{, <attrib_name> }] ]
<attrib_name> ::= <string_literal> [AS <column_alias>]
Specifies the name of the required columns from the column view.
column_alias ::= <string literal>
A string representing the desired column alias.
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
111
Description:
The CE_JOIN_VIEW operator returns results for an existing join view (also known as Attribute View). It takes
the name of the join view and an optional list of attributes as parameters of such views/models.
8.1.3 CE_OLAP_VIEW
Syntax:
CE_OLAP_VIEW(<olap_view_name>, '['<attributes>']')
Syntax elements:
<olap_view_name> ::= [<schema_name>.]<identifier>
Identifies the olap view, with optional schema name.
<attributes> ::= <aggregate_exp> [{, <dimension>}] [{, <aggregate_exp>}]
Specifies the attributes of the OLAP view.
Note
Note you must have at least one <aggregation_exp> in the attributes.
<aggregate_exp> ::= <aggregate_func>(<aggregate_column> [AS <column_alias>])
Specifies the required aggregation expression for the key figure.
<aggregate_func> ::= COUNT | SUM | MIN | MAX
Specifies the aggregation function to use. Supported aggregation functions are:
count("column")
sum("column")
min("column")
max("column")
use sum("column") / count("column") to compute the average
<aggregate_column> ::= <string_literal>
The identifier for the aggregation column.
<column_alias> ::= <string_literal>
Specifies an alias for the aggregate column.
<dimension> ::= <string_literal>
The dimension on which the OLAP view should be grouped.
112
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
Description:
The CE_OLAP_VIEW operator returns results for an existing OLAP view (also known as an Analytical View). It
takes the name of the OLAP view and an optional list of key figures and dimensions as parameters. The OLAP
cube that is described by the OLAP view is grouped by the given dimensions and the key figures are
aggregated using the default aggregation of the OLAP view.
8.1.4 CE_CALC_VIEW
Syntax:
CE_CALC_VIEW(<calc_view_name>, [<attributes>])
Syntax elements:
<calc_view_name> ::= [<schema_name>.]<identifier>
Identifies the calculation view, with optional schema name.
<attributes> ::= [ <attrib_name>[{, <attrib_name> }] ]
<attrib_name> ::= <string_literal>
Specifies the name of the required attributes from the calculation view.
Description:
The CE_CALC_VIEW operator returns results for an existing calculation view. It takes the name of the
calculation view and optionally a projection list of attribute names to restrict the output to the given attributes.
8.2
Relational Operators
The calculation engine plan operators presented in this section provide the functionality of relational operators
that are directly executed in the calculation engine. This allows exploitation of the specific semantics of the
calculation engine and to tune the code of a procedure if required.
8.2.1 CE_JOIN
Syntax:
CE_JOIN (<left_table>, <right_table>, <join_attributes> [<projection_list>])
Syntax elements:
<left_table>
::= :<identifier>
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
113
Note
If the optional projection list is present, it must at least contain the join attributes.
Description:
The CE_JOIN operator calculates a natural (inner) join of the given pair of tables on a list of join attributes. For
each pair of join attributes, only one attribute will be in the result. Optionally, a projection list of attribute
names can be given to restrict the output to the given attributes. Finally, the plan operator requires each pair
of join attributes to have identical attribute names. In case of join attributes having different names, one of
them must be renamed prior to the join.
8.2.2 CE_LEFT_OUTER_JOIN
Calculate the left outer join. Besides the function name, the syntax is the same as for CE_JOIN.
8.2.3 CE_RIGHT_OUTER_JOIN
Calculate the right outer join. Besides the function name, the syntax is the same as for CE_JOIN.
Note
CE_FULL_OUTER_JOIN is not supported.
114
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
8.2.4 CE_PROJECTION
Syntax:
CE_PROJECTION(<var_table>, <projection_list>[, <filter>])
Syntax elements:
<var_table> ::= :<identifier>
Specifies the table variable which is subject to the projection.
<projection_list> ::= [ <attrib_name>[{, <attrib_name> }] ]
<attrib_name>
::= <string_literal> [AS <column_alias>]
<column_alias>
::= <string_literal>
Specifies a list of attributes that should be in the resulting table. The list must at least have one element. The
attributes can be renamed using the SQL keyword AS, and expressions can be evaluated using the CE_CALC
function.
<filter> ::= <filter_expression>
Specifies an optional filter where Boolean expressions are allowed. See CE_CALC [page 116] for the filter
expression syntax.
Description:
Restricts the columns of the table variable <var_table> to those mentioned in the projection list. Optionally,
you can also rename columns, compute expressions, or apply a filter.
With this operator, the <projection_list> is applied first, including column renaming and computation of
expressions. As last step, the filter is applied.
Caution
Be aware that <filter> in CE_PROJECTION can be vulnerable to SQL injection because it behaves like
dynamic SQL. Avoid use cases where the value of <filter> is passed as an argument from outside of the
procedure by the user himself or herself, for example:
create procedure proc (in filter nvarchar (20), out output ttype)
begin
tablevar = CE_COLUMN_TABLE(TABLE);
output = CE_PROJECTION(:tablevar,
["A", "B"], '"B" = :filter );
end;
It enables the user to pass any expression and to query more than was intended, for example: '02 OR B =
01'.
SAP recommends that you use plain SQL instead.
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
115
8.2.5 CE_CALC
Syntax:
CE_CALC ('<expr>', <result_type>)
Syntax elements:
<expr> ::= <expression>
Specifies the expression to be evaluated. Expressions are analyzed using the following grammar:
b --> b1 ('or' b1)*
b1 --> b2 ('and' b2)*
b2 --> 'not' b2 | e (('<' | '>' | '=' | '<=' | '>=' | '!=') e)*
e --> '-'? e1 ('+' e1 | '-' e1)*
e1 --> e2 ('*' e2 | '/' e2 | '%' e2)*
e2 --> e3 ('**' e2)*
e3 --> '-' e2 | id ('(' (b (',' b)*)? ')')? | const | '(' b ')'
Where terminals in the grammar are enclosed, for example 'token' (denoted with id in the grammar), they are
like SQL identifiers. An exception to this is that unquoted identifiers are converted into lower-case. Numeric
constants are basically written in the same way as in the C programming language, and string constants are
enclosed in single quotes, for example, 'a string'. Inside string, single quotes are escaped by another single
quote.
An example expression valid in this grammar is: "col1" < ("col2" + "col3"). For a full list of expression
functions, see the following table.
<result_type> ::= DATE | TIME | SECONDDATE | TIMESTAMP | TINYINT
| SMALLINT | INTEGER | BIGINT | SMALLDECIMAL | DECIMAL
| REAL | DOUBLE | VARCHAR | NVARCHAR | ALPHANUM
| SHORTTEXT | VARBINARY | BLOB | CLOB | NCLOB | TEXT
Specifies the result type of the expression as an SQL type
Description:
CE_CALC is used inside other relational operators. It evaluates an expression and is usually then bound to a
new column. An important use case is evaluating expressions in the CE_PROJECTION operator. The CE_CALC
function takes two arguments:
The following expression functions are supported:
Table 19: Expression Functions
Name
Description
Conversion Functions
float
float float(arg)
double
double double(arg)
decfloat
decfloat decfloat(arg)
116
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
Syntax
Name
Description
Syntax
fixed
string
string string(arg)
date
String Functions
Functions on strings
charpos
chars
chars(string)
returns the number of characters in a
UTF-8 string. In a CESU-8 encoded
string this function returns the number
of 16-bit words utilized by the string,
just the same as if the string is en
coded using UTF-16.
strlen
int strlen(string)
midstr
leftstr
rightstr
returns arg2 bytes from the right of the string rightstr(string, int)
arg1. If arg1 is shorter than the value of
arg2, the complete string will be re
turned. 1
instr
hextoraw
rawtohex
string rawtohex(string)
converts a string of bytes to its hexa
decimal representation. The output will
contain only 0-9 and (upper case) A-F,
no spaces and is twice as many bytes
as the original string.
charpos(string, int)
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
117
Name
Description
ltrim
string ltrim(string)
string rtrim(string)
string trim(string)
rtrim
trim
lpad
rpad
Mathematical Functions
118
trim(s) = ltrim(rtrim(s))
Syntax
The math functions described here generally operate on floating point values;
their inputs will automatically convert to double, the output will also be a double.
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
Name
double log(double)
double exp(double)
double log10(double)
double sin(double)
double cos(double)
double tan(double)
double asin(double)
double acos(double)
double atan(double)
double sinh(double)
double cosh(double)
double floor(double)
double ceil(double)
sign
abs
Description
Syntax
int sign(date)
int sign(time)
int abs(int).
double abs(double)
decfloat abs(decfloat)
time abs(time)
Date Functions
utctolocal
localtoutc
localtoutc(datearg, timezonearg)
weekday
weekday(date)
now
daysbetween
daysbetween(date1, date2)
Further Functions
if
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
119
Name
Description
case
Syntax
case(arg1, default)
isnull
isnull(arg1)
rownum
rownum()
1 Due
to calendar variations with dates earlier that 1582, the use of the date data type is deprecated; you
Note
date is based on the proleptic Gregorian calendar. daydate is based on the Gregorian calendar which is
also the calendar used by SAP HANA SQL.
2 These
Calculation Engine string functions operate using single byte characters. To use these functions with
multi-byte character strings please see section: Using String Functions with Multi-byte Character Encoding
below. Note, this limitation does not exist for the SQL functions of the SAP HANA database which support
Unicode encoded strings natively.
8.2.5.1
To allow the use of the string functions of Calculation Engine with multi-byte character encoding you can use
the charpos and chars (see table above for syntax of these commands) functions. An example of this usage
for the single byte character function midstr follows below:midstr(<input_string>, charpos(<input_string>, 32), 1)
8.2.6 CE_AGGREGATION
Syntax:
CE_AGGREGATION (<var_table>, <aggregate_list> [, <group_columns>]);
Syntax elements:
<var_table>
::= :<identifier>
120
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
Note
CE_AGGREGATION cannot handle tables directly as input.
<aggregate_list> ::= '['<aggregate_exp>[{, <aggregate_exp>}] ']'
Specifies a list of aggregates. For example, [SUM ("A"), MAX("B")] specifies that in the result, column "A"
has to be aggregated using the SQL aggregate SUM and for column B, the maximum value should be given.
<aggregate_exp> ::= <aggregate_func>(<aggregate_column>[AS <column_alias>])
Specifies the required aggregation expression.
<aggregate_func> ::= COUNT | SUM | MIN | MAX
Specifies the aggregation function to use. Supported aggregation functions are:
count("column")
sum("column")
min("column")
max("column")
use sum("column") / count("column") to compute the average
<aggregate_column> ::= <string_literal>
The identifier for the aggregation column.
<column_alias> ::= <string_literal>
Specifies an alias for the aggregate column.
<group_columns> ::= '['<group_column_name> [{,<group_column_name>}...]']'
Specifies an optional list of group-by attributes. For instance, ["C"] specifies that the output should be
grouped by column C. Note that the resulting schema has a column named C in which every attribute value
from the input table appears exactly once. If this list is absent the entire input table will be treated as a single
group, and the aggregate function is applied to all tuples of the table.
<group_column_name> ::= <identifier>
Specifies the name of the column attribute for the results to be grouped by.
Note
CE_AGGREGATION implicitly defines a projection: All columns that are not in the list of aggregates, or in the
group-by list, are not part of the result.
Description:
Groups the input and computes aggregates for each group.
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
121
The result schema is derived from the list of aggregates, followed by the group-by attributes. The order of the
returned columns is defined by the order of columns defined in these lists. The attribute names are:
For the aggregates, the default is the name of the attribute that is aggregated.
For instance, in the example above ([SUM("A"),MAX("B")]), the first column is called A and the second
is B.
The attributes can be renamed if the default is not appropriate.
For the group-by attributes, the attribute names are unchanged. They cannot be renamed using
CE_AGGREGATION.
Note
Note that count(*) can be achieved by doing an aggregation on any integer column; if no group-by
attributes are provided, this counts all non-null values.
8.2.7 CE_UNION_ALL
Syntax:
CE_UNION_ALL (<var_table1>, :var_table2)
Syntax elements:
<var_table1> ::= :<identifier>
<var_table2> ::= :<identifier>
Specifies the table variables to be used to form the union.
Description:
The CE_UNION_ALL function is semantically equivalent to SQL UNION ALL statement. It computes the union
of two tables which need to have identical schemas. The CE_UNION_ALL function preserves duplicates, so the
result is a table which contains all the rows from both input tables.
8.3
Special Operators
122
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
8.3.1 CE_VERTICAL_UNION
Syntax:
CE_VERTICAL_UNION(<var_table>, <projection_list> [{,<var_table>,
<projection_list>}...])
Syntax elements:
<var_table> ::= :<identifier>
Specifies a table variable containing a column for the union.
<projection_list> ::= [ <attrib_name>[{, <attrib_name> }] ]
<attrib_name>
::= <string_literal> [AS <column_alias>]
<column_alias>
::= <string_literal>
Specifies a list of attributes that should be in the resulting table. The list must at least have one element. The
attributes can be renamed using the SQL keyword AS.
Description:
For each input table variable the specified columns are concatenated. Optionally columns can be renamed. All
input tables must have the same cardinality.
Caution
The vertical union is sensitive to the order of its input. SQL statements and many calculation engine plan
operators may reorder their input or return their result in different orders across starts. This can lead to
unexpected results.
.
8.3.2 CE_CONVERSION
Syntax:
CE_CONVERSION(<var_table>, <conversion_params>, [<rename_clause>])
Syntax elements:
<var_table> ::= :<identifier>
Specifies a table variable to be used for the conversion.
<conversion_params> ::= '['<key_val_pair>[{,<key_val_pair>}...]']'
Specifies the parameters for the conversion. The CE_CONVERSIONoperator is highly configurable via a list of
key-value pairs. For the exact conversion parameters permissible, see the Conversion parameters table.
<key_val_pair> ::= <key> = <value>
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
123
Specify the key and value pair for the parameter setting.
<key> ::= <identifier>
Specifies the parameter key name.
<value> ::= <string_literal>
Specifies the parameter value.
<rename_clause> ::= <rename_att>[{,<rename_att>}]
Specifies new names for the result columns.
<rename_att>
::= <convert_att> AS <new_param_name>
<convert_att>
::= <identifier>
<new_param_name> ::= <identifier>
Specifies the new name for a result column.
Description:
Applies a unit conversion to input table <var_table> and returns the converted values. Result columns can
optionally be renamed. The following syntax depicts valid combinations. Supported keys with their allowed
domain of values are:
Table 20: Conversion parameters
Key
Values
Type
Mandatory
Default
Documentation
'family'
'currency'
key
none
'method'
'ERP'
key
none
The conversion
method.
error_handling
key
'fail on error'
The reaction if a
rate could not be
determined for a
row.
'output'
combinations of
'input', 'uncon
verted', 'con
verted',
'passed_through',
'output_unit',
'source_unit', 'tar
get_unit', 'refer
ence_date'
key
'converted,
passed_through,
output_unit'
Specifies which at
tributes should be
included in the
output.
'source_unit'
Any
Constant
None
'target_unit'
Any
Constant
None
124
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
Key
Values
Type
Mandatory
Default
Documentation
'reference_date'
Any
Constant
None
'source_unit_col
umn'
None
'target_unit_col
umn'
None
'refer
<DP}!#x?2Tl'
umn'
None
'output_unit_col
umn'
Any
column name
"OUTPUT_UNIT"
Mandatory
Default
Values
Type
'client'
Any
Constant
None
The client as
stored in the ta
bles.
'conversion_type'
Any
Constant
'M'
The conversion
type as stored in
the tables.
'schema'
Any
schema name
current schema
The default
schema in which
the conversion ta
bles should be
looked-up.
8.3.3 TRACE
Syntax:
TRACE(<var_input>)
Syntax elements:
<var_input>
::= :<identifier>
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
125
Description:
The TRACE operator is used to debug SQLScript procedures. It traces the tabular data passed as its argument
into a local temporary table and returns its input unmodified. The names of the temporary tables can be
retrieved from the SYS.SQLSCRIPT_TRACE monitoring view. See SQLSCRIPT_TRACE below.
Example:
You trace the content of variable input to a local temporary table.
out = TRACE(:input);
Note
This operator should not be used in production code as it will cause significant runtime overhead.
Additionally, the naming conventions used to store the tracing information may change. Thus, this operator
should only be used during development for debugging purposes.
Related Information
SQLSCRIPT_TRACE
126
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
To eliminate the dependency of having a procedure or a function that already exist when you want to create a
new procedure consuming them, you can use headers in their place.
When creating a procedure, all nested procedures that belong to that procedure must exist beforehand. If
procedure P1 calls P2 internally, then P2 must have been created earlier than P1. Otherwise, P1 creation fails
with the error message,P2 does not exist. With large application logic and no export or delivery unit available,
it can be difficult to determine the order in which the objects need to be created.
To avoid this kind of dependency problem, SAP introduces HEADERS. HEADERS allow you to create a minimum
set of metadata information that contains only the interface of the procedure or function.
Code Syntax
AS HEADER ONLY
You create a header for a procedure by using the HEADER ONLY keyword, as in the following example:
Sample Code
CREATE PROCEDURE <proc_name> [(<parameter_clause>)] AS HEADER ONLY;
With this statement you are creating a procedure <proc_name> with the given signature
<parameter_clause>. The procedure <proc_name> has no body definition and thus has no dependent base
objects. Container properties (for example, security mode, default_schema, and so on) cannot be defined
with the header definition. These are included in the body definition.
The following statement creates the procedure TEST_PROC with a scalar input INVAR and a tabular output
OUTTAB:
Sample Code
CREATE PROCEDURE TEST_PROC (IN INVAR NVARCHAR(10), OUT OUTTAB TABLE(no INT))
AS HEADER ONLY
You can create a function header similarly.
Sample Code
CREATE FUNCTION <func_name> [(<parameter_clause>)] RETURNS <return_type> AS
HEADER ONLY
By checking the is_header_only field in the system view PROCEDURE, you can verify that a procedure only
header is defined.
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
127
Sample Code
SELECT procedure_name, is_header_only from SYS.PROCEDURES
If you want to check for functions, then you need to look into the system view FUNCTIONS.
Once a header of a procedure or function is defined, other procedures or functions can refer to it in their
procedure body. Procedures containing these headers can be compiled as shown in the following example:
Sample Code
CREATE PROCEDURE OUTERPROC (OUT OUTTAB TABLE (NO INT)) LANGUAGE SQLSCRIPT
AS
BEGIN
DECLARE s INT;
s = 1;
CALL TEST_PROC (:s, outtab);
END;
As long as the procedure and/or the function contain only a header definition, they cannot be executed.
Furthermore, all procedures and functions that use this procedure or function containing headers cannot be
executed because they are all invalid.
To change this and to make a valid procedure or function from the header definition, you must replace the
header by the full container definition. Use the ALTER statement to replace the header definition of a
procedure, as follows:
Sample Code
ALTER PROCEDURE <proc_name> [(<parameter_clause>)] [LANGUAGE <lang>] [SQL
SECURITY <mode>] [DEFAULT SCHEMA <default_schema_name>][READS SQL DATA] AS
BEGIN [SEQUENTIAL EXECUTION]
<procedure_body>
END
For a function header, the task is similar, as shown in the following example:
Sample Code
ALTER FUNCTION <func_name> RETURNS <return_type> [LANGUAGE <lang>] [SQL
SECURITY <mode>][DEFAULT SCHEMA <default_schema_name>]
AS
BEGIN
<function_body>
END
For example, if you want to replace the header definition of TEST_PROC that was defined already, then the
ALTER statement might look as follows:
Sample Code
ALTER PROCEDURE TEST_PROC (IN INVAR NVARCHAR(10), OUT OUTTAB TABLE(no INT))
LANGUAGE SQLSCRIPT SQL SECURITY INVOKER READS SQL DATA
AS
128
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
BEGIN
END
You cannot change the signature with the ALTER statement. If the name of the procedure or the function or
the input and output variables do not match, you will receive an error.
Note
The ALTER PROCEDURE and the ALTER FUNCTION statements are supported only for a procedure or a
function respectively, that contain a header definition.
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
129
SQLScript supports the spatial data type ST_GEOMETRY and SQL spatial functions to access and manipulate
spatial data. In addition SQLScript also supports the objective style function calls, which are needed for some
SQL spatial functions.
The next example illustrates a small scenario of using spatial data type and function in SQLScript.
The function get_distance calculates the distance between the two given parameters <first> and
<second> of type ST_GEOMETRY by using the spatial function ST_DISTANCE.
Note the : in front of the variable <first>. This needed because you are reading from the variable.
The function get_distance itself is called by the procedure nested_call. The procedure returns the
distance and the text representation of the ST_GEOMETRY variable <first>.
CREATE FUNCTION get_distance( IN first ST_GEOMETRY, IN second ST_GEOMETRY )
RETURNS distance
double
AS
BEGIN
distance = :first.st_distance(:second);
END;
CREATE PROCEDURE nested_call(
IN first ST_GEOMETRY,
IN second ST_GEOMETRY,
OUT distance double,
OUT res3 CLOB
)
AS
BEGIN
Distance = get_distance (:first, :second);
res3 = :first.st_astext();
END;
first
second
distance
res3
=>
=>
=>
=>
st_geomfromtext('Point(7 48)'),
st_geomfromtext('Point(2 55)'),
?,
?);
130
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
131
11
System Variables
System Variables are build-in variables in SQLScript that provide you information about the current context.
11.1
::CURRENT_OBJECT_NAME
and ::CURRENT_OBJECT_SCHEMA
To identify the name of the current running procedure or function you can use the following two system
variables:
::CURRENT_OBJECT_NAME
::CURRENT_OBJECT_SCHEMA
132
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
END;
Note
Note that in anonymous blocks the value of both system variables is NULL.
The two system variable will always return the schema name and the name of the procedure or function.
Creating a synonym on top of the procedure or function and calling it with the synonym will still return the
original name as shown in the next example.
We create a synonym on the RETURN_NAME function from above and will query it with the synonym:
CREATE SYNONYM SYN_FOR_FUNCTION FOR RETURN_NAME;
SELECT SYNONYM_FOR_FUNCTION().schema_name, SYNONYM_FOR_FUNCTION().name FROM
dummy;
The result is the following:
SCHEMA_NAME
NAME
-----------------------------------------------------MY_SCHEMA
RETURN_NAME
11.2
::ROWCOUNT
The System Variable ::ROWCOUNT stores the number of updated row counts of the previously executed DML
statement. There is no accumulation of all previously executed DML statements.
The next examples shows you how you can use ::ROWCOUNT in a procedure. Consider we have the following
table T:
CREATE
INSERT
INSERT
INSERT
VAL INT);
1);
2);
2);
Now we want to update table T and want to return the number of updated rows:
CREATE PROCEDURE PROC_UPDATE (OUT updated_rows INT) AS
BEGIN
UPDATE T SET VAL = VAL + 1 WHERE VAL = 2;
updated_rows = ::ROWCOUNT;
END;
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
133
By calling the procedure you will see that the number of updated rows is now 1. That is because the las update
statements only updated one row.
UPDATED_ROWS
------------------------1
If you now want to have the number of all updated rows you have to retrieve the row count information after
each update statement and accumulate them:
ALTER PROCEDURE PROC_UPDATE (OUT updated_rows INT) AS
BEGIN
UPDATE T SET VAL = VAL + 1 WHERE VAL = 4;
updated_rows = ::ROWCOUNT;
UPDATE T SET VAL = VAL + 1 WHERE VAL = 2;
updated_rows = :updated_rows + ::ROWCOUNT;
END;
By now calling this procedure again the number of updated row is now 3:
UPDATED_ROWS
------------------------3
134
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
12
Supportability
12.1
M_ACTIVE_PROCEDURES
The view M_ACTIVE_PROCEDURES monitors all internally executed statements starting from a procedure
call. That also includes remotely executed statements.
M_ACTIVE_PROCEDURES is similar to M_ACTIVE_STATEMENTS but keeps the records of completed internal
statements until the parent procedure finishes, and shows them in hierarchical order of nested level. The
structure of M_ACTIVE_PROCEDURES looks as follows:
Table 22:
Column name
Data type
Description
PROCEDURE_HOST
VARCHAR(64)
Procedure Host
PROCEDURE_PORT
INTEGER
PROCEDURE_SCHEMA_NAME
NVARCHAR(256)
PROCEDURE_NAME
NVARCHAR(256)
PROCEDURE_CONNECTION_ID
INTEGER
Procedure connection ID
PROCEDURE_TRANSACTION_ID
INTEGER
Procedure transaction ID
STATEMENT_ID
VARCHAR(20)
STATEMENT_STRING
NCLOB
SQL statement
STATEMENT_PARAMETERS
NCLOB
Statement parameters
STATEMENT_STATUS
VARCHAR(16)
STATEMENT_EXECUTION_COUNT
INTEGER
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
135
Column name
Data type
Description
STATEMENT_DEPTH
INTEGER
Statement depth
STATEMENT_COMPILE_TIME
BIGINT
STATEMENT_EXECUTION_TIME
BIGINT
STATEMENT_START_TIME
TIMESTAMP
STATEMENT_END_TIME
TIMESTAMP
STATEMENT_CONNECTION_ID
INTEGER
STATEMENT_TRANSACTION_ID
INTEGER
Among other things the M_ACTIVE_PROCEDURES is helpful for analyzing long running procedures and to
determine their current status. You can run the following query from another session to find more about the
status of a procedure, like MY_SCHEMA.MY_PROC in the example:
select * from M_ACTIVE_PROCEDURES where procedure_name = my_proc and
procedure_schema_name = my_schema;
There is also the INI-configuration execution_monitoring_level available to control the granularity of
monitoring level:
Table 23:
Level
Description
Please note that you have to define the parameter in the section sqlscript.
To prevent flooding of the memory with irrelevant data, the number of records is limited. If the record count
exceeds the given threshold then the first record will be erased independent from its status. The limit can be
adjust the INI-Parameter execution_monitoring_limit, e.g. execution_monitoring_limit = 100
000.
Limitations:
No triggers and functions are supported.
136
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
Information other than EAPI layer is not monitored. (but might be included in the total compilation time or
execution time)
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
137
13
So far this document has introduced the syntax and semantics of SQLScript. This knowledge is sufficient for
mapping functional requirements to SQLScript procedures. However, besides functional correctness, nonfunctional characteristics of a program play an important role for user acceptance. For instance, one of the
most important non-functional characteristics is performance.
The following optimizations all apply to statements in SQLScript. The optimizations presented here cover how
dataflow exploits parallelism in the SAP HANA database.
Reduce Complexity of SQL Statements: Break up a complex SQL statement into many simpler ones. This
makes a SQLScript procedure easier to comprehend.
Identify Common Sub-Expressions: If you split a complex query into logical sub queries it can help the
optimizer to identify common sub expressions and to derive more efficient execution plans.
Multi-Level-Aggregation: In the special case of multi-level aggregations, SQLScript can exploit results at a
finer grouping for computing coarser aggregations and return the different granularities of groups in
distinct table variables. This could save the client the effort of reexamining the query result.
Understand the Costs of Statements: Employ the explain plan facility to investigate the performance
impact of different SQL queries.
Exploit Underlying Engine: SQLScript can exploit the specific capabilities of the OLAP- and JOIN-Engine by
relying on views modeled appropriately.
Reduce Dependencies: As SQLScript is translated into a dataflow graph, and independent paths in this
graph can be executed in parallel, reducing dependencies enables better parallelism, and thus better
performance.
Avoid Mixing Calculation Engine Plan Operators and SQL Queries: Mixing calculation engine plan operators
and SQL may lead to missed opportunities to apply optimizations as calculation engine plan operators and
SQL statements are optimized independently.
Avoid Using Cursors: Check if use of cursors can be replaced by (a flow of) SQL statements for better
opportunities for optimization and exploiting parallel execution.
Avoid Using Dynamic SQL: Executing dynamic SQL is slow because compile time checks and query
optimization must be done for every invocation of the procedure. Another related problem is security
because constructing SQL statements without proper checks of the variables used may harm security.
13.1
138
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
FROM :books_per_publisher);
Writing this query as a single SQL statement either requires the definition of a temporary view (using WITH) or
repeating a sub query multiple times. The two statements above break the complex query into two simpler
SQL statements that are linked via table variables. This query is much easier to comprehend because the
names of the table variables convey the meaning of the query and they also break the complex query into
smaller logical pieces.
The SQLScript compiler will combine these statements into a single query or identify the common subexpression using the table variables as hints. The resulting application program is easier to understand without
sacrificing performance.
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
139
GROUP BY year;
It is a matter of developer choice and also the application requirements as to which alternative is the best fit
for the purpose.
140
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
141
Read-Only Access
For read-only access to a cursor consider using simple selects or join:
CREATE PROCEDURE foreach_proc LANGUAGE SQLSCRIPT AS
Reads SQL DATA
BEGIN
DECLARE val decimal(34,10) = 0;
DECLARE CURSOR c_cursor1 FOR
SELECT isbn, title, price FROM books;
FOR r1 AS c_cursor1 DO
val = :val + r1.price;
END FOR;
END;
This sum can also be computed by the SQL engine:
SELECT sum(price) into val FROM books;
Computing this aggregate in the SQL engine may result in parallel execution on multiple CPUs inside the SQL
executor.
142
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
Computing this in the SQL engine reduces the calls through the runtime stack of HDB and potentially benefits
from internal optimizations like buffering or parallel execution.
Proposed Solution
Projected attributes
Dynamic SQL
Projected literals
SQL + variables
FROM clause
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
143
Feature
Proposed Solution
APPLY_FILTER
144
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
This section contains information about creating applications with SQLScript for SAP HANA.
Related Information
SAP HANA SQL and System Views Reference
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
145
z_proxy
lv_scalar
lt_table
lt_table_res.
Using the connection clause of the CALL DATABASE PROCEDURE command, it is also possible to call a
database procedure using a secondary database connection. Please consult the ABAP help for detailed
instructions of how to use the CALL DATABASE PROCEDURE command and for the exceptions may be raised.
It is also possible to create procedure proxies with an ABAP API programmatically. Please consult the
documentation of the class CL_DBPROC_PROXY_FACTORY for more information on this topic.
Using ADBC
*&---------------------------------------------------------------------*
*& Report ZRS_NATIVE_SQLSCRIPT_CALL
*&---------------------------------------------------------------------*
*&
*&---------------------------------------------------------------------*
report zrs_native_sqlscript_call.
parameters:
con_name type dbcon-con_name default 'DEFAULT'.
146
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
types:
* result table structure
begin of result_t,
key
type i,
value type string,
end of result_t.
data:
* ADBC
sqlerr_ref type ref to cx_sql_exception,
con_ref type ref to cl_sql_connection,
stmt_ref type ref to cl_sql_statement,
res_ref type ref to cl_sql_result_set,
* results
result_tab type table of result_t,
row_cnt type i.
start-of-selection.
try.
con_ref = cl_sql_connection=>get_connection( con_name ).
stmt_ref = con_ref->create_statement( ).
*************************************
** Setup test and procedure
*************************************
* Create test table
try.
stmt_ref->execute_ddl( 'DROP TABLE zrs_testproc_tab' ).
catch cx_sql_exception.
endtry.
stmt_ref->execute_ddl(
'CREATE TABLE zrs_testproc_tab( key INT PRIMARY KEY, value
NVARCHAR(255) )' ).
stmt_ref->execute_update(
'INSERT INTO zrs_testproc_tab VALUES(1, ''Test value'' )' ).
* Create test procedure with one output parameter
try.
stmt_ref->execute_ddl( 'DROP PROCEDURE zrs_testproc' ).
catch cx_sql_exception.
endtry.
stmt_ref->execute_ddl(
`CREATE PROCEDURE zrs_testproc( OUT t1 zrs_testproc_tab ) ` &&
`READS SQL DATA AS ` &&
`BEGIN ` &&
`
t1 = SELECT * FROM zrs_testproc_tab; ` &&
`END`
).
*************************************
** Execution time
*************************************
perform execute_with_transfer_table.
perform execute_with_gen_temptables.
con_ref->close( ).
catch cx_sql_exception into sqlerr_ref.
perform handle_sql_exception using sqlerr_ref.
endtry.
form execute_with_transfer_table.
data lr_result type ref to data.
* Create transfer table for output parameter
* this table is used to transfer data for parameter 1 of proc zrs_testproc
* for each procedure a new transfer table has to be created
* when the procedure is executed via result view, this table is not needed
* If the procedure has more than one table type parameter, a transfer table is
needed for each parameter
* Transfer tables for input parameters have to be filled first before the call
is executed
try.
stmt_ref->execute_ddl( 'DROP TABLE zrs_testproc_p1' ).
catch cx_sql_exception.
endtry.
stmt_ref->execute_ddl(
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
147
148
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
149
150
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
151
15
Appendix
15.1
The examples used throughout this manual make use of various predefined code blocks. These code snippets
are presented below.
15.1.1 ins_msg_proc
This code is used in the examples in this reference manual to store outputs so the action of the examples can
be seen. It simple stores some text along with a timestamp of the entry.
Before you can use this procedure you must create the following table.
CREATE TABLE message_box (p_msg VARCHAR(200), tstamp TIMESTAMP);
You can create the procedure as follows.
CREATE PROCEDURE ins_msg_proc (p_msg VARCHAR(200)) LANGUAGE SQLSCRIPT AS
BEGIN
INSERT INTO message_box VALUES (:p_msg, CURRENT_TIMESTAMP);
END;
To view the contents of the message_box you select the messages in the table.
select * from message_box;
152
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
SAP HANA server software and tools can be used for several SAP HANA platform and options scenarios as
well as the respective capabilities used in these scenarios. The availability of these is based on the available
SAP HANA licenses and the SAP HANA landscape, including the type and version of the back-end systems the
SAP HANA administration and development tools are connected to. There are several types of licenses
available for SAP HANA. Depending on your SAP HANA installation license type, some of the features and
tools described in the SAP HANA platform documentation may only be available in the SAP HANA options and
capabilities, which may be released independently of an SAP HANA Platform Support Package Stack (SPS).
Although various features included in SAP HANA options and capabilities are cited in the SAP HANA platform
documentation, each SAP HANA edition governs the options and capabilities available. Based on this,
customers do not necessarily have the right to use features included in SAP HANA options and capabilities.
For customers to whom these license restrictions apply, the use of features included in SAP HANA options and
capabilities in a production system requires purchasing the corresponding software license(s) from SAP. The
documentation for the SAP HANA optional components is available in SAP Help Portal at http://
help.sap.com/hana_options. If you have additional questions about what your particular license provides, or
wish to discuss licensing features available in SAP HANA options, please contact your SAP account team
representative.
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
153
Coding Samples
Any software coding and/or code lines / strings ("Code") included in this documentation are only examples and are not intended to be used in a productive system
environment. The Code is only intended to better explain and visualize the syntax and phrasing rules of certain coding. SAP does not warrant the correctness and
completeness of the Code given herein, and SAP shall not be liable for errors or damages caused by the usage of the Code, unless damages were caused by SAP
intentionally or by SAP's gross negligence.
Accessibility
The information contained in the SAP documentation represents SAP's current view of accessibility criteria as of the date of publication; it is in no way intended to be
a binding guideline on how to ensure accessibility of software products. SAP in particular disclaims any liability in relation to this document. This disclaimer, however,
does not apply in cases of wilful misconduct or gross negligence of SAP. Furthermore, this document does not result in any direct or indirect contractual obligations of
SAP.
Gender-Neutral Language
As far as possible, SAP documentation is gender neutral. Depending on the context, the reader is addressed directly with "you", or a gender-neutral noun (such as
"sales person" or "working days") is used. If when referring to members of both sexes, however, the third-person singular cannot be avoided or a gender-neutral noun
does not exist, SAP reserves the right to use the masculine form of the noun and pronoun. This is to ensure that the documentation remains comprehensible.
Internet Hyperlinks
The SAP documentation may contain hyperlinks to the Internet. These hyperlinks are intended to serve as a hint about where to find related information. SAP does
not warrant the availability and correctness of this related information or the ability of this information to serve a particular purpose. SAP shall not be liable for any
damages caused by the use of related information unless damages have been caused by SAP's gross negligence or willful misconduct. All links are categorized for
transparency (see: http://help.sap.com/disclaimer).
154
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
PUBLIC
2015 SAP SE or an SAP affiliate company. All rights reserved.
155
www.sap.com/contactsap