Database procedures are the most flexible mechanism to implement data-intensive calculations in SAP HANA. For this, SQLScript can be used. As with user-defined functions, there are two options to manage database procedures:
Executing direct SQL statements in the SQL Console of SAP Web IDE to generate the run-time object — this is not recommended as this approach provides no support for managing the lifecycle of procedures, such as adjusting the code of an existing procedure. It also means the procedure is not considered as part of a complete set of development files that should be built together as a complete unit.
Defining the procedure in a development source file of the type .hdbprocedure in the SAP Web IDE Development view, and then building it to generate the runtime object — this is the recommended approach. Code samples will still refer to the direct SQL method (using the CREATE PROCEDURE statement) so as to provide a complete example. The chief syntactic difference is that .hdbprocedure files omit the CREATE keyword.
Basic Syntax to Define a Stored Procedure
12345678910CREATE PROCEDURE <procedure_name>
[(<parameter_clause>)}
[LANGUAGE <lang>]
[SQL SECURITY <mode>]
[DEFAULT SCHEMA <default_schema_name>]
[READS SQL DATA]
AS
BEGIN [SEQUENTIAL EXECUTION]
<procedure body>
END;Elements of a Procedure Header
The following elements may appear in a procedure header:
Parameters must specify if they are used for input, output, or both, in addtion to being named and defined in terms of structure.
LANGUAGE specifies which programming language the procedure is implemented in; SQLScript is the default.
SQL SECURITY specifies which user’s privileges apply when the procedure is executed; the default is DEFINER.
DEFAULT SCHEMA specifies the database schema to be used for unqualified database object accesses within the procedure body.
READS SQL DATA specifies that the procedure is free of side effects and is read-only; ou need to remove this line if your procedure uses DDl statements.
SEQUENTIAL EXECUTION forces sequential execution of the procedure logic with no parallelism taking place.
input and Output Parameters In Procedures
Database procedures can have several output parameters, and a mix of parameters of scalar and table types is possible. Their value is set using assignments, as shown in the following example:
1234567891011121314151617181920212223-- create a read-only procedure that passes in a unit of time and converts
-- the overtime hours of officials into the target unit; use the
-- previously-created Convert_Hours function to provide the time conversion
CREATE PROCEDURE Convert_OfficialsHours
(IN im_to VARCHAR(1),
OUT ex_official TABLE
(PNr NVARCHAR(3),
Name NVARCHAR(20),
Overtime DEC(5,2)
)
)
LANGUAGE SQLSCRIPT
SQL SECURITY INVOKER
READS SQL DATA
AS
BEGIN
ex_official = SELECT PNr, Name, Convert_Hours(Overtime, :im_to) AS Overtime
FROM :lt_official;
END;Recall that this conversion was accomplished earlier using only functions; wrapping a procedure around the functions is not necessary, but makes for a relatively straightforward example.
Procedures can also have parameters which are both input and output. This is why the keywords IN, OUT, and INOUT are necessary in the definition of the signature to indicate the kind of parameter. IN is the default.
Declarative Logic in SQLScript
Using declarative logic only, which implies side-effect free programming, has a very big advantage:
The procedure developer defines what data to select using SELECT or CE functions.
The database system determines the how and executes accordingly.
Data selection using declarative statements can be parallelized massively.
The following procedure uses declarative logic only:
123456789101112131415161718192021222324252627282930313233343536373839-- create a read-only procedure that provides separate counts of privately-owned and
-- company-owned cars; exclude cars that are reported as stolen
CREATE PROCEDURE CarOwner_Count
(OUT ex_company_cars INTEGER,
OUT ex_private_cars INTEGER )
LANGUAGE SQLSCRIPT
SQL SECURITY INVOKER
READS SQL DATA
AS
BEGIN
-- identify the owner IDs of company cars, which have a NULL value of owner birthday
lt_companies = SELECT OwnerID
FROM Owner
WHERE Birthday IS NULL;
-- identify the owner IDs of personal cars, which have a non-NULL value owner birthday
lt_persons = SELECT OwnerID
FROM Owner
WHERE Birthday IS NOT NULL;
-- identify the plate numbers of cars that are not reported as stolen
lt_cars = SELECT *
FROM Car c
WHERE PlateNumber NOT IN (SELECT PlateNumber
FROM Stolen);
-- populate the output parameter values
SELECT COUNT(*) INTO ex_company_cars
FROM :lt_cars JOIN :lt_companies ON Owner = OwnerID;
SELECT COUNT(*) INTO ex_private_cars
FROM :lt_cars JOIN :lt_persons ON Owner = OwnerID;
END;
-- after the procedure is created, execute it via a CALL statement:
CALL CarOwner_Count(?,?);The procedure CarOwner_Count uses declarative logic only. This allows the execution of the first three queries and assignments in parallel because their results are mutually independent.
Recap: Imperative Logic in SQLScript
You can also use imperative logic in procedures, provided they are not defined as read only. Imperative language constructs allow the developer to control data and execution flow.
Recall the capabilities of imperative logic:
Scalar variable manipulation
Branching logic
Loops
DDL statements and INSERT, UPDATE, and DELETE statements
Imperative logic is executed exactly as scripted, which is procedural and prevents parallel processing.
If they have been created directly using SQL, procedures can be deleted using the DROP PROCEDURE command:
1DROP PROCEDURE <procedure name>;Executing Stored Procedures
To call a procedure, use the CALL statement. There are different options depending on where you CALL the procedure and for passing parameters. Arguments for tabular IN parameters can either be physical tables, views, or table variables. The actual value passed for tabular OUT parameters must be ‘?’ when calling a procedure in the SQL Console.
- Passing parameters by position
- Code Snippet1CALL Convert_OfficialsHours(NULL,'d',?)
- Passing parameters by name – allows ignoring their order
- Code Snippet12CALL Convert_OfficialsHours(im_filter=>NULL, im_to=>'d', ex_official=>?)
- In another procedure:
- Code Snippet12CALL Convert_OfficialsHours(im_filter=>NULL, im_to=>'d', ex_official=>lt_official)
Using WITH OVERVIEW
WITH OVERVIEW defines that the result of the procedure call is stored in one or more physical tables. These tables are dropped automatically when the database session is closed.
Calling a procedure WITH OVERVIEW returns one result set that details which physical table contains the result of each table output variable. Scalar outputs are represented as temporary tables with only one cell. When you pass existing tables to the output parameters, WITH OVERVIEW inserts the result set tuples of the procedure into the provided tables. When you pass '?' to the output parameters, temporary tables holding the result sets are generated.
Create a Function Using Source File in SAP Web IDE
A procedure should always be created as a source file in the Development view of SAP Web IDE. Click a project folder that belongs to a HDB module and use the menu option New → Procedure. You don’t need to specify the extension .hdbprocedure as this is automatically appended to the name you provide for the file.
As this is the recommended approach to define a procedure, let’s take a look at it now.

The file opens with basic shell code for you to complete. When you have finished writing the code, you simply save, then build the object. Assuming you have no errors, a runtime procedure is created in the container that belongs to the project. You can then open the SQL Console against the container and write some SQL to test your procedure.
As with a function built using a source file, you do not drop the procedure directly using a Drop statement in SQL. If you wish to remove the procedure you simply delete the source file. Then, in the next build of the folder in which it was stored, the run-time object is then deleted.